Repository: sunpy/sunpy

Issue:
make it easy to access the UnifiedResponse table
if you want to access keys you should be able to get at the table


Discussion:
Could you please elaborate? :sweat_smile: 
oh man, I should open better issues.

I think this is that sometimes you want to be able to get at the table we print out when you `str(results)` from a `results = Fido.search(...)`, I don't think at the moment there is any property on `UnifiedResponse` which gives you that table.
Would the table as a pandas dataframe be sufficient?
@Cadair as far as I understand the issue, currently `UnifiedResponse` object has no helper function by which we can access the range of values obtained on `result` for specific values i.e. something like values of `results['Source']`.
Am I right?

Resolving patch:
diff --git a/sunpy/net/dataretriever/client.py b/sunpy/net/dataretriever/client.py
--- a/sunpy/net/dataretriever/client.py
+++ b/sunpy/net/dataretriever/client.py
@@ -85,19 +85,19 @@ def response_block_properties(self):
         return s
 
     def __repr__(self):
-        return repr(type(self)) + repr(self._build_table())
+        return repr(type(self)) + repr(self.build_table())
 
     def __str__(self):
-        return str(self._build_table())
+        return str(self.build_table())
 
     def _repr_html_(self):
-        return self._build_table()._repr_html_()
+        return self.build_table()._repr_html_()
 
-    def _build_table(self):
+    def build_table(self):
         columns = OrderedDict((('Start Time', []), ('End Time', []),
                                ('Source', []), ('Instrument', []),
                                ('Wavelength', [])))
