Repository: jupyterhub/zero-to-jupyterhub-k8s

Issue:
cost estimator fails to run
To reproduce:

- Launch the cost estimator on binder https://mybinder.org/v2/gh/jupyterhub/zero-to-jupyterhub-k8s/master?filepath=doc/ntbk/draw_function.ipynb
- Run the first cell
- Observe a traceback similar to the following:

```
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-1-de8d1e48d121> in <module>
----> 1 from z2jh import cost_display, disk, machines
      2 machines

~/doc/ntbk/z2jh/__init__.py in <module>
      1 """Helper functions to calculate cost."""
      2 
----> 3 from .cost import cost_display, disk
      4 from .cost import machines_list as machines

~/doc/ntbk/z2jh/cost.py in <module>
     39                 thisrow.append(jj.text.strip())
     40         rows.append(thisrow)
---> 41     df = pd.DataFrame(rows[:-1], columns=header)
     42     all_dfs.append(df)
     43 

/srv/conda/lib/python3.6/site-packages/pandas/core/frame.py in __init__(self, data, index, columns, dtype, copy)
    433                     if is_named_tuple(data[0]) and columns is None:
    434                         columns = data[0]._fields
--> 435                     arrays, columns = to_arrays(data, columns, dtype=dtype)
    436                     columns = ensure_index(columns)
    437 

/srv/conda/lib/python3.6/site-packages/pandas/core/internals/construction.py in to_arrays(data, columns, coerce_float, dtype)
    402     if isinstance(data[0], (list, tuple)):
    403         return _list_to_arrays(data, columns, coerce_float=coerce_float,
--> 404                                dtype=dtype)
    405     elif isinstance(data[0], compat.Mapping):
    406         return _list_of_dict_to_arrays(data, columns,

/srv/conda/lib/python3.6/site-packages/pandas/core/internals/construction.py in _list_to_arrays(data, columns, coerce_float, dtype)
    434         content = list(lib.to_object_array(data).T)
    435     return _convert_object_array(content, columns, dtype=dtype,
--> 436                                  coerce_float=coerce_float)
    437 
    438 

/srv/conda/lib/python3.6/site-packages/pandas/core/internals/construction.py in _convert_object_array(content, columns, coerce_float, dtype)
    490             raise AssertionError('{col:d} columns passed, passed data had '
    491                                  '{con} columns'.format(col=len(columns),
--> 492                                                         con=len(content)))
    493 
    494     # provide soft conversion of object dtypes

AssertionError: 10 columns passed, passed data had 5 columns
```


Discussion:
hey @akhmerov - thanks for following up on this. I bet that somethings been updated which is causing this error to pop up (maybe bqplot? if I remember, that was a bit unstable for a while).

Is this something you were able to figure out, by any chance? We can try to take a look at it, but might be a bit of time.
No, I found this during a quick search for cloud cost estimations. Didn't find an alternative source, and moved on.

Overall I'd be also fully satisfied with a ballpark estimate that's not relying on stability of bqplot. Anything like "A student working for X hours a week for Y weeks will cost you Z"

Resolving patch:
diff --git a/doc/ntbk/z2jh/cost.py b/doc/ntbk/z2jh/cost.py
--- a/doc/ntbk/z2jh/cost.py
+++ b/doc/ntbk/z2jh/cost.py
@@ -15,16 +15,16 @@
 locale.setlocale(locale.LC_ALL, '')
 
 # --- MACHINE COSTS ---
-http = requests.get('https://cloud.google.com/compute/pricing')
-http = bs4(http.text)
+resp = requests.get('https://cloud.google.com/compute/pricing')
+html = bs4(resp.text)
 
 # Munge the cost data
 all_dfs = []
-for table in http.find_all('table'):
-    header = table.find_all('th')
+for table in html.find_all('table'):
+    header = table.find('thead').find_all('th')
     header = [item.text for item in header]
 
-    data = table.find_all('tr')[1:]
+    data = table.find('tbody').find_all('tr')
     rows = []
     for ii in data:
         thisrow = []


Test patch: