Skip to content
This repository was archived by the owner on Jan 9, 2020. It is now read-only.

Commit 93f18ec

Browse files
authored
Merge pull request #254 from jrleeman/Improve_Solution_Checks
Improve Solution Automation
2 parents 3d9e9f3 + 6f1ff28 commit 93f18ec

5 files changed

Lines changed: 30 additions & 85 deletions

File tree

notebooks/Animation/Creating Animations.ipynb

Lines changed: 3 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -476,52 +476,7 @@
476476
"metadata": {},
477477
"outputs": [],
478478
"source": [
479-
"# %load solutions/animation.py\n",
480-
"from netCDF4 import num2date\n",
481-
"\n",
482-
"# Load data\n",
483-
"cat = TDSCatalog('http://thredds-test.unidata.ucar.edu/thredds/catalog/casestudies/irma/model/gfs/catalog.xml')\n",
484-
"best_ds = cat.datasets['Best GFS Half Degree Forecast Time Series']\n",
485-
"\n",
486-
"# Access the best dataset using the subset service and request data\n",
487-
"ncss = best_ds.subset()\n",
488-
"\n",
489-
"# Set up query\n",
490-
"query = ncss.query().accept('netcdf4')\n",
491-
"query.lonlat_box(west=-90, east=-55, south=15, north=30)\n",
492-
"query.variables('Pressure_surface', 'Wind_speed_gust_surface')\n",
493-
"query.time_range(datetime(2017, 9, 6, 12), datetime(2017, 9, 11, 12))\n",
494-
"\n",
495-
"# Pull useful pieces out of nc\n",
496-
"nc = ncss.get_data(query)\n",
497-
"lon = nc.variables['longitude'][:]\n",
498-
"lat = nc.variables['latitude'][:]\n",
499-
"press = nc.variables['Pressure_surface']\n",
500-
"winds = nc.variables['Wind_speed_gust_surface']\n",
501-
"time_var = nc.variables['time1']\n",
502-
"times = num2date(time_var[:], time_var.units)\n",
503-
"\n",
504-
"# Create a figure for plotting\n",
505-
"proj = ccrs.LambertConformal(central_longitude=-70)\n",
506-
"fig = plt.figure(figsize=(10, 5))\n",
507-
"ax = fig.add_subplot(1, 1, 1, projection=proj)\n",
508-
"ax.coastlines()\n",
509-
"add_metpy_logo(fig, x=15, y=15)\n",
510-
"\n",
511-
"# Setup up the animation, looping over data to do the plotting that we want\n",
512-
"pressure_levels = np.arange(95000, 105000, 800)\n",
513-
"wind_levels = np.arange(0., 100., 10.)\n",
514-
"artists = []\n",
515-
"\n",
516-
"for press_slice, wind_slice, time in zip(press, winds, times):\n",
517-
" press_contour = ax.contour(lon, lat, press_slice, pressure_levels,\n",
518-
" transform=ccrs.PlateCarree(), colors='black')\n",
519-
" wind_contour = ax.contour(lon, lat, wind_slice, wind_levels,\n",
520-
" transform=ccrs.PlateCarree(), colors='blue')\n",
521-
" text = ax.text(0.5, 1.01, time, transform=ax.transAxes, ha='center')\n",
522-
" artists.append(press_contour.collections + wind_contour.collections + [text])\n",
523-
"\n",
524-
"manimation.ArtistAnimation(fig, artists, interval=100)\n"
479+
"# %load solutions/animation.py"
525480
]
526481
},
527482
{
@@ -535,9 +490,9 @@
535490
],
536491
"metadata": {
537492
"kernelspec": {
538-
"display_name": "Python [conda env:unidata-workshop]",
493+
"display_name": "Python 3",
539494
"language": "python",
540-
"name": "conda-env-unidata-workshop-py"
495+
"name": "python3"
541496
},
542497
"language_info": {
543498
"codemirror_mode": {

notebooks/CartoPy/CartoPy.ipynb

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -396,34 +396,15 @@
396396
"metadata": {},
397397
"outputs": [],
398398
"source": [
399-
"# %load solutions/map.py\n",
400-
"fig = plt.figure(figsize=(10, 8))\n",
401-
"ax = fig.add_subplot(1, 1, 1, projection=ccrs.Mercator())\n",
402-
"\n",
403-
"ax.add_feature(cfeat.COASTLINE)\n",
404-
"ax.add_feature(cfeat.LAND, facecolor='tab:brown')\n",
405-
"ax.add_feature(cfeat.OCEAN, facecolor='tab:cyan')\n",
406-
"ax.add_feature(cfeat.BORDERS, linewidth=2)\n",
407-
"ax.add_feature(state_borders, linestyle=\"--\", edgecolor='black')\n",
408-
"\n",
409-
"ax.plot(-101.877, 33.583, marker='o', color='tab:green', transform=ccrs.PlateCarree())\n",
410-
"\n",
411-
"ax.set_extent([-108, -93, 25, 37])"
399+
"# %load solutions/map.py"
412400
]
413-
},
414-
{
415-
"cell_type": "code",
416-
"execution_count": null,
417-
"metadata": {},
418-
"outputs": [],
419-
"source": []
420401
}
421402
],
422403
"metadata": {
423404
"kernelspec": {
424-
"display_name": "Python [conda env:unidata-workshop]",
405+
"display_name": "Python 3",
425406
"language": "python",
426-
"name": "conda-env-unidata-workshop-py"
407+
"name": "python3"
427408
},
428409
"language_info": {
429410
"codemirror_mode": {

notebooks/Satellite_Data/Working with Satellite Data.ipynb

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
"metadata": {},
8989
"outputs": [],
9090
"source": [
91-
"list(cat.datasets)[-5:]"
91+
"cat.datasets[-5:]"
9292
]
9393
},
9494
{
@@ -115,7 +115,7 @@
115115
"<div class=\"alert alert-success\">\n",
116116
" <b>EXERCISE</b>:\n",
117117
" <ul>\n",
118-
" <li>Using the link to the Hurricane Irma archive above, get data from September 10, 2017 at 15Z for the mid-level water vapor channel (9) in the Mesoscale-1 sector. You could search through the datasets list manually, but instead try the `cat.datasets.filter_time_nearest` functinoality!</li>\n",
118+
" <li>Using the link to the Hurricane Irma archive above, get data from September 10, 2017 at 15Z for the mid-level water vapor channel (9) in the Mesoscale-1 sector. You could search through the datasets list manually, but instead try the `cat.datasets.filter_time_nearest` functionality!</li>\n",
119119
" </ul>\n",
120120
"</div>"
121121
]
@@ -135,11 +135,7 @@
135135
"metadata": {},
136136
"outputs": [],
137137
"source": [
138-
"# %load solutions/satellite_data.py\n",
139-
"cat = TDSCatalog('http://thredds-test.unidata.ucar.edu/thredds/catalog/casestudies/irma/goes16/Mesoscale-1/Channel09/20170910/catalog.xml')\n",
140-
"ds = cat.datasets.filter_time_nearest(datetime(2017, 9, 10, 0))\n",
141-
"print(ds.name)\n",
142-
"ds = ds.remote_access(service='OPENDAP')"
138+
"# %load solutions/satellite_data.py"
143139
]
144140
},
145141
{
@@ -175,7 +171,7 @@
175171
"source": [
176172
"Our goal is to plot the imagery, so we'll be using the `Sectorized_CMI` variable from the `.variables` dictionary.\n",
177173
"Rather than just giving back the raw array of data, this gives back a `Variable` object; from here not only\n",
178-
"can we get the raw data values, but there is useful metadata as well. We can see just what additional information\n",
174+
"can we get the raw data values, but there are useful metadata as well. We can see just what additional information\n",
179175
"is present by printing out the `Variable` object:"
180176
]
181177
},
@@ -342,7 +338,7 @@
342338
"metadata": {},
343339
"source": [
344340
"Now that we know how to properly reference the imagery data, we can plot\n",
345-
"the data. CartoPy's projections are designed to interface with matplotlib, so they can just be passed as the `projection` keyword argument when creating an `Axes` using the `add_subplot` method. Since the x and y coordinates, as well as the image data, are referenced in the lambert conformal projection, we can pass all of them directly to plotting methods (such as `imshow`) with no additional information. The `extent` keyword argument to `imshow` is used to specify the bounds of the image data being plotted. It is **especially important** to specify that the `origin` is at the the upper left of the image (standard practice in imagery). If your forget this, your image will be flipped. Try it!"
341+
"the data. CartoPy's projections are designed to interface with matplotlib, so they can just be passed as the `projection` keyword argument when creating an `Axes` using the `add_subplot` method. Since the x and y coordinates, as well as the image data, are referenced in the lambert conformal projection, we can pass all of them directly to plotting methods (such as `imshow`) with no additional information. The `extent` keyword argument to `imshow` is used to specify the bounds of the image data being plotted. It is **especially important** to specify that the `origin` is at the upper left of the image (standard practice in imagery). If your forget this, your image will be flipped. Try it!"
346342
]
347343
},
348344
{
@@ -370,7 +366,7 @@
370366
"cell_type": "markdown",
371367
"metadata": {},
372368
"source": [
373-
"This is a nice start, but it would be nice to have better geographic references for the image. For example, where are the states? Cartopy's `feature` module has support for adding geographic features to plots ,with many features are built in. The `BORDERS` built-in feature contains country borders. There is also support for creating \"custom\" features from the [Natural Earth](http://www.naturalearthdata.com/) set of free vector and raster map data (CartoPy will automatically download the necessary data and cache it locally). Here we create a feature for states/provinces."
369+
"This is a nice start, but it would be nice to have better geographic references for the image. For example, where are the states? Cartopy's `feature` module has support for adding geographic features to plots, with many features are built in. The `BORDERS` built-in feature contains country borders. There is also support for creating \"custom\" features from the [Natural Earth](http://www.naturalearthdata.com/) set of free vector and raster map data (CartoPy will automatically download the necessary data and cache it locally). Here we create a feature for states/provinces."
374370
]
375371
},
376372
{
@@ -552,8 +548,7 @@
552548
"cell_type": "markdown",
553549
"metadata": {},
554550
"source": [
555-
"Well, that's a good idea, but the text is invisible in some places! White text and black text have issues, but we can outline the text using matplotlib's [path effects](http://matplotlib.org/users/patheffects_guide.html). More details on path effects can be found in the [documentation](http://matplotlib.org/users/patheffects_guide.html) if you want to know more.\n",
556-
"\n"
551+
"Well, that's a good idea, but the text is invisible in some places! White text and black text have issues, but we can outline the text using matplotlib's [path effects](http://matplotlib.org/users/patheffects_guide.html). More details on path effects can be found in the [documentation](http://matplotlib.org/users/patheffects_guide.html) if you want to know more."
557552
]
558553
},
559554
{
@@ -649,7 +644,7 @@
649644
"cell_type": "markdown",
650645
"metadata": {},
651646
"source": [
652-
"First we'll import the animation support from matplotlib. We also tell it that we want it to render the animations to HTML using the HTML5 video tag:"
647+
"First, we'll import the animation support from matplotlib. We also tell it that we want it to render the animations to HTML using the HTML5 video tag:"
653648
]
654649
},
655650
{
@@ -690,7 +685,7 @@
690685
"cell_type": "markdown",
691686
"metadata": {},
692687
"source": [
693-
"Then we loop over a bunch of the datasets. For each one we pull out the data and plot both the timestamp and the image. The `ArtistAnimation` class takes the `Figure` instance and a list as required arguments. The contents of this list is a collection of matplotlib artists for each frame of the animation. In the loop below, we populate this list with the `Text` instance created when adding the timestamp as well as the image that results from plotting the data."
688+
"Then we loop over a bunch of the datasets. For each one we pull out the data and plot both the timestamp and the image. The `ArtistAnimation` class takes the `Figure` instance and a list as required arguments. The contents of this list are a collection of matplotlib artists for each frame of the animation. In the loop below, we populate this list with the `Text` instance created when adding the timestamp as well as the image that results from plotting the data."
694689
]
695690
},
696691
{
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
shr_u, shr_v = mpcalc.bulk_shear(p, u, v, depth=200 * units.hPa)
2+
print(shr_u, shr_v)

notebooks_preprocess.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ def format_script_for_cell(path):
99
"""Read and format a .py file to be inserted into the json for a cell."""
1010
header = '\n# Cell content replaced by load magic replacement.\n'
1111
with open(str(path), encoding='utf8') as f:
12-
return header + f.read()
12+
solution = f.read()
13+
if not solution:
14+
raise RuntimeError('Solution {} has no content.'.format(path))
15+
return header + solution
1316

1417

1518
def find_load_magics_in_cell(cell):
@@ -27,6 +30,14 @@ def get_cell_content_as_string(cell):
2730
return ''.join(cell['source']) + '\n'
2831

2932

33+
def find_extra_content(cell_text):
34+
"""Find and non load magic or blank lines in a solution cell."""
35+
for line in cell_text.split('\n'):
36+
m = re.match('#\s?%load.*', line)
37+
if not m and line:
38+
raise RuntimeError('Solution cell has extra content: {}'.format(cell_text))
39+
40+
3041
def process_cell(path, cell):
3142
"""Append the data from the load magics into the cell content."""
3243
modified = False
@@ -39,6 +50,7 @@ def process_cell(path, cell):
3950
script_path = path.parent / magic_string.split('load ')[1]
4051
formatted_script = format_script_for_cell(script_path)
4152
cell_str = get_cell_content_as_string(cell)
53+
find_extra_content(cell_str)
4254
cell['source'] = cell_str + formatted_script
4355
modified = True
4456
return modified

0 commit comments

Comments
 (0)