Added 'block info' to retrieve and export portions of Curve.data[*].info.
authorW. Trevor King <wking@drexel.edu>
Thu, 2 Sep 2010 02:06:59 +0000 (22:06 -0400)
committerW. Trevor King <wking@drexel.edu>
Thu, 2 Sep 2010 02:06:59 +0000 (22:06 -0400)
doc/tutorial.txt
hooke/plugin/curve.py
test/block_info.py [new file with mode: 0644]

index e24358fa08e9e2f5988b43f9321efc47d43520f2..13e06944eec3bccfaa3c887dabf62bc80cc3053f 100644 (file)
@@ -308,69 +308,24 @@ ways.::
     hooke> remove_cantilever_from_extension --block retract
     hooke> flat_peaks_to_polymer_peaks --block retract
     hooke> polymer_fit_peaks --block retract
+    hooke> block_info --block retract --output data.yaml name 'polymer peak [0-9]*'
 
 This stores the fit parameters in the block's
-:attr:`~hooke.curve.Data.info` dictionary (see each command's `Help`_
-for details).  To access the parameters, save the playlist,::
-
-    hooke> save_playlist --output mylist.hkp
-
-load it with PyYAML_::
+:attr:`~hooke.curve.Data.info` dictionary and also appends them to
+:file:`data.yaml` (see each command's `Help`_ for details).  To access
+the parameters, load :file:`data.yaml` with PyYAML_::
 
     $ python
     >>> import yaml
     >>> import hooke.util.yaml
-    >>> mylist = yaml.load(open('mylist.hkp', 'r'))
-
-navigate to your curve,::
-
-    >>> mylist.jump(1234)
+    >>> data = yaml.load(open('data.yaml', 'r'))
 
-and extract your parameter.::
+and extract your parameter::
 
-    >>> mylist.current().data[1].info['polymer peak 0']['contour length (m)']
+    >>> data['20071120a_i27_t33.100']['retract']['polymer peak 0']['contour length (m)']
     2.124...e-08
 
-You can also get a list of all available parameters::
-
-    >>> mylist.current().data[1].info['polymer peak 0'].keys()
-    ['model', 'contour length (m)', 'temperature (K)', 'fit',
-     'persistence length (m)']
-
-or just pprint_ the whole thing::
-
-    >>> from pprint import pprint
-    >>> pprint(mylist.current().data[1].info['polymer peak 0'])
-    {'contour length (m)': 2.1248220207858103e-08,
-     'fit': {'active fitted parameters': 3.7024307200788127,
-             'active parameters': 3.7024307200788127,
-             'convergence flag': 1,
-             'covariance matrix': None,
-             'data scale factor': 5.2716380732198972e-10,
-             'fitted parameters': -2.5663294148411571,
-             'info': {'fjac': None,
-                      'fvec': None,
-                      'ipvt': None,
-                      'nfev': 16,
-                      'qtf': None},
-             'initial parameters': [-0.69314718055994529],
-             'message': 'Both actual and predicted relative reductions in the sum of squares\n  are at most 0.000000',
-             'rescaled': True,
-             'scale': None},
-     'model': u'WLC',
-     'persistence length (m)': 4.0000000000000001e-10,
-     'temperature (K)': 301.0}
-
 .. _PyYAML: http://pyyaml.org/
-.. _pprint: http://docs.python.org/library/pprint.html
-
-.. todo:: UI access to block (and curve?) info dicts.  Someone should
-   make a tree-based object browser ;).
-
-.. todo:: Playlists currently drop all :class:`~hooke.curve.Data`
-   instances when they are unloaded, so you can't extract the fitted
-   parameters from unloaded curves.  We'll need a way to export chosen
-   curve attributes to file.
 
 Command stacks
 ~~~~~~~~~~~~~~
