Source code for sfftk.notes.view

"""
``sfftk.notes.view``
=============================

Display notes in EMDB-SFF files
"""
import sys
import textwrap

import sfftkrw.schema.adapter_v0_8_0_dev1 as schema
from sfftkrw.core import _str
from sfftkrw.core.print_tools import print_date
from styled import Styled

__author__ = "Paul K. Korir, PhD"
__email__ = "pkorir@ebi.ac.uk, paul.korir@gmail.com"
__date__ = "2017-04-07"
__updated__ = '2018-02-14'


def _add_index(input_list, pre="\t"):
    """Add indexes to items in L"""
    output_list = list()
    for i, item in enumerate(input_list):
        output_list.append("{}{}: {}".format(pre, i, item))
    return output_list


[docs] class View(object): """View base class""" DISPLAY_WIDTH = 110 NOT_DEFINED = "-*- NOT DEFINED -*-" NOT_DEFINED_ALT = "N/A" LINE1 = ('=' * DISPLAY_WIDTH) LINE2 = ('-' * DISPLAY_WIDTH) LINE3 = ('*' * DISPLAY_WIDTH)
[docs] class NoteView(View): """NoteView class Display annotation for a single segment """ def __init__(self, segment, _long=False, list_ids=False): self._segment = segment self._long = _long self.list_ids = list_ids @property def id(self): return self._segment.id @property def parent_id(self): return self._segment.parent_id @property def name(self): if self._segment.biological_annotation.name: return self._segment.biological_annotation.name else: return self.NOT_DEFINED @property def description(self): if self._segment.biological_annotation.description: return textwrap.fill(self._segment.biological_annotation.description, self.DISPLAY_WIDTH) else: return self.NOT_DEFINED @property def number_of_instances(self): if self._segment.biological_annotation.number_of_instances: return self._segment.biological_annotation.number_of_instances else: return self.NOT_DEFINED_ALT @property def number_of_external_references(self): return self._segment.biological_annotation.num_external_references @property def external_references(self): if self._segment.biological_annotation: string_list = list() string_list.append( "\t{:>3} {:<16} {:<56} {:<20} {:1} {:1}".format( "#", "resource", "url", "accession", "L", "D", ) ) string_list.append("\t" + "-" * (self.DISPLAY_WIDTH - len("\t".expandtabs()))) i = 0 for ext_ref in self._segment.biological_annotation.external_references: resource = ext_ref.resource url = ext_ref.url accession = ext_ref.accession label = "Y" if ext_ref.label is not None else "N" description = "Y" if ext_ref.description is not None else "N" string_list.append( "\t{id:>2}: {resource:<16} {url:<56} {accession:<20} {label:1} {description:1}".format( id=i, resource=resource, url=url, accession=accession, label=label, description=description, ) ) i += 1 return "\n".join(string_list) else: return "\t" + self.NOT_DEFINED @property def colour(self): if self._segment.colour.value: return self._segment.colour.value else: return self.NOT_DEFINED @property def segment_type(self): segment_type = list() if self._segment.mesh_list: segment_type.append("mesh_list") if self._segment.shape_primitive_list: segment_type.append("shape_primitive_list") if self._segment.three_d_volume: segment_type.append("three_d_volume") # json EMDB-SFF files do not have geometrical data if not segment_type: return None else: return ", ".join(segment_type) if sys.version_info[0] > 2: def __bytes__(self): return self.__str__().encode('utf-8') def __str__(self): if self._long: string = """\ \r{} \rID:\t\t{} \rPARENT ID:\t{} \rSegment Type:\t{} \r{} \rName: \r\t{} \rDescription: \r\t{} \rNumber of instances: \r\t{} \r{} \rExternal references: (L = label present; D = description present) \r{} \r{} \rColour: \r\t{}\ """.format( # **** self.LINE3, self.id, self.parent_id, self.segment_type, # --- self.LINE2, self.name, self.description, self.number_of_instances, # ----- self.LINE2, self.external_references, # ---- # ---- self.LINE2, self.colour, ) elif self.list_ids: string = "{}".format(self.id) else: colour = self.colour string = "{:<7} {:<7} {:<50} {:>5} {:>5} {:^30}".format( self.id, self.parent_id, self.name + "::" + self.description if len(self.name + "::" + self.description) <= 40 else (self.name + "::" + self.description)[:37] + "...", self.number_of_instances, self.number_of_external_references, "(" + ", ".join(map(str, map(lambda c: round(c, 3), colour))) + ")", ) return string else: def __str__(self): return self.__unicode__().encode('utf-8') def __unicode__(self): if self._long: string = """\ \r{} \rID:\t\t{} \rPARENT ID:\t{} \rSegment Type:\t{} \r{} \rName: \r\t{} \rDescription: \r\t{} \rNumber of instances: \r\t{} \r{} \rExternal references: \r{} \r{} \rColour: \r\t{}\ """.format( # **** self.LINE3, self.id, self.parent_id, self.segment_type, # --- self.LINE2, self.name, self.description, self.number_of_instances, # ----- self.LINE2, self.external_references, # ---- self.LINE2, self.colour, ) elif self.list_ids: string = "{}".format(self.id) else: colour = self.colour string = "{:<7} {:<7} {:<50} {:>5} {:>5} {:^30}".format( self.id, self.parent_id, self.name + "::" + self.description if len(self.name + "::" + self.description) <= 40 else (self.name + "::" + self.description)[:37] + "...", self.number_of_instances, self.number_of_external_references, "(" + ", ".join(map(str, map(lambda c: round(c, 3), colour))) + ")", ) return string
[docs] class HeaderView(View): """HeaderView class Display EMDB-SFF header """ def __init__(self, segmentation): self._segmentation = segmentation @property def name(self): if self._segmentation.name: return self._segmentation.name else: return self.NOT_DEFINED @property def version(self): return self._segmentation.version @property def software_list(self): software = "" for i, sw in enumerate(self._segmentation.software_list): if sw.name is not None: sw_name = sw.name else: sw_name = "\t" + self.NOT_DEFINED if sw.version is not None: sw_version = sw.version else: sw_version = "\t" + self.NOT_DEFINED if sw.processing_details is not None: sw_proc_det = textwrap.fill("\tproc/det: " + sw.processing_details, self.DISPLAY_WIDTH) else: sw_proc_det = "\tproc/det: " + self.NOT_DEFINED software += "\t{id} {name}/{version}\n{proc_det}\n".format( id=i, name=sw_name, version=sw_version, proc_det=sw_proc_det, ) return software @property def primary_descriptor(self): return self._segmentation.primary_descriptor @property def bounding_box(self): if self._segmentation.bounding_box is not None: return self._segmentation.bounding_box.xmin, self._segmentation.bounding_box.xmax, \ self._segmentation.bounding_box.ymin, self._segmentation.bounding_box.ymax, \ self._segmentation.bounding_box.zmin, self._segmentation.bounding_box.zmax @property def transforms(self): string_list = list() for transform in self._segmentation.transforms: transform_list = transform.data_array.flatten().tolist() string_list.append( "\t{}\t{}\n\t\t{}\n\t\t{}".format( transform.id, transform_list[:4], transform_list[4:8], transform_list[8:], ) ) string_list.append( "\t{}".format(self.LINE2) ) if string_list: string_list.pop() # remove the last line return "\n".join(string_list) @property def global_external_references(self): if self._segmentation.global_external_references: string_list = list() string_list.append( "{:>3} {:<16} {:<56} {:<20} {:1} {:1}".format( "#", "resource", "url", "accession", "L", "D", ) ) string_list.append("\t" + "-" * (self.DISPLAY_WIDTH - len("\t".expandtabs()))) i = 0 for g_ext_ref in self._segmentation.global_external_references: resource = g_ext_ref.resource url = g_ext_ref.url accession = g_ext_ref.accession label = "Y" if g_ext_ref.label is not None else "N" description = "Y" if g_ext_ref.description is not None else "N" string_list.append( "\t{id:>2}: {resource:<16} {url:<56} {accession:<20} {label:1} {description:1}".format( id=i, resource=resource, url=url, accession=accession, label=label, description=description, ) ) i += 1 return "\n".join(string_list) else: return self.NOT_DEFINED @property def details(self): if self._segmentation.details: return "\n".join(textwrap.wrap("\t" + self._segmentation.details, self.DISPLAY_WIDTH)) else: return "\t" + self.NOT_DEFINED if sys.version_info[0] > 2: def __bytes__(self): return self.__str__().encode('utf-8') def __str__(self): string = """\ \r{} \rEMDB-SFF v.{} \r{} \rSegmentation name: \r\t{} \rSegmentation software: \r{} \r{} \rPrimary descriptor [three_d_volume|mesh_list|shape_primitive_list]: \r\t{} \r{} \rTransforms: \r{} \r{} \rBounding box (xmin,xmax,ymin,ymax,zmin,zmax): \r\t{} \r{} \rGlobal external references: (L = label present; D = description present) \r\t{} \r{} \rSegmentation details: \r{}\ """.format( # === self.LINE1, self.version, # --- self.LINE2, self.name, self.software_list, # --- self.LINE2, self.primary_descriptor, # --- self.LINE2, self.transforms, # --- self.LINE2, self.bounding_box, # --- self.LINE2, self.global_external_references, # ---- self.LINE2, self.details, ) return string else: def __str__(self): return self.__unicode__().encode('utf-8') def __unicode__(self): string = """\ \r{} \rEMDB-SFF v.{} \r{} \rSegmentation name: \r\t{} \rSegmentation software: \r{} \r{} \rPrimary descriptor [three_d_volume|mesh_list|shape_primitive_list]: \r\t{} \r{} \rTransforms: \r\t{} \r{} \rBounding box (xmin,xmax,ymin,ymax,zmin,zmax): \r\t{} \r{} \rGlobal external references: (L = label present; D = description present) \r\t{} \r{} \rSegmentation details: \r{}\ """.format( # === self.LINE1, self.version, # --- self.LINE2, self.name, self.software_list, # --- self.LINE2, self.primary_descriptor, # --- self.LINE2, self.transforms, # --- self.LINE2, self.bounding_box, # --- self.LINE2, self.global_external_references, # ---- self.LINE2, self.details, ) return string
[docs] class TableHeaderView(View): """Class defining the view of a table header object""" if sys.version_info[0] > 2: def __bytes__(self): return self.__str__().encode('utf-8') def __str__(self): return self._unicode() else: def __str__(self): return self.__unicode__().encode('utf-8') def __unicode__(self): return self._unicode() def _unicode(self): string = """\ \r{} \r{:<7} {:<7} {:<50} {:>5} {:>5} {:^26} \r{}\ """.format( View.LINE3, "id", "par_id", "name::description", "#inst", "#ext_ref", "colour", View.LINE2 ) return string
[docs] def list_notes(args, configs): """List all notes in an EMDB-SFF file :param args: parsed arguments :type args: :py:class:`argparse.Namespace` :return status: 0 is OK, else failure :rtype status: int """ sff_seg = schema.SFFSegmentation.from_file(args.sff_file) # todo: make this optional # todo: define the stream to use if args.header: string = Styled("[[ ''|fg-cyan:no-end ]]") string += _str(HeaderView(sff_seg)) string += Styled("[[ ''|reset ]]") print(_str(string)) note_views = [NoteView(segment, _long=args.long_format, list_ids=args.list_ids) for segment in sff_seg.segments] if args.sort_by_name: sorted_note_views = sorted(note_views, key=lambda n: n.name, reverse=args.reverse) else: sorted_note_views = sorted(note_views, key=lambda n: n.id, reverse=args.reverse) # table header if not args.list_ids and not args.long_format: string = Styled("[[ ''|fg-cyan:no-end ]]") string += _str(TableHeaderView()) string += Styled("[[ ''|reset ]]") print(_str(string)) for note_view in sorted_note_views: if args.list_ids: string = _str(note_view) else: # add colour string = Styled("[[ ''|fg-cyan:no-end ]]") string += _str(note_view) string += Styled("[[ ''|reset ]]") print(_str(string)) return 0
[docs] def show_notes(args, configs): """Show notes in an EMDB-SFF file for the specified segment IDs :param args: parsed arguments :type args: :py:class:`argparse.Namespace` :return status: 0 is OK, else failure :rtype status: int """ sff_seg = schema.SFFSegmentation.from_file(args.sff_file) if args.header: string = Styled("[[ ''|fg-cyan:no-end ]]") string += _str(HeaderView(sff_seg)) string += Styled("[[ ''|reset ]]") print(_str(string)) if args.segment_id is not None: if not args.long_format: string = Styled("[[ ''|fg-cyan:no-end ]]") string += _str(TableHeaderView()) string += Styled("[[ ''|reset ]]") print(_str(string)) found_segment = False for segment in sff_seg.segment_list: if segment.id in args.segment_id: string = Styled("[[ ''|fg-cyan:no-end ]]") string += _str(NoteView(segment, _long=args.long_format)) string += Styled("[[ ''|reset ]]") print(_str(string)) found_segment = True if not found_segment: print_date("No segment with ID(s) {}".format(", ".join(map(str, args.segment_id)))) return 65 return 0