Beispiel #1
0
 def __init__(self, uri, **kwds):
 #-------------------------------
   from biosignalml.data.time import RelativeTimeLine   ## Otherwise circular import...
   super(Recording, self).__init__(uri, **kwds)
   self.timeline = RelativeTimeLine(str(uri) + '/timeline')
   self._resources = OrderedDict()
Beispiel #2
0
class Recording(AbstractObject):
#===============================
  """
  An abstract BioSignalML Recording.

  :param uri: The URI of the recording.
  :param kwds: Recording attributes, specified as keywords.
  """

  metaclass = BSML.Recording  #: :attr:`.BSML.Recording`

  attributes = [ 'dataset', 'source', 'format', 'comment', 'investigation',
                 'starttime', 'duration', 'timeline', 'generatedBy'
               ]              #: Generic attributes of a Recording.

  mapping = { 'format':        PropertyMap(DCT.format),
              'dataset':       PropertyMap(BSML.dataset),
              'source':        PropertyMap(DCT.source, functional=False),
              'investigation': PropertyMap(DCT.subject),
              'investigator':  PropertyMap(DCT.creator),
              'starttime':     PropertyMap(DCT.created, XSD.dateTime,
                                           utils.datetime_to_isoformat,
                                           utils.isoformat_to_datetime),
              'duration':      PropertyMap(DCT.extent, XSD.dayTimeDuration,
                                           utils.seconds_to_isoduration,
                                           utils.isoduration_to_seconds),
##            'digest':        PropertyMap(BSML.digest),
              'timeline':      PropertyMap(TL.timeline,
                                           to_rdf=PropertyMap.get_uri,
                                           from_rdf=_get_timeline, subelement=True),
              'generatedBy':   PropertyMap(PROV.wasGeneratedBy, to_rdf=PropertyMap.get_uri,
                                           subelement=True),
            }

  SignalClass = Signal       #: The class of :class:`~.signal.Signal`\s in the Recording
  EventClass  = Event        #: The class of :class:`~.event.Event`\s in the Recording


  def __init__(self, uri, **kwds):
  #-------------------------------
    from biosignalml.data.time import RelativeTimeLine   ## Otherwise circular import...
    super(Recording, self).__init__(uri, **kwds)
    self.timeline = RelativeTimeLine(str(uri) + '/timeline')
    self._resources = OrderedDict()

  def resources(self, cls):
  #-------------------------
    """
    The recording's resources of a given class, as a list.
    """
    return [ r for r in self._resources.values() if isinstance(r, cls) ]

  def add_resource(self, resource):
  #--------------------------------
    """Associate a resource with a Recording."""
    self._resources[str(resource.uri)] = resource
    return resource

  def remove_resource(self, uri):
  #------------------------------
    """Remove a resource from a Recording."""
    return self._resources.pop(str(uri), None)

  def get_resource(self, uri):
  #---------------------------
    """
    Get a resource associated with the Recording.

    :param uri: The resource's URI.

    """
    return self._resources.get(str(uri))

  def __len__(self):
  #-----------------
    return len(self.signals())

  def signals(self):
  #-----------------
    """
    The recording's signals as a list.
    """
    return self.resources(Signal)

  def get_signal(self, uri):
  #-------------------------
    """
    Retrieve a :class:`~.signal.Signal` from a Recording.

    :param uri: The URI of the signal.
    :return: A signal in the recording.

    """
    signal = self.get_resource(uri)
    if not isinstance(signal, self.SignalClass):
      raise KeyError, str(uri)
    return signal

  def add_signal(self, signal):
  #----------------------------
    """
    Add a :class:`~.signal.Signal` to a Recording.

    :param signal: The signal to add to the recording.

    """
    sig_uri = str(signal.uri)
    try:
      self.get_signal(sig_uri)
      raise Exception, "Signal '%s' already in recording" % sig_uri
    except KeyError:
      pass
    if signal.recording is not None:     ## Set from RDF mapping...
      if isinstance(signal.recording, Recording): rec_uri = signal.recording.uri
      else:                                       rec_uri = signal.recording
      if str(rec_uri) != str(self.uri):
        raise Exception, "Adding to '%s', but signal '%s' is in '%s'" % (self.uri, sig_uri, rec_uri)
    signal.recording = self
    self.add_resource(signal)
    return signal

  def new_signal(self, uri, units, id=None, **kwds):
  #-------------------------------------------------
    """
    Create a new Signal and add it to the Recording.

    :param uri: The URI for the signal.
    :param units: The units signal data values are in.
    :return: A Signal of type :attr:`SignalClass`.
    """
    if uri is None and id is None:
      raise Exception, "Signal must have 'uri' or 'id' specified"
    if uri is None:
      uri = str(self.uri) + '/signal/%s' % str(id)
    try:
      self.get_signal(uri)
      raise Exception, "Signal '%s' already in recording" % uri
    except KeyError:
      pass
    signal = self.SignalClass(uri, units, **kwds)
    signal.recording = self
    self.add_resource(signal)
    return signal


  def events(self):
  #-----------------
    """
    The recording's events as a list.
    """
    return self.resources(Event)

  def get_event(self, uri):
  #------------------------
    """
    Retrieve an :class:`~.event.Event` from a Recording.

    :param uri: The URI of the event.
    :return: An event in the recording.

    """
    event = self.get_resource(uri)
    if event is None:
      event = Event.create_from_graph(uri, self.graph)  ## graphstore ??
      if self.uri != event.recording.uri:
        raise KeyError, "Event <%s> doesn't refer to recording" % uri
      self.add_event(event)
    elif not isinstance(event, self.EventClass):
      raise KeyError, "<%s> isn't an Event" % uri
    return event

  def add_event(self, event):
  #--------------------------
    """
    Add an :class:`~.event.Event` to a Recording.

    :param event: The event to add to the recording.

    """
    event.recording = self
    self.add_resource(event)
    return event

  def new_event(self, uri, etype, at, duration=None, end=None, **kwds):
  #--------------------------------------------------------------------
    """
    Create a new Event and add it to the Recording.

    :return: An Event of type :attr:`EventClass`.
    """
    return self.add_event(self.EventClass(uri, etype, self.interval(at, duration, end), **kwds))


  def instant(self, when):
  #----------------------
    """
    Create an :class:`.Instant` on the recording's timeline.
    """
    return self.timeline.instant(when)

  def interval(self, start, duration=None, end=None):
  #--------------------------------------------------
    """
    Create an :class:`.Interval` on the recording's timeline.
    """
    return self.timeline.interval(start, duration, end=end)


## Or create urn:uuid URIs ??
  def new_segment(self, uri, at, duration=None, end=None, **kwds):  ## Of a Recording
  #---------------------------------------------------------------
    """
    Create a new :class:`~.segment.Segment` of a Recording.

    :param uri: The URI of the Segment.
    :param float at: When the segment starts.
    :param float duration: The duration of the Segment. Optional.
    :param float duration: When the Segment ends. Optional.
    :param kwds: Optional additional attributes for the Segment.

    """
    return self.add_resource(Segment(uri, self, self.interval(at, duration, end), **kwds))


  def save_metadata_to_graph(self, graph):
  #---------------------------------------
    """
    Add a Recording's metadata to a RDF graph.

    :param graph: A RDF graph.
    :type graph: :class:`~biosignalml.rdf.Graph`
    """
    super(Recording, self).save_metadata_to_graph(graph)
    for resource in self._resources.values():
      resource.save_metadata_to_graph(graph)

  @classmethod
  def create_from_graph(cls, uri, graph, signals=True, **kwds):
  #------------------------------------------------------------
    """
    Create a new instance of a Recording, setting attributes from RDF triples in a graph.

    :param uri: The URI for the Recording.
    :param graph: A RDF graph.
    :type graph: :class:`~biosignalml.rdf.Graph`
    :type signals: Set False to not load the Recording's Signals.
    :rtype: :class:`Recording`
    """

    self = super(Recording, cls).create_from_graph(uri, graph, **kwds)
    if signals:
      for r in graph.query("select ?s where { ?s a <%s> . ?s <%s> <%s> } order by ?s"
                           % (BSML.Signal, BSML.recording, self.uri)):
        self.add_signal(self.SignalClass.create_from_graph(str(r['s']), graph, units=None))
    # Do we load events? There may be a lot of them...
    return self
Beispiel #3
0
class Recording(AbstractObject):
    #===============================
    """
  An abstract BioSignalML Recording.

  :param uri: The URI of the recording.
  :param kwds: Recording attributes, specified as keywords.
  """

    metaclass = BSML.Recording  #: :attr:`.BSML.Recording`

    attributes = [
        'dataset', 'source', 'format', 'comment', 'investigation', 'starttime',
        'duration', 'timeline', 'generatedBy'
    ]  #: Generic attributes of a Recording.

    mapping = {
        'format':
        PropertyMap(DCT.format),
        'dataset':
        PropertyMap(BSML.dataset),
        'source':
        PropertyMap(DCT.source, functional=False),
        'investigation':
        PropertyMap(DCT.subject),
        'investigator':
        PropertyMap(DCT.creator),
        'starttime':
        PropertyMap(DCT.created, XSD.dateTime, utils.datetime_to_isoformat,
                    utils.isoformat_to_datetime),
        'duration':
        PropertyMap(DCT.extent, XSD.dayTimeDuration,
                    utils.seconds_to_isoduration,
                    utils.isoduration_to_seconds),
        ##            'digest':        PropertyMap(BSML.digest),
        'timeline':
        PropertyMap(TL.timeline,
                    to_rdf=PropertyMap.get_uri,
                    from_rdf=_get_timeline,
                    subelement=True),
        'generatedBy':
        PropertyMap(PROV.wasGeneratedBy,
                    to_rdf=PropertyMap.get_uri,
                    subelement=True),
    }

    SignalClass = Signal  #: The class of :class:`~.signal.Signal`\s in the Recording
    EventClass = Event  #: The class of :class:`~.event.Event`\s in the Recording

    def __init__(self, uri, **kwds):
        #-------------------------------
        from biosignalml.data.time import RelativeTimeLine  ## Otherwise circular import...
        super(Recording, self).__init__(uri, **kwds)
        self.timeline = RelativeTimeLine(str(uri) + '/timeline')
        self._resources = OrderedDict()

    def resources(self, cls):
        #-------------------------
        """
    The recording's resources of a given class, as a list.
    """
        return [r for r in self._resources.values() if isinstance(r, cls)]

    def add_resource(self, resource):
        #--------------------------------
        """Associate a resource with a Recording."""
        self._resources[str(resource.uri)] = resource
        return resource

    def remove_resource(self, uri):
        #------------------------------
        """Remove a resource from a Recording."""
        return self._resources.pop(str(uri), None)

    def get_resource(self, uri):
        #---------------------------
        """
    Get a resource associated with the Recording.

    :param uri: The resource's URI.

    """
        return self._resources.get(str(uri))

    def __len__(self):
        #-----------------
        return len(self.signals())

    def signals(self):
        #-----------------
        """
    The recording's signals as a list.
    """
        return self.resources(Signal)

    def get_signal(self, uri):
        #-------------------------
        """
    Retrieve a :class:`~.signal.Signal` from a Recording.

    :param uri: The URI of the signal.
    :return: A signal in the recording.

    """
        signal = self.get_resource(uri)
        if not isinstance(signal, self.SignalClass):
            raise KeyError, str(uri)
        return signal

    def add_signal(self, signal):
        #----------------------------
        """
    Add a :class:`~.signal.Signal` to a Recording.

    :param signal: The signal to add to the recording.

    """
        sig_uri = str(signal.uri)
        try:
            self.get_signal(sig_uri)
            raise Exception, "Signal '%s' already in recording" % sig_uri
        except KeyError:
            pass
        if signal.recording is not None:  ## Set from RDF mapping...
            if isinstance(signal.recording, Recording):
                rec_uri = signal.recording.uri
            else:
                rec_uri = signal.recording
            if str(rec_uri) != str(self.uri):
                raise Exception, "Adding to '%s', but signal '%s' is in '%s'" % (
                    self.uri, sig_uri, rec_uri)
        signal.recording = self
        self.add_resource(signal)
        return signal

    def new_signal(self, uri, units, id=None, **kwds):
        #-------------------------------------------------
        """
    Create a new Signal and add it to the Recording.

    :param uri: The URI for the signal.
    :param units: The units signal data values are in.
    :return: A Signal of type :attr:`SignalClass`.
    """
        if uri is None and id is None:
            raise Exception, "Signal must have 'uri' or 'id' specified"
        if uri is None:
            uri = str(self.uri) + '/signal/%s' % str(id)
        try:
            self.get_signal(uri)
            raise Exception, "Signal '%s' already in recording" % uri
        except KeyError:
            pass
        signal = self.SignalClass(uri, units, **kwds)
        signal.recording = self
        self.add_resource(signal)
        return signal

    def events(self):
        #-----------------
        """
    The recording's events as a list.
    """
        return self.resources(Event)

    def get_event(self, uri):
        #------------------------
        """
    Retrieve an :class:`~.event.Event` from a Recording.

    :param uri: The URI of the event.
    :return: An event in the recording.

    """
        event = self.get_resource(uri)
        if event is None:
            event = Event.create_from_graph(uri, self.graph)  ## graphstore ??
            if self.uri != event.recording.uri:
                raise KeyError, "Event <%s> doesn't refer to recording" % uri
            self.add_event(event)
        elif not isinstance(event, self.EventClass):
            raise KeyError, "<%s> isn't an Event" % uri
        return event

    def add_event(self, event):
        #--------------------------
        """
    Add an :class:`~.event.Event` to a Recording.

    :param event: The event to add to the recording.

    """
        event.recording = self
        self.add_resource(event)
        return event

    def new_event(self, uri, etype, at, duration=None, end=None, **kwds):
        #--------------------------------------------------------------------
        """
    Create a new Event and add it to the Recording.

    :return: An Event of type :attr:`EventClass`.
    """
        return self.add_event(
            self.EventClass(uri, etype, self.interval(at, duration, end),
                            **kwds))

    def instant(self, when):
        #----------------------
        """
    Create an :class:`.Instant` on the recording's timeline.
    """
        return self.timeline.instant(when)

    def interval(self, start, duration=None, end=None):
        #--------------------------------------------------
        """
    Create an :class:`.Interval` on the recording's timeline.
    """
        return self.timeline.interval(start, duration, end=end)