@@ -428,7 +383,7 @@ freely jointed chain fitting`_ and `Command stacks`_.::
     hooke> remove_cantilever_from_extension --block retract
     hooke> flat_peaks_to_polymer_peaks --block retract
     hooke> polymer_fit_peaks --block retract
-    hooke> export_block --block retract --output myblock.dat
+    hooke> block_info --block retract --output data.yaml name 'polymer peak [0-9]*'
     hooke> stop_command_capture
     hooke> apply_command_stack_to_playlist
 
index b287ee4fa11bd5ad96f10de2269c6523ebd81bad..92e5bd1bb8a1aeba4b5ca90597c6f685d154fe0d 100644 (file)
@@ -25,8 +25,10 @@ associated :class:`hooke.command.Command`\s for handling
 """
 
 import copy
+import re
 
 import numpy
+import yaml
 
 from ..command import Command, Argument, Failure
 from ..command_stack import CommandStack
@@ -263,8 +265,8 @@ class CurvePlugin (Builtin):
     def __init__(self):
         super(CurvePlugin, self).__init__(name='curve')
         self._commands = [
-            GetCommand(self), InfoCommand(self), DeltaCommand(self),
-            ExportCommand(self), DifferenceCommand(self),
+            GetCommand(self), InfoCommand(self), BlockInfoCommand(self),
+            DeltaCommand(self), ExportCommand(self), DifferenceCommand(self),
             DerivativeCommand(self), PowerSpectrumCommand(self),
             ClearStackCommand(self)]
 
@@ -345,6 +347,59 @@ class InfoCommand (CurveCommand):
         return [block.shape for block in curve.data]
 
 
+class BlockInfoCommand (BlockCommand):
+    """Get selected information about a :class:`hooke.curve.Curve` data block.
+    """
+    def __init__(self, plugin):
+        super(BlockInfoCommand, self).__init__(
+            name='block info', arguments=[
+                Argument(
+                    name='key', count=-1, optional=False,
+                    help='Dot-separted (.) key selection regexp.'),
+                Argument(
+                    name='output',
+                    help="""
+File name for the output (appended).
+""".strip()),
+                ],
+            help=self.__doc__, plugin=plugin)
+
+    def _run(self, hooke, inqueue, outqueue, params):
+        block = self._block(hooke, params)
+        values = {'index': self._block_index(hooke, params)}
+        for key in params['key']:
+            keys = [(0, key.split('.'), block.info)]
+            while len(keys) > 0:
+                index,key_stack,info = keys.pop(0)
+                regexp = re.compile(key_stack[index])
+                matched = False
+                for k,v in info.items():
+                    if regexp.match(k):
+                        matched = True
+                        new_stack = copy.copy(key_stack)
+                        new_stack[index] = k
+                        if index+1 == len(key_stack):
+                            vals = values
+                            for k in new_stack[:-1]:
+                                if k not in vals:
+                                    vals[k] = {}
+                                vals = vals[k]
+                            vals[new_stack[-1]] = v
+                        else:
+                            keys.append((index+1, new_stack, v))
+                if matched == False:
+                    raise ValueError('no match found for %s in %s'
+                                     % (key_stack[index], key))
+        if params['output'] != None:
+            curve = self._curve(hooke, params)
+            with open(params['output'], 'a') as f:
+                yaml.dump({curve.name:{
+                            'path': curve.path,
+                            block.info['name']: values
+                            }}, f)
+        outqueue.put(values)
+
+
 class DeltaCommand (BlockCommand):
     """Get distance information between two points.
 
diff --git a/test/block_info.py b/test/block_info.py
new file mode 100644 (file)
index 0000000..d3da920
--- /dev/null
@@ -0,0 +1,52 @@
+# Copyright (C) 2010 W. Trevor King <wking@drexel.edu>
+#
+# This file is part of Hooke.
+#
+# Hooke is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# Hooke is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with Hooke.  If not, see
+# <http://www.gnu.org/licenses/>.
+
+"""
+>>> import os
+>>> from uuid import uuid4
+>>> from hooke.hooke import Hooke, HookeRunner
+>>> h = Hooke()
+>>> r = HookeRunner()
+>>> h = r.run_lines(h, ['load_playlist test/data/test']) # doctest: +ELLIPSIS
+<FilePlaylist test.hkp>
+Success
+<BLANKLINE>
+
+>>> file_name = '%s.block' % uuid4()
+>>> block_info_already_exists = os.path.exists(file_name)
+>>> block_info_already_exists
+False
+>>> h = r.run_lines(h, ['block_info --output %s name columns "raw info.file*"'
+...                     % file_name]) # doctest: +ELLIPSIS, +REPORT_UDIFF
+{'index': 0, 'name': 'approach', 'columns': ['z piezo (m)', 'deflection (m)'], 'raw info': {'filetype': 'picoforce'}}
+Success
+<BLANKLINE>
+>>> with open(file_name, 'r') as f:
+...     text = f.read()
+>>> if block_info_already_exists == False:
+...    os.remove(file_name)
+>>> print text
+picoforce.000:
+  approach:
+    columns: [z piezo (m), deflection (m)]
+    index: 0
+    name: approach
+    raw info: {filetype: picoforce}
+  path: /tmp/hooke/test/data/picoforce.000
+<BLANKLINE>
+"""