-        for i, qrblock in enumerate(self):
+        for qrblock in self:
             columns['Start Time'].append(
                 (qrblock.time.start).strftime(TIME_FORMAT))
             columns['End Time'].append(
diff --git a/sunpy/net/fido_factory.py b/sunpy/net/fido_factory.py
--- a/sunpy/net/fido_factory.py
+++ b/sunpy/net/fido_factory.py
@@ -158,6 +158,27 @@ def response_block_properties(self):
             s.intersection(self.get_response(i).response_block_properties())
         return s
 
+    @property
+    def tables(self):
+        """
+        Returns a list of `astropy.table.Table` for all responses present in a specific
+        `~sunpy.net.fido_factory.UnifiedResponse` object. They can then be used
+        to perform key-based indexing of objects of either type
+        `sunpy.net.dataretriever.client.QueryResponse`, `sunpy.net.vso.QueryResponse` or
+        `sunpy.net.jsoc.JSOCClient`
+
+        Returns
+        -------
+        `list`
+            A list of `astropy.table.Table`, consisting of data either from the
+            `sunpy.net.dataretriever.client.QueryResponse`, `sunpy.net.vso.QueryResponse` or
+            `sunpy.net.jsoc.JSOCClient`.
+        """
+        tables = []
+        for block in self.responses:
+            tables.append(block.build_table())
+        return tables
+
     @property
     def responses(self):
         """
diff --git a/sunpy/net/jsoc/jsoc.py b/sunpy/net/jsoc/jsoc.py
--- a/sunpy/net/jsoc/jsoc.py
+++ b/sunpy/net/jsoc/jsoc.py
@@ -62,6 +62,9 @@ def __getitem__(self, item):
     def __iter__(self):
         return (t for t in [self])
 
+    def build_table(self):
+        return self.table
+
     def append(self, table):
         if self.table is None:
             self.table = table


Test patch:
diff --git a/sunpy/net/jsoc/tests/test_jsoc.py b/sunpy/net/jsoc/tests/test_jsoc.py
--- a/sunpy/net/jsoc/tests/test_jsoc.py
+++ b/sunpy/net/jsoc/tests/test_jsoc.py
@@ -61,6 +61,18 @@ def test_post_pass():
     assert tmpresp['method'] == 'url'
 
 
+@pytest.mark.remote_data
+def test_build_table():
+    responses = client.search(
+        vso_attrs.Time('2012/1/1T00:00:00', '2012/1/1T00:00:45'),
+        attrs.Series('hmi.M_45s'), attrs.Notify('jsoc@cadair.com'))
+    table = responses.build_table()
+    assert isinstance(table, astropy.table.Table)
+
+    columns = ['T_REC', 'TELESCOP', 'INSTRUME', 'WAVELNTH', 'CAR_ROT']
+    assert columns == table.colnames
+
+
 @pytest.mark.remote_data
 def test_post_wavelength():
     responses = client.search(
diff --git a/sunpy/net/tests/test_fido.py b/sunpy/net/tests/test_fido.py
--- a/sunpy/net/tests/test_fido.py
+++ b/sunpy/net/tests/test_fido.py
@@ -12,6 +12,7 @@
 from parfive.utils import FailedDownload
 
 import astropy.units as u
+from astropy.table import Table
 
 from sunpy import config
 from sunpy.net import Fido, attr
@@ -21,7 +22,7 @@
 from sunpy.net.dataretriever.sources.goes import XRSClient
 from sunpy.net.fido_factory import UnifiedResponse
 from sunpy.net.tests.strategies import goes_time, offline_instruments, online_instruments, time_attr
-from sunpy.net.vso import QueryResponse as vsoQueryResponse
+from sunpy.net.vso import QueryResponse as vsoQueryResponse, attrs as va
 from sunpy.net.vso.vso import DownloadFailed
 from sunpy.time import TimeRange, parse_time
 from sunpy.util.datatype_factory_base import MultipleMatchError
@@ -223,6 +224,62 @@ def test_unifiedresponse_slicing_reverse():
     assert results[0, ::-1]._list[0] == results._list[0][::-1]
 
 
+@pytest.mark.remote_data
+def test_tables_single_response():
+    results = Fido.search(
+        a.Time("2012/1/1", "2012/1/5"), a.Instrument("lyra"))
+    tables = results.tables
+
+    assert isinstance(tables, list)
+    assert isinstance(tables[0], Table)
+    assert len(tables) == 1
+
+    columns = ['Start Time', 'End Time', 'Source', 'Instrument', 'Wavelength']
+    assert columns == tables[0].colnames
+    assert len(tables[0]) == 5
+
+
+@pytest.mark.remote_data
+def test_tables_multiple_response():
+    results = Fido.search(a.Time('2012/3/4', '2012/3/6'),
+                          a.Instrument('lyra') | a.Instrument('rhessi'))
+    tables = results.tables
+    assert isinstance(tables, list)
+    assert isinstance(tables[0], Table) and isinstance(tables[1], Table)
+    assert len(tables) == 2
+
+    columns = ['Start Time', 'End Time', 'Source', 'Instrument', 'Wavelength']
+    assert columns == tables[0].colnames and columns == tables[1].colnames
+
+    assert all(entry == 'lyra' for entry in tables[0]['Instrument'])
+    assert all(entry == 'rhessi' for entry in tables[1]['Instrument'])
+
+
+@pytest.mark.remote_data
+def test_tables_all_types():
+    # Data retriver response objects
+    drclient = Fido.search(a.Time('2012/3/4', '2012/3/6'),
+                           a.Instrument('lyra') | a.Instrument('rhessi'))
+    drtables = drclient.tables
+    assert isinstance(drtables, list)
+    assert isinstance(drtables[0], Table)
+
+    # VSO response objects
+    vsoclient = Fido.search(va.Time('2011-06-07 06:33', '2011-06-07 06:33:08'),
+                            va.Instrument('aia'), va.Wavelength(171 * u.AA))
+    vsotables = vsoclient.tables
+    assert isinstance(vsotables, list)
+    assert isinstance(vsotables[0], Table)
+
+    # JSOC response objects
+    jsocclient = Fido.search(a.Time('2014-01-01T00:00:00', '2014-01-01T01:00:00'),
+                             a.jsoc.Series('hmi.v_45s'), a.jsoc.Notify('sunpy@sunpy.org'))
+    jsoctables = jsocclient.tables
+
+    assert isinstance(jsoctables, list)
+    assert isinstance(jsoctables[0], Table)
+
+
 def test_vso_unifiedresponse():
     vrep = vsoQueryResponse([])
     vrep.client = True