## Or create urn:uuid URIs ??

    def new_segment(self,
                    uri,
                    at,
                    duration=None,
                    end=None,
                    **kwds):  ## Of a Recording
        #---------------------------------------------------------------
        """
    Create a new :class:`~.segment.Segment` of a Recording.

    :param uri: The URI of the Segment.
    :param float at: When the segment starts.
    :param float duration: The duration of the Segment. Optional.
    :param float duration: When the Segment ends. Optional.
    :param kwds: Optional additional attributes for the Segment.

    """
        return self.add_resource(
            Segment(uri, self, self.interval(at, duration, end), **kwds))

    def save_metadata_to_graph(self, graph):
        #---------------------------------------
        """
    Add a Recording's metadata to a RDF graph.

    :param graph: A RDF graph.
    :type graph: :class:`~biosignalml.rdf.Graph`
    """
        super(Recording, self).save_metadata_to_graph(graph)
        for resource in self._resources.values():
            resource.save_metadata_to_graph(graph)

    @classmethod
    def create_from_graph(cls, uri, graph, signals=True, **kwds):
        #------------------------------------------------------------
        """
    Create a new instance of a Recording, setting attributes from RDF triples in a graph.

    :param uri: The URI for the Recording.
    :param graph: A RDF graph.
    :type graph: :class:`~biosignalml.rdf.Graph`
    :type signals: Set False to not load the Recording's Signals.
    :rtype: :class:`Recording`
    """

        self = super(Recording, cls).create_from_graph(uri, graph, **kwds)
        if signals:
            for r in graph.query(
                    "select ?s where { ?s a <%s> . ?s <%s> <%s> } order by ?s"
                    % (BSML.Signal, BSML.recording, self.uri)):
                self.add_signal(
                    self.SignalClass.create_from_graph(str(r['s']),
                                                       graph,
                                                       units=None))
        # Do we load events? There may be a lot of them...
        return self
Beispiel #4
0
 def __init__(self, uri, **kwds):
     #-------------------------------
     from biosignalml.data.time import RelativeTimeLine  ## Otherwise circular import...
     super(Recording, self).__init__(uri, **kwds)
     self.timeline = RelativeTimeLine(str(uri) + '/timeline')
     self._resources = OrderedDict()
Beispiel #5
0
def _get_timeline(tl):  # Stops a circular import
    #--------------------
    from biosignalml.data.time import RelativeTimeLine
    return RelativeTimeLine(tl)