Skip to content

Instantly share code, notes, and snippets.

@fonsp
Created October 29, 2020 16:17
Show Gist options
  • Save fonsp/5d25fcbd73a3d36b110d6fa6b14eaf9c to your computer and use it in GitHub Desktop.
Save fonsp/5d25fcbd73a3d36b110d6fa6b14eaf9c to your computer and use it in GitHub Desktop.
### A Pluto.jl notebook ###
# v0.11.14
using Markdown
using InteractiveUtils
# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
macro bind(def, element)
quote
local el = $(esc(element))
global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing
el
end
end
# ╔═╡ 65d601a6-fe45-11ea-22c6-270bf7d353bc
begin
# some nasty Pkg business so that the homework package env does not interfere
import Pkg
grader_env = mktempdir()
Pkg.activate(grader_env)
Pkg.add(Pkg.PackageSpec(name="Pluto", rev="6ade60b"))
Pkg.add(Pkg.PackageSpec(name="PlutoUI", version=v"0.6.5"))
Pkg.add(Pkg.PackageSpec(name="DataFrames", version=v"0.21.7"))
Pkg.add(Pkg.PackageSpec(name="CSV", version=v"0.7.7"))
Pkg.add(Pkg.PackageSpec(name="BrowseTables", version=v"0.3.0"))
Pkg.activate()
pushfirst!(LOAD_PATH, grader_env)
import Pluto
import UUIDs: UUID
import Distributed
using PlutoUI
using DataFrames
import CSV
import BrowseTables: HTMLTable
end
# ╔═╡ 122cbbdc-00cd-11eb-223b-ef82328f674f
let
function solution_pascal(n)
if n <= 1
[1.0]
else
prev = solution_pascal(n - 1)
0.5 * ([prev..., 0] .+ [0, prev...])
end
end
function solution_gaussian_kernel(n)
solution_pascal(2n+1)
end
K = solution_gaussian_kernel(9)
K[1] < K[5] < K[9] > K[13] > K[17]
end
# ╔═╡ 68da9dec-e6e1-11ea-1d9e-cdb7028f9b6a
md"# Part 1: autograding"
# ╔═╡ 836cc4be-e6d7-11ea-2a6e-436c187304da
md"## Step 1: Select submission files"
# ╔═╡ 4e4e78d2-f865-11ea-0c21-fdd76cbf3532
md"You need to write some code that returns the **absolute paths** to the students' homework submissions. The following code works for me, but probably not for you."
# ╔═╡ 427f19de-e6d2-11ea-10a8-5d3552224b31
submission_files = let
all = readdir("/mnt/c/Users/fonsv/Documents/18.s191-private/homework1/grading/submissions/"; join=true)
filter(f -> endswith(f, ".jl"), all)
end
# ╔═╡ 6b40e1d0-f865-11ea-3c2a-39fb643c8068
md"It should return an arrays of strings, something like:"
# ╔═╡ 728d1ca8-f865-11ea-1a3e-33cb0a44b9dd
submission_files_EXAMPLE = ["/home/fonsi/hw1/submissions/hw1 - fonsi.jl", "/home/fonsi/hw1/submissions/hw1 - template.jl"]
# ╔═╡ 54c9b77c-e6e2-11ea-03ff-5d0d4a9dd763
if !all(isabspath, submission_files)
md"""
!!! warning
Submission paths need to be _absolute_
"""
end
# ╔═╡ d0dd703a-e6da-11ea-1d4f-0b10bf75fad6
md"## Step 2: autograde actions"
# ╔═╡ a0776916-f865-11ea-387a-bf2e3094a182
md"I have already written these, you can ignore this."
# ╔═╡ 490cc11a-00cb-11eb-24bc-2fb026c3094e
all
# ╔═╡ 8c6c6114-e6d7-11ea-20b1-e718907e0767
md"## Step 3: autograde all notebooks"
# ╔═╡ a1598cfc-e6d7-11ea-1f0b-f5560304fe7a
md"**Click to start running the notebooks:**
$(@bind run_notebooks CheckBox()) run $(length(submission_files)) notebooks
---"
# ╔═╡ b5d0d970-e6d9-11ea-20a5-01f0c4e3875c
pluto_session = Pluto.ServerSession(;options=Pluto.Configuration.from_flat_kwargs(
launch_browser=false,
require_token_for_open_links=false,
workspace_use_distributed=false,
port=2468,
))
# ╔═╡ a6fe722e-e6da-11ea-21e8-1dea77b462ef
submission_files_to_run = run_notebooks ? Pluto.tamepath.(submission_files) : String[]
# ╔═╡ 9982bbfa-00c0-11eb-0a5c-818fae8c01ce
Pluto
# ╔═╡ 5642a754-e6d9-11ea-35b6-0fe20d6a098e
notebooks = let
for nb in values(pluto_session.notebooks)
Pluto.SessionActions.shutdown(pluto_session, nb)
end
map(submission_files_to_run) do path
nb = Pluto.load_notebook(Pluto.tamepath(path))
pluto_session.notebooks[nb.notebook_id] = nb
Pluto.update_save_run!(pluto_session, nb, nb.cells; run_async=false, prerender_text=true)
nb
end
end
# ╔═╡ 3f0aec92-e6e1-11ea-1b53-29b7b543674d
# ╔═╡ 3fb6b20c-e6e1-11ea-35eb-e74598e31daf
md"# Part 2: manual review"
# ╔═╡ 9090ee3e-e6de-11ea-14c3-27032e8710d3
md"## Step 1: start notebook server"
# ╔═╡ 9c1c40f0-e6de-11ea-08d8-77acb2a550d4
md"**Click to start notebook server:**
$(@bind run_server CheckBox()) run notebook server
---"
# ╔═╡ a3e47050-e6de-11ea-2a91-0597143f71ba
if run_server
@async Pluto.run(pluto_session)
md"> Server is running at [https://localhost:2468](https://localhost:2468)"
end
# ╔═╡ 657f23ce-f687-11ea-13c0-f9959437ddf5
md"## Step 2: manual grade actions"
# ╔═╡ 8904534a-e6e1-11ea-34b7-31d1f6f8ca8f
md"## Step 3: select notebook"
# ╔═╡ eade89f2-e6de-11ea-11bb-531a6b65c666
@bind inspected_notebook_index_str Select([string(i) => nb.path for (i,nb) in enumerate(notebooks)])
# ╔═╡ 9815b7f4-f686-11ea-0760-05b34811be7f
md"#### Autograde results for selected homework"
# ╔═╡ b8d4598c-f686-11ea-24dc-6f94370fc996
md"#### Editable view of selected homework"
# ╔═╡ ca945d02-f686-11ea-03b4-393858220381
md"#### Manual grading"
# ╔═╡ dfbaecac-f855-11ea-1f3e-0522a86136ea
md"Currently stored grades for this notebook:"
# ╔═╡ 0ae5802c-f856-11ea-11ac-31a3ba67606c
@bind reset_results Button("Reset manual results")
# ╔═╡ 633ee8e4-f68a-11ea-271e-433eafd12a62
manual_results_dict = let
reset_results
Dict()
end;
# ╔═╡ 06edecc6-f864-11ea-03b0-03db23d2c780
# ╔═╡ 1a999dfe-e6e1-11ea-12f4-ed24f03245e0
inspected_notebook_index = parse(Int, inspected_notebook_index_str)
# ╔═╡ 64c26a50-e6df-11ea-2762-57186f445501
inspected_notebook = notebooks[inspected_notebook_index]
# ╔═╡ b6b986c8-e6de-11ea-1d13-5d9d370eccdc
if run_server
cd(inspected_notebook.path |> dirname)
"""
<iframe src="http://localhost:2468/edit?id=$(inspected_notebook.notebook_id)" style="width: calc(100% - 8px); height: 100vh; margin: 0; border: 4px solid pink;" allow="camera;microphone">
""" |> HTML
end
# ╔═╡ 8d1aaee8-e6de-11ea-2c2c-4d2ba138d5ce
md"# Appendix"
# ╔═╡ ce055a44-e6d8-11ea-3a07-75392c0f6c26
md"## Grading actions"
# ╔═╡ c1f5bd3a-e6d2-11ea-20ab-d93e142aa71e
abstract type GradingAction end
# ╔═╡ 9057dc04-e6d2-11ea-196b-1519cac7d248
struct AutoTestAction <: GradingAction
name
points_value::Number
test::Expr
end
# ╔═╡ 3abb56e4-e6d3-11ea-3337-392a434e1a21
struct GetValue <: GradingAction
name
getter::Expr
end
# ╔═╡ eaa49370-e6da-11ea-21d9-ddf11a7df51f
actions = let
prologue = quote
function solution_extend(v, i)
if i < 1
v[1]
elseif i > length(v)
v[end]
else
v[i]
end
end
function solution_blur_1D(v, l)
return map(eachindex(v)) do i
mean([extend(v, i+j) for j in -l:l])
end
end
function solution_convolve_vector(v, k)
l = (length(k) - 1) ÷ 2
return map(eachindex(v)) do i
sum([extend(v, i - j) * k[j + 1 + l] for j in -l:l])
end
end
function solution_pascal(n)
if n <= 1
[1.0]
else
prev = pascal(n - 1)
0.5 * ([prev..., 0] .+ [0, prev...])
end
end
function solution_gaussian_kernel(n)
solution_pascal(2n+1)
end
function solution_extend_mat(M::AbstractMatrix, i, j)
if i < 1
solution_extend_mat(M, 1, j)
elseif i > size(M, 1)
solution_extend_mat(M, size(M,1), j)
else
if j < 1
solution_extend_mat(M, i, 1)
elseif j > size(M, 2)
solution_extend_mat(M, i, size(M,2))
else
M[i,j]
end
end
end
function solution_convolve_image(M::AbstractMatrix, K::AbstractMatrix)
l = (size(K,1) - 1) ÷ 2
map(CartesianIndices(M)) do i
sum(CartesianIndices(K)) do a
offset = a - CartesianIndex(-l-1, -l-1)
extend_mat(M, (i - offset).I...) * K[a]
end
end
end
function solution_convolve_image_inverted(M::AbstractMatrix, K::AbstractMatrix)
l = (size(K,1) - 1) ÷ 2
map(CartesianIndices(M)) do i
sum(CartesianIndices(K)) do a
offset = a - CartesianIndex(-l-1, -l-1)
extend_mat(M, (i + offset).I...) * K[a]
end
end
end
function solution_with_gaussian_blur(image; sigma=3, l=5)
gauss(x,y; sigma=3) = (1/(2*pi*sigma^2))exp(-(x^2 + y^2)/(2*sigma^2))
K_gauss = [gauss(xy...) for xy in Iterators.product(-l:l,-l:l)]
convolve_image(image, K_gauss ./ sum(K_gauss))
end
function solution_sobel_edge_detect(image)
K_sobol = [
1 0 -1
2 0 -2
1 0 -1
]
x = solution_convolve_image(image, K_sobol)
y = solution_convolve_image(image, K_sobol')
return x .* x .+ y .* y
end
testimg() = rand(RGB, (10, 20))
end
actions = [
GetValue("name", :(student.name)),
GetValue("kerberos_id", :(student.kerberos_id)),
##Excercise 1.1
AutoTestAction("1.1 - random vector", 5, quote
length(random_vect) == 10 && length(Set(random_vect)) == 10
end),
AutoTestAction("1.1 - mean", 5, quote
mean([-1, -1, 2]) ≈ 0
end),
AutoTestAction("1.1 - mean of random vec", 2.5, quote
m == mean(random_vect)
end),
AutoTestAction("1.1 - demean function", 5, quote
demean([1,2,3,4,5]) ≈ [-2,-1,0,1,2]
end),
##Excercise 1.2
AutoTestAction("1.2 - create bar - 20", 2.5, quote
create_bar() ≈ let
x = zeros(100)
x[40:59] .= 1
x
end
end),
AutoTestAction("1.2 - create bar - 20 or 21", 2.5, quote
create_bar() ≈ let
x = zeros(100)
x[40:59] .= 1
x
end ||
create_bar() ≈ let
x = zeros(100)
x[40:60] .= 1
x
end
end),
##Excercise 1.3
AutoTestAction("1.3 - vecvec_to_matrix", 5, quote
vecvec_to_matrix([[1,2,3], [4,5,6], [7,8,9]]) == hcat([1,2,3], [4,5,6], [7,8,9]) ||
vecvec_to_matrix([[1,2,3], [4,5,6], [7,8,9]]) == hcat([1,2,3], [4,5,6], [7,8,9])'
end),
AutoTestAction("1.3 - matrix_to_vecvec", 5, quote
let
A = [1 2; 4 5; 7 8]
matrix_to_vecvec(A) == collect(eachcol(A)) ||
matrix_to_vecvec(A) == collect(eachrow(A))
end
end),
##Excercise 2.1
AutoTestAction("2.1 - mean colors", 5, quote
$(prologue)
t = testimg()
mean_colors(t) == (mean(t).r, mean(t).g, mean(t).b) ||
mean_colors(t) == RGB(mean(t).r, mean(t).g, mean(t).b)
end),
##Excercise 2.2
AutoTestAction("2.2 - quantize::Number", 5, quote
quantize(3) ≈ (floor(30) / 10)
end),
##Excercise 2.3
AutoTestAction("2.3 - quantize::Color", 2.5, quote
c = rand(RGB)
quantize(c) == RGB(quantize(c.r), quantize(c.g), quantize(c.b))
end),
##Excercise 2.4
AutoTestAction("2.4 - quantize::AbstractMatrix", 2.5, quote
$(prologue)
t = testimg()
quantize(t) == quantize.(t)
end),
##Excercise 2.5
AutoTestAction("2.5 - invert", 5, quote
c = rand(RGB)
invert(c) == RGB(1-c.r, 1-c.g, 1-c.b)
end),
##Excercise 2.6
AutoTestAction("2.6 - noisify::Number - noise", 2.5, quote
let
N = 10_000
# x = [.5 + rand()*.5 + .25 for _ in 1:N]
x = [noisify(.5, .25) for _ in 1:N]
sample_mean = sum(x) / N
sample_var = sum((x .- sample_mean) .^ 2) / N
abs(sample_mean - .5) < .01 && abs(sample_var - 1/48) < .01
end
end),
AutoTestAction("2.6 - noisify::Number - clamp", 2.5, quote
let
N = 10_000
# x = [rand()*.5 + .25 for _ in 1:N]
x = [noisify(.5, 10) for _ in 1:N]
0.0 <= minimum(x) < maximum(x) <= 1.0
end
end),
AutoTestAction("2.6 - noisify::Color", 2.5, quote
c = rand(RGB)
noisify(c, .1) != c
end),
AutoTestAction("2.6 - noisify::Image", 2.5, quote
$(prologue)
t = testimg()
noisify(t, .1) != t
end),
##Excercise 3.1
# N/A
##Excercise 3.2
AutoTestAction("3.2 - extend", 10, quote
$(prologue)
extend(v, -5) == solution_extend(v, -5)
end),
##Excercise 3.3
AutoTestAction("3.3 - blur_1D", 5, quote
$(prologue)
blur_1D(v, 2) == solution_blur_1D(v, 2)
end),
##Exercise 3.4
# N/A
##Excercise 3.5
AutoTestAction("3.5 - convolution", 5, quote
$(prologue)
convolve_vector([1, 10, 100, 1000, 10000], [0, 1, 0]) == solution_convolve_vector([1, 10, 100, 1000, 10000], [0, 1, 0])
end),
##Excercise 3.6
AutoTestAction("3.6 - gaussian_kernel - centered", 2.5, quote
K = gaussian_kernel(9)
center = length(K) ÷ 2
K[1] < K[center] > K[end]
end),
AutoTestAction("3.6 - gaussian_kernel - normalized", 2.5, quote
K = gaussian_kernel(9)
.9 < sum(K) < 1.1
end),
##Excercise 4.1
AutoTestAction("4.1 - extend mat", 10, quote
$(prologue)
M = rand(Float64, (3,3))
all([
extend_mat(M, i, j) ≈ solution_extend_mat(M, i, j)
for i in [-5,4,2], j in [-1,1,0,10]
])
end),
##Excercise 4.2
# AutoTestAction("4.2 - convolve image", 15, quote
# $(prologue)
# M = testimg()
# K = rand(Float64, (3,3))
# convolve_image(M, K) ≈ solution_convolve_image(M, K) ||
# convolve_image(M, K) ≈ solution_convolve_image_inverted(M, K)
# end),
# ##Excercise 4.2
# AutoTestAction("4.2 - convolve image - kernel inverted correctly", 5, quote
# $(prologue)
# M = testimg()
# K = rand(Float64, (3,3))
# convolve_image(M, K) ≈ solution_convolve_image(M, K)
# end),
##Excercise 4.3
AutoTestAction("4.3 - with_gaussian_blur - no error", 5, quote
$(prologue)
try
with_gaussian_blur(rand(Gray, (20,10))) isa Array
catch
false
end || try
with_gaussian_blur(rand(Float64, (20,10))) isa Array
catch
false
end || try
with_gaussian_blur(rand(RGB, (20,10))) isa Array
catch
false
end
end),
##Excercise 4.4
AutoTestAction("4.4 - sobel_edge_detect - no error", 5, quote
$(prologue)
try
with_sobel_edge_detect(rand(Gray, (20,10))) isa Array
catch
false
end || try
with_sobel_edge_detect(rand(Float64, (20,10))) isa Array
catch
false
end || try
with_sobel_edge_detect(rand(RGB, (20,10))) isa Array
catch
false
end
end),
]
end
# ╔═╡ c0d9fb36-f858-11ea-35c0-77963b5cf57a
sum(actions) do action
try
action.points_value
catch
0
end
end
# ╔═╡ 96917e4e-f687-11ea-2256-7b1057a3b523
begin
struct ManualScoreAction <: GradingAction
name
points_value::Number
rubric
end
ManualScoreAction(name, points_value) = ManualScoreAction(name, points_valie, name)
end
# ╔═╡ e5d3fa7c-f687-11ea-044f-6b00f0321da8
begin
struct ManualCheckAction <: GradingAction
name
points_value::Number
rubric
end
ManualCheckAction(name, points_value) = ManualCheckAction(name, points_valie, name)
end
# ╔═╡ 7b3bb8c8-f687-11ea-27bb-45ed5807090e
manual = [
ManualCheckAction("3.1 - colored line", 5, md"Did they write `colored_line(v)`?"),
ManualScoreAction("3.4 - make it interactive", 8, md"Did they create a slider? Does the slider control the amount of blur?"),
ManualScoreAction("4.2 - convolve_image", 20, md"Is convole_image correct?"),
ManualScoreAction("4.3 - Gaussian blur", 10, md"Does the Gaussian blur look okay?"),
ManualScoreAction("4.4 - Sobel filter", 10, md"Does the Sobel filter look okay?"),
]
# ╔═╡ 144aa7ec-f864-11ea-35c4-43e19d898887
sum(manual) do action
try
action.points_value
catch
0
end
end
# ╔═╡ 22fd661e-e6e7-11ea-2b4a-8981b17a790d
# struct ManualCheckAction <: GradingAction
# name
# points_value::Number
# rubric
# end
begin
inspected_notebook
manual_results_dict
@bind inspected_manual_results let
boxes = map(enumerate(manual)) do (i,action)
"""<tr>
<td><input type='number' id='field$(i)' min=0 max=$(action.points_value) step=1 value=$(action.points_value)><code> / $(action.points_value)</code></td>
<td>$(repr(MIME"text/html"(), action.rubric))</td>
</tr>"""
end
"""
<div id="hello">
<table>
<tbody>
$(join(boxes))
</tbody>
</table>
<input type="submit" value="Save!">
</div>
<style>
div#hello table td {
text-align: left;
}
div#hello table input[type=number] {
width: 4em;
text-align: center;
}
</style>
<script>
const div = this.querySelector("#hello")
const table = div.querySelector("table")
const inputs = table.querySelectorAll("input")
const update_value = () => {
//div.value = Object.fromEntries(Array.from(inputs).map((b) => [b.id, b.value]))
div.value = Array.from(inputs).map((b) => Number(b.value))
}
inputs.forEach(el => {
el.oninput = (e) => {
update_value()
e.stopPropagation()
}
})
const submit = div.querySelector("input[type=submit]")
submit.onclick = () => {
update_value()
div.dispatchEvent(new CustomEvent("input", {}))
}
</script>
""" |> HTML
end
end
# ╔═╡ e9b4c316-f852-11ea-3840-e7b1428df4e9
if inspected_manual_results === missing
get(manual_results_dict, basename(inspected_notebook.path),
md"_Scores for this homework are not yet saved._"
)
else
inspected_manual_results
end
# ╔═╡ f131ee3a-f68a-11ea-3d6c-d34439133a63
# reactively add the result to our Dict
updated_manual_results_dict = let
if inspected_manual_results !== missing
manual_results_dict[inspected_notebook_index] = inspected_manual_results
end
manual_results_dict
end
# ╔═╡ 8b9a3f7a-e6d4-11ea-34c5-ef986e6af936
begin
# default
displayname(action::GradingAction) = action.name
end
# ╔═╡ e48f2a16-e6e1-11ea-070a-d58f87569b91
md"## Misc"
# ╔═╡ 33588e20-e6d4-11ea-08f6-7d10d9ef1481
function eval_in_notebook(notebook::Pluto.Notebook, expr)
ws = Pluto.WorkspaceManager.get_workspace((pluto_session, notebook))
fetcher = :(Core.eval($(ws.module_name), $(expr |> QuoteNode)))
Distributed.remotecall_eval(Main, ws.pid, fetcher)
end
# ╔═╡ 31111cbe-e6d3-11ea-0130-a98e45b82f2b
begin
function do_action(notebook::Pluto.Notebook, action::AutoTestAction)
tester = quote
try
$(action.test)
catch
false
end
end
if eval_in_notebook(notebook, tester) === true
action.points_value
else
zero(action.points_value)
end
end
function do_action(notebook::Pluto.Notebook, action::GetValue)
eval_in_notebook(notebook, action.getter)
end
end
# ╔═╡ 33e66768-e6d9-11ea-1aba-256b4c9998b8
autograde_results = map(notebooks) do nb
map(actions) do action
do_action(nb, action)
end
end
# ╔═╡ 5ca8fb02-e6e3-11ea-0ad6-158746799400
autograde_results_df = DataFrame(map(autograde_results) do results
(;
map(zip(actions, results)) do (action, result)
Symbol(displayname(action)) => result
end...
)
end)
# ╔═╡ 7cb1c9bc-f684-11ea-00f3-dfd11c9b72ef
HTMLTable(autograde_results_df)
# ╔═╡ c9a66a4a-e6e4-11ea-0528-f3bbf6f17675
DownloadButton(sprint(CSV.write, autograde_results_df), "autograde_results.csv")
# ╔═╡ 991ddb18-e6e6-11ea-220d-71b6794f39d8
autograde_results_df[inspected_notebook_index, :]
# ╔═╡ 469a28fc-f856-11ea-2a6b-a706e607cf9f
manual_results_df = DataFrame(map(enumerate(collect(updated_manual_results_dict))) do (i,(filename, results))
(;
map(zip(manual, results)) do (action, result)
Symbol(displayname(action)) => result
end...,
autograde_results_df[1,:]...,
)
end)
# ╔═╡ 459687e0-e6e3-11ea-0c85-89516c7a2da0
DownloadButton(sprint(CSV.write, manual_results_df), "manual_results.csv")
# ╔═╡ bbcd528e-e6e5-11ea-2d9c-a50835477b2e
md"## Not used"
# ╔═╡ 0796b462-f85c-11ea-1bf2-53ad24ec447c
struct GradedStudent
name
email
grader
end
# ╔═╡ b6512e8e-f85b-11ea-0cb6-619a5b3f67bc
graders_raw = """
Muhammad Ashhad Alam ashhad13@mit.edu Jeremiah
Ali Alasmari alasmari@mit.edu Jeremiah
Shaden Alshammari shaden@mit.edu Jeremiah
Brian Avendano onadneva@mit.edu Jeremiah
Soumayya Bint Outhman bintouth@mit.edu Jeremiah
Alexander Bookbinder a01@mit.edu Jeremiah
Ray Dedhia rdedhia@mit.edu Jeremiah
Keegan Deppe kdeppe@mit.edu Jeremiah
Julian Espada jesp1999@mit.edu Shashi
Alison Fang af_16@mit.edu Shashi
RuiYang Guo mguo42@mit.edu Shashi
Tommy Heng theng@mit.edu Shashi
Terry Kang terryk@mit.edu Shashi
Justin Linick jlinick@mit.edu Shashi
Megha Maran megham@mit.edu Shashi
Zoe Marschner zoem@mit.edu Shashi
Maya Nigrin mayigrin@mit.edu Peter
Nicholas Ortiz njortiz@mit.edu Peter
Olivia Seow olivias@mit.edu Peter
Carlos Solano Saltachin csolano@mit.edu Peter
Joshua Torres torresjo@mit.edu Peter
Tho Tran thotran9@mit.edu Peter
Benjamin Urquhart benu@mit.edu Peter
Wayne Zhao wayzhao@mit.edu Peter"""
# ╔═╡ c7553450-f85b-11ea-1dbd-3d72253a58d6
graders = map(split(graders_raw, '\n')) do line
GradedStudent(split(line, '\t')...)
end
# ╔═╡ Cell order:
# ╠═65d601a6-fe45-11ea-22c6-270bf7d353bc
# ╠═122cbbdc-00cd-11eb-223b-ef82328f674f
# ╟─68da9dec-e6e1-11ea-1d9e-cdb7028f9b6a
# ╟─836cc4be-e6d7-11ea-2a6e-436c187304da
# ╟─4e4e78d2-f865-11ea-0c21-fdd76cbf3532
# ╠═427f19de-e6d2-11ea-10a8-5d3552224b31
# ╟─6b40e1d0-f865-11ea-3c2a-39fb643c8068
# ╟─728d1ca8-f865-11ea-1a3e-33cb0a44b9dd
# ╟─54c9b77c-e6e2-11ea-03ff-5d0d4a9dd763
# ╟─d0dd703a-e6da-11ea-1d4f-0b10bf75fad6
# ╟─a0776916-f865-11ea-387a-bf2e3094a182
# ╠═eaa49370-e6da-11ea-21d9-ddf11a7df51f
# ╠═490cc11a-00cb-11eb-24bc-2fb026c3094e
# ╠═c0d9fb36-f858-11ea-35c0-77963b5cf57a
# ╠═7cb1c9bc-f684-11ea-00f3-dfd11c9b72ef
# ╟─8c6c6114-e6d7-11ea-20b1-e718907e0767
# ╟─a1598cfc-e6d7-11ea-1f0b-f5560304fe7a
# ╟─b5d0d970-e6d9-11ea-20a5-01f0c4e3875c
# ╠═a6fe722e-e6da-11ea-21e8-1dea77b462ef
# ╠═9982bbfa-00c0-11eb-0a5c-818fae8c01ce
# ╠═5642a754-e6d9-11ea-35b6-0fe20d6a098e
# ╠═33e66768-e6d9-11ea-1aba-256b4c9998b8
# ╟─3f0aec92-e6e1-11ea-1b53-29b7b543674d
# ╟─5ca8fb02-e6e3-11ea-0ad6-158746799400
# ╟─c9a66a4a-e6e4-11ea-0528-f3bbf6f17675
# ╟─3fb6b20c-e6e1-11ea-35eb-e74598e31daf
# ╟─9090ee3e-e6de-11ea-14c3-27032e8710d3
# ╟─9c1c40f0-e6de-11ea-08d8-77acb2a550d4
# ╟─a3e47050-e6de-11ea-2a91-0597143f71ba
# ╟─657f23ce-f687-11ea-13c0-f9959437ddf5
# ╠═7b3bb8c8-f687-11ea-27bb-45ed5807090e
# ╠═144aa7ec-f864-11ea-35c4-43e19d898887
# ╟─8904534a-e6e1-11ea-34b7-31d1f6f8ca8f
# ╟─eade89f2-e6de-11ea-11bb-531a6b65c666
# ╟─9815b7f4-f686-11ea-0760-05b34811be7f
# ╠═991ddb18-e6e6-11ea-220d-71b6794f39d8
# ╟─b8d4598c-f686-11ea-24dc-6f94370fc996
# ╟─b6b986c8-e6de-11ea-1d13-5d9d370eccdc
# ╟─ca945d02-f686-11ea-03b4-393858220381
# ╟─dfbaecac-f855-11ea-1f3e-0522a86136ea
# ╟─e9b4c316-f852-11ea-3840-e7b1428df4e9
# ╟─22fd661e-e6e7-11ea-2b4a-8981b17a790d
# ╟─0ae5802c-f856-11ea-11ac-31a3ba67606c
# ╟─633ee8e4-f68a-11ea-271e-433eafd12a62
# ╟─469a28fc-f856-11ea-2a6b-a706e607cf9f
# ╟─459687e0-e6e3-11ea-0c85-89516c7a2da0
# ╟─06edecc6-f864-11ea-03b0-03db23d2c780
# ╟─f131ee3a-f68a-11ea-3d6c-d34439133a63
# ╟─1a999dfe-e6e1-11ea-12f4-ed24f03245e0
# ╟─64c26a50-e6df-11ea-2762-57186f445501
# ╟─8d1aaee8-e6de-11ea-2c2c-4d2ba138d5ce
# ╟─ce055a44-e6d8-11ea-3a07-75392c0f6c26
# ╠═c1f5bd3a-e6d2-11ea-20ab-d93e142aa71e
# ╠═9057dc04-e6d2-11ea-196b-1519cac7d248
# ╠═3abb56e4-e6d3-11ea-3337-392a434e1a21
# ╠═96917e4e-f687-11ea-2256-7b1057a3b523
# ╠═e5d3fa7c-f687-11ea-044f-6b00f0321da8
# ╠═31111cbe-e6d3-11ea-0130-a98e45b82f2b
# ╠═8b9a3f7a-e6d4-11ea-34c5-ef986e6af936
# ╟─e48f2a16-e6e1-11ea-070a-d58f87569b91
# ╠═33588e20-e6d4-11ea-08f6-7d10d9ef1481
# ╟─bbcd528e-e6e5-11ea-2d9c-a50835477b2e
# ╠═0796b462-f85c-11ea-1bf2-53ad24ec447c
# ╟─b6512e8e-f85b-11ea-0cb6-619a5b3f67bc
# ╠═c7553450-f85b-11ea-1dbd-3d72253a58d6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment