diff --git a/src/Juliet.jl b/src/Juliet.jl index 1e5bacf..da0b4ff 100644 --- a/src/Juliet.jl +++ b/src/Juliet.jl @@ -17,6 +17,8 @@ function __init__() Welcome to Juliet, the Julia Interative Educational Tutor. Type `juliet()` to get started """) + # Seed the rng to make testing deterministic + srand(1) end """ @@ -43,8 +45,6 @@ end courses = Vector{Types.Course}() -storeDir = "$(Pkg.dir("Juliet"))/store" - help = Dict( "select" => """ HELP: @@ -73,7 +73,7 @@ function juliet() end """ -Choose a lesson and complete it +Choose a course, and then choose a lesson """ function choose_lesson(courses::Vector{Types.Course}) @match courses begin @@ -97,6 +97,9 @@ function choose_lesson(courses::Vector{Types.Course}) choose_lesson(courses[parse(Int, input)]) end +""" +Choose a lesson, then complete it +""" function choose_lesson(course::Types.Course) @match courses begin [] => begin println("No lessons in $(course.name) - exiting course"); return end @@ -132,6 +135,9 @@ function choose_lesson(course::Types.Course) end end +""" +Print a list of options +""" function print_options(list, message) if length(list) > 0 println(message) @@ -184,6 +190,9 @@ function complete_lesson(lesson::Types.Lesson) println("Finished ", lesson.name) end +""" +Get input for a question +""" function get_input(question) print("> ") input = @getInput @@ -198,6 +207,9 @@ function get_input(question::Types.InfoQuestion) return replace(input, r"\e\[([A-Z]|[0-9])", "") end +""" +Ask a question +""" function ask(question) println(question.text) end @@ -214,9 +226,12 @@ end function ask(question::Types.FunctionQuestion) println(question.text) println("`!submit` to submit file and run tests") - setup_function_file(lesson, question) + setup_function_file(question) end +""" +Validate an answer to a question +""" function validate(question::Types.InfoQuestion, response) return true end @@ -227,8 +242,8 @@ end function validate(question::Types.FunctionQuestion, response) if strip(response) != "!submit" return false end - dir = normpath("$(homedir())/Juliet/$(question.lessonName)") - file = normpath("$dir/$(question.index).jl") + dir = joinpath(homedir(), "Juliet", "FunctionQuestion") + file = joinpath(dir, filename(question)) try inputs = [pair[1] for pair in question.tests] @@ -284,32 +299,48 @@ end function show_congrats(question::Types.InfoQuestion) end +""" +Set up the file for a function question +""" function setup_function_file(question::Types.FunctionQuestion) - dir = normpath("$(homedir())/Juliet") - if !isdir(dir) mkdir(dir) end - dir = normpath("$(homedir())/Juliet/$(question.lessonName)") - if !isdir(dir) mkdir(dir) end + dir = joinpath(homedir(), "Juliet", "FunctionQuestion") + mkpath(dir) - file = normpath("$dir/$(question.index).jl") + file = joinpath(dir, filename(question)) if !isfile(file) open(file, "w") do f write(f, question.template) end end - @compat @static if is_windows() - Util.run(`explorer.exe $file`; whitelist=[1]) - elseif is_linux() - run(`xdg-open $file`) - elseif is_apple() - try - run(`open $file`) - catch - run(`open -a TextEdit $file`) + try + @compat @static if is_windows() + Util.run(`explorer.exe $file`; whitelist=[1]) + elseif is_linux() + run(`xdg-open $file`) + elseif is_apple() + try + run(`open $file`) + catch + run(`open -a TextEdit $file`) + end end + catch + println("Could not open file: please open `$file` manually") end end +""" +Generate a filename for a function question +""" +function filename(question::Types.FunctionQuestion) + description = x -> x[1:min(25, length(x))] + return "$(description(question.text))-$(hash(question)).jl" +end + +""" +Register a course with the current session of Juliet +""" function register(course::Types.Course) if !in(course, courses) push!(courses, course) diff --git a/src/convert.jl b/src/convert.jl index cd4da57..8f9b0f3 100644 --- a/src/convert.jl +++ b/src/convert.jl @@ -3,6 +3,9 @@ module Convert using Juliet using Match +""" +Convert a suitable dictionary to a leson +""" function to_lesson(dict::Dict) dict = lowercase(dict) lesson = Juliet.Util.new_lesson(dict["name"]) @@ -20,6 +23,9 @@ function to_lesson(dict::Dict) return lesson end +""" +Convert a dictionary structure into a question based on `type` field +""" function match_question(question) @match Base.lowercase(question["type"]) begin "infoquestion" => Juliet.Types.InfoQuestion(question["text"]) @@ -32,6 +38,9 @@ function match_question(question) end end +""" +Convert all dictionary keys to lowercase +""" function lowercase(dict::Dict) newdict = Dict{AbstractString, Any}() for pair in dict diff --git a/src/types.jl b/src/types.jl index 3f9bfbf..80cc0a7 100644 --- a/src/types.jl +++ b/src/types.jl @@ -30,8 +30,6 @@ immutable FunctionQuestion <: AbstractQuestion # use an array for now tests::Array{Array{AbstractString, 1}, 1} template::AbstractString - #lessonName::AbstractString - #index::Int end immutable MultiQuestion <: AbstractQuestion diff --git a/test/in/1 b/test/in/1 new file mode 100644 index 0000000..978aa7f --- /dev/null +++ b/test/in/1 @@ -0,0 +1,4 @@ + +a = 1 +b = 2a +2 diff --git a/test/in/2 b/test/in/2 new file mode 100644 index 0000000..8aa5b9f --- /dev/null +++ b/test/in/2 @@ -0,0 +1,7 @@ + +a = 2 +a = 1 +b = 3a +b = 2a +1 +2 diff --git a/test/in/3 b/test/in/3 new file mode 100644 index 0000000..85af0d5 --- /dev/null +++ b/test/in/3 @@ -0,0 +1 @@ +!submit diff --git a/test/out/1 b/test/out/1 new file mode 100644 index 0000000..85f8f9e --- /dev/null +++ b/test/out/1 @@ -0,0 +1,18 @@ +Welcome to Juliet, the Julia Interative Educational Tutor. +Type `juliet()` to get started + +Welcome to Juliet, the Julia Interative Educational Tutor. +Selct a lesson or course to get started, or type `!help` for information. + +Starting Basic syntax +1 / 4: This lesson will teach you about basic Julia syntax +...2 / 4: Set `a` to be 1 +> Keep up the great work! +3 / 4: Assign `b` to be twice `a` +> Keep up the great work! +4 / 4: Is this fun? +Options: +1 - no +2 - yes +> You're doing great! +Finished Basic syntax diff --git a/test/out/2 b/test/out/2 new file mode 100644 index 0000000..eb4b7e1 --- /dev/null +++ b/test/out/2 @@ -0,0 +1,21 @@ +Welcome to Juliet, the Julia Interative Educational Tutor. +Type `juliet()` to get started + +Welcome to Juliet, the Julia Interative Educational Tutor. +Selct a lesson or course to get started, or type `!help` for information. + +Starting Basic syntax +1 / 4: This lesson will teach you about basic Julia syntax +...2 / 4: Set `a` to be 1 +> One more try +> Keep up the great work! +3 / 4: Assign `b` to be twice `a` +> Close, but no cigar +> Great job! +4 / 4: Is this fun? +Options: +1 - no +2 - yes +> Hang in there +> Keep up the great work! +Finished Basic syntax diff --git a/test/out/3 b/test/out/3 new file mode 100644 index 0000000..f3e378d --- /dev/null +++ b/test/out/3 @@ -0,0 +1,12 @@ +Welcome to Juliet, the Julia Interative Educational Tutor. +Type `juliet()` to get started + +Welcome to Juliet, the Julia Interative Educational Tutor. +Selct a lesson or course to get started, or type `!help` for information. + +Starting Functions TEST +1 / 1: Return the same number +`!submit` to submit file and run tests +> 1/1 tests passed +Keep up the great work! +Finished Functions TEST diff --git a/test/runtests.jl b/test/runtests.jl index 74c1422..cc3593b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,8 +3,19 @@ using Base.Test include("unittest.jl") -f = x -> strip(x) != "" -lesson = Juliet.Util.new_lesson("1234") -Juliet.Util.add_question!(lesson, Juliet.Types.FunctionQuestion("question 1", - ["hint 1", "hint 2", "hint 3"], f)) -# Juliet.complete_lesson(lesson) +clean = x -> replace(x, "\r\n", "\n") + +for i in readdir("in") + input = joinpath("in", i) + script = joinpath("script", "$i.jl") + output = joinpath("out", i) + try + run(pipeline(pipeline(`cat $input`, `julia $script`), stdout="temp")) + run(pipeline(pipeline(`type $input`, `julia $script`), stdout="temp")) + end + open("temp", "r") do f + open("$output", "r") do g + @test clean(readall(f)) == clean(readall(g)) + end + end +end diff --git a/test/test.toml b/test/script/1.jl similarity index 73% rename from test/test.toml rename to test/script/1.jl index ef1cce6..0cc2195 100644 --- a/test/test.toml +++ b/test/script/1.jl @@ -1,3 +1,6 @@ +using Juliet + +text = """ name = "Basic syntax" description = "Some basic syntax stuff" authors = ["Matthew Lake"] @@ -19,10 +22,17 @@ text = "Assign `b` to be twice `a`" answer = "b = 2a" hints = [] - [[questions]] type = "MultiQuestion" text = "Is this fun?" options = ["no", "yes"] answer = 2 hints = [] +""" + +lesson = Juliet.Convert.to_lesson(TOML.parse(text)) + +c = Juliet.Types.Course("Basic Syntax Lesson", "", v"1", [], [], [lesson]) +Juliet.register(c) + +juliet() diff --git a/test/script/2.jl b/test/script/2.jl new file mode 100644 index 0000000..0cc2195 --- /dev/null +++ b/test/script/2.jl @@ -0,0 +1,38 @@ +using Juliet + +text = """ +name = "Basic syntax" +description = "Some basic syntax stuff" +authors = ["Matthew Lake"] +keywords = ["basic", "syntax"] + +[[questions]] +type = "InfoQuestion" +text = "This lesson will teach you about basic Julia syntax" + +[[questions]] +type = "SyntaxQuestion" +text = "Set `a` to be 1" +answer = "a = 1" +hints = [] + +[[questions]] +type = "SyntaxQuestion" +text = "Assign `b` to be twice `a`" +answer = "b = 2a" +hints = [] + +[[questions]] +type = "MultiQuestion" +text = "Is this fun?" +options = ["no", "yes"] +answer = 2 +hints = [] +""" + +lesson = Juliet.Convert.to_lesson(TOML.parse(text)) + +c = Juliet.Types.Course("Basic Syntax Lesson", "", v"1", [], [], [lesson]) +Juliet.register(c) + +juliet() diff --git a/test/script/3.jl b/test/script/3.jl new file mode 100644 index 0000000..53c1a00 --- /dev/null +++ b/test/script/3.jl @@ -0,0 +1,36 @@ +using Juliet +using TOML + +text = """ +name = "Functions TEST" +description = "All functions all the time" +authors = ["Matthew Lake"] +keywords = ["basic", "functions"] + +[[questions]] +type = "FunctionQuestion" +text = "Return the same number" +tests = [["123", "123"]] +template = "# write code here" +hints = ["Just return the number you read in"] + +""" + +lesson = Juliet.Convert.to_lesson(TOML.parse(text)) + +# Write a passing file +answer = """ +print(readline()) +""" +dir = joinpath(homedir(), "Juliet", "FunctionQuestion") +file = joinpath(dir, Juliet.filename(lesson.questions[1])) +open(file, "w") do f + write(f, answer) +end + +c = Juliet.Types.Course("Basic Function Lesson", "", v"1", [], [], [lesson]) +Juliet.register(c) + +juliet() + +# rm(file) diff --git a/test/test_functions.toml b/test/test_functions.toml deleted file mode 100644 index a95f246..0000000 --- a/test/test_functions.toml +++ /dev/null @@ -1,11 +0,0 @@ -name = "Functions" -description = "All functions all the time" -authors = ["Matthew Lake"] -keywords = ["basic", "functions"] - -[[questions]] -type = "FunctionQuestion" -text = "Return a non-zero number" -tests = [["123", "123"]] -template = "# write code here" -hints = ["hi"] diff --git a/test/unittest.jl b/test/unittest.jl index 12e8842..0cbae9b 100644 --- a/test/unittest.jl +++ b/test/unittest.jl @@ -1,4 +1,38 @@ using Juliet using Base.Test +# @tryprogress @test Juliet.@tryprogress(:(1+1)) == :(1+1) + +# @getInput +# @test Juliet.@getInput() == :(readline()) + +# choose_lesson + +# print_options + +# complete_lesson + +# get_input + +# ask + +# validate + +# show_hint + +# show_congrats + +# setup_function_file + +# register +@test Juliet.courses == [] +question = Juliet.Types.InfoQuestion("info") +lesson = Juliet.Types.Lesson("Test course", "description", v"1", ["Author"], + ["Keyword"], [question]) +course = Juliet.Types.Course("Test course", "description", v"1", ["Author"], + ["Keyword"], [lesson]) +Juliet.register(course) +@test Juliet.courses == [course] +Juliet.register(course) +@test Juliet.courses == [course]