Habit Tracker
Mix.install([
{:kino, github: "livebook-dev/kino", override: true},
{:kino_lab, "~> 0.1.0-dev", github: "jonatanklosko/kino_lab"},
{:vega_lite, "~> 0.1.4"},
{:kino_vega_lite, "~> 0.1.1"},
{:benchee, "~> 0.1"},
{:ecto, "~> 3.7"},
{:math, "~> 0.7.0"},
{:faker, "~> 0.17.0"},
{:utils, path: "#{__DIR__}/../utils"},
{:tested_cell, git: "https://github.com/BrooklinJazz/tested_cell"}
])
Navigation
Setup
Ensure you type the ea
keyboard shortcut to evaluate all Elixir cells before starting. Alternatively you can evaluate the Elixir cells as you read.
Habit Tracker
We are building a habit tracking app.
Each task has been assigned a different point value. Users earn points based on the tasks they complete and track progress on daily goals.
flowchart
A["Large (30 points)"]
B["Medium (20 points)"]
C["Small (5 points)"]
Define Tasks
Bind a variable small
, medium
, and large
to represent the point value as an integer (see above) for each task.
Replace nil
with your answers.
ExUnit.start(auto_run: false)
defmodule Assertion do
use ExUnit.Case
test "Exercise" do
try do
Process.flag(:trap_exit, true)
small = 5
medium = 20
large = 30
assert small == 5
assert medium == 20
assert large == 30
catch
error ->
flunk("""
Your solution threw the following error:
#{inspect(error)}
""")
:exit, {error, {GenServer, message_type, [_pid, message, _timeout]}} ->
flunk("""
GenServer crashed with the following error:
#{inspect(error)}
When it recieved: #{inspect(message)} #{message_type}
Likely you need to define the corresponding handler for #{inspect(message)}.
Ensure you defined a `handle_call/3`, `handle_info/2`, or `handle_cast/2` or appropriate handler function.
def handle_call(:message, _from, state) do
...
end
Then ensure you call `GenServer.call/2`, `GenServer.cast/2`, or otherwise send the message correctly.
GenServer.call(pid, :message)
""")
:exit, error ->
flunk("""
Unhandled exit with the following error:
#{inspect(error)}
""")
after
# all warnings and errors are printed to the previous Kino Frame
# to avoid cluttering the test results display.
Process.sleep(10)
Kino.render(Kino.Markdown.new("### Test Results
"))
end
end
end
ExUnit.run()
# Make variables and modules defined in the test available.
# Also allows for exploration using the output of the cell.
# Unfortunately, this results in duplication of warnings.
small = 5
medium = 20
large = 30
Add Tasks
Given a user has completed a small and medium task, how many points do the have?
Use the small
and medium
variables to calculate your answer.
Replace nil
with your answer below.
ExUnit.start(auto_run: false)
defmodule Assertion do
use ExUnit.Case
test "Exercise" do
try do
Process.flag(:trap_exit, true)
small = 5
medium = 20
total_points = small + medium
assert total_points == 20 + 5
catch
error ->
flunk("""
Your solution threw the following error:
#{inspect(error)}
""")
:exit, {error, {GenServer, message_type, [_pid, message, _timeout]}} ->
flunk("""
GenServer crashed with the following error:
#{inspect(error)}
When it recieved: #{inspect(message)} #{message_type}
Likely you need to define the corresponding handler for #{inspect(message)}.
Ensure you defined a `handle_call/3`, `handle_info/2`, or `handle_cast/2` or appropriate handler function.
def handle_call(:message, _from, state) do
...
end
Then ensure you call `GenServer.call/2`, `GenServer.cast/2`, or otherwise send the message correctly.
GenServer.call(pid, :message)
""")
:exit, error ->
flunk("""
Unhandled exit with the following error:
#{inspect(error)}
""")
after
# all warnings and errors are printed to the previous Kino Frame
# to avoid cluttering the test results display.
Process.sleep(10)
Kino.render(Kino.Markdown.new("### Test Results
"))
end
end
end
ExUnit.run()
# Make variables and modules defined in the test available.
# Also allows for exploration using the output of the cell.
# Unfortunately, this results in duplication of warnings.
small = 5
medium = 20
total_points = small + medium
Point Goal Percentages
Users define a number of points they would like to earn each day.
What percentage progress have they made if they would like to earn 40
points and have
completed a small
and a medium
task?
Remember that you can calculate percentage with $\frac{points}{point\ goal} * 100$
Replace nil
with your answer below.
ExUnit.start(auto_run: false)
defmodule Assertion do
use ExUnit.Case
test "Exercise" do
try do
Process.flag(:trap_exit, true)
point_goal = 40
percentage = 25 / 40 * 100
assert percentage == (5 + 20) / 40 * 100
catch
error ->
flunk("""
Your solution threw the following error:
#{inspect(error)}
""")
:exit, {error, {GenServer, message_type, [_pid, message, _timeout]}} ->
flunk("""
GenServer crashed with the following error:
#{inspect(error)}
When it recieved: #{inspect(message)} #{message_type}
Likely you need to define the corresponding handler for #{inspect(message)}.
Ensure you defined a `handle_call/3`, `handle_info/2`, or `handle_cast/2` or appropriate handler function.
def handle_call(:message, _from, state) do
...
end
Then ensure you call `GenServer.call/2`, `GenServer.cast/2`, or otherwise send the message correctly.
GenServer.call(pid, :message)
""")
:exit, error ->
flunk("""
Unhandled exit with the following error:
#{inspect(error)}
""")
after
# all warnings and errors are printed to the previous Kino Frame
# to avoid cluttering the test results display.
Process.sleep(10)
Kino.render(Kino.Markdown.new("### Test Results
"))
end
end
end
ExUnit.run()
# Make variables and modules defined in the test available.
# Also allows for exploration using the output of the cell.
# Unfortunately, this results in duplication of warnings.
point_goal = 40
percentage = 25 / 40 * 100
New Feature Added - Penalties
A new feature is added. Users now must complete tasks within a time limit or receive a penalty.
Late tasks earn 50%
their normal value. So a task worth 10
points is now worth 5
when late.
A user has completed
- a medium task
- a small task
- a late large task
What is their total point score? Replace nil
with your answer.
ExUnit.start(auto_run: false)
defmodule Assertion do
use ExUnit.Case
test "Exercise" do
try do
Process.flag(:trap_exit, true)
small = 5
medium = 20
large = 30
total_points = small + medium + large / 2
assert total_points == 5 + 20 + 30 * 0.5
catch
error ->
flunk("""
Your solution threw the following error:
#{inspect(error)}
""")
:exit, {error, {GenServer, message_type, [_pid, message, _timeout]}} ->
flunk("""
GenServer crashed with the following error:
#{inspect(error)}
When it recieved: #{inspect(message)} #{message_type}
Likely you need to define the corresponding handler for #{inspect(message)}.
Ensure you defined a `handle_call/3`, `handle_info/2`, or `handle_cast/2` or appropriate handler function.
def handle_call(:message, _from, state) do
...
end
Then ensure you call `GenServer.call/2`, `GenServer.cast/2`, or otherwise send the message correctly.
GenServer.call(pid, :message)
""")
:exit, error ->
flunk("""
Unhandled exit with the following error:
#{inspect(error)}
""")
after
# all warnings and errors are printed to the previous Kino Frame
# to avoid cluttering the test results display.
Process.sleep(10)
Kino.render(Kino.Markdown.new("### Test Results
"))
end
end
end
ExUnit.run()
# Make variables and modules defined in the test available.
# Also allows for exploration using the output of the cell.
# Unfortunately, this results in duplication of warnings.
small = 5
medium = 20
large = 30
total_points = small + medium + large / 2
A user has completed
-
three late
medium
tasks -
three late
small
tasks.
What is their total point score? Replace nil
with your answer.
ExUnit.start(auto_run: false)
defmodule Assertion do
use ExUnit.Case
test "Exercise" do
try do
Process.flag(:trap_exit, true)
small = 5
medium = 20
total_points = 3 * medium / 2 + 3 * small / 2
assert total_points == 5 / 2 * 3 + 20 / 2 * 3
catch
error ->
flunk("""
Your solution threw the following error:
#{inspect(error)}
""")
:exit, {error, {GenServer, message_type, [_pid, message, _timeout]}} ->
flunk("""
GenServer crashed with the following error:
#{inspect(error)}
When it recieved: #{inspect(message)} #{message_type}
Likely you need to define the corresponding handler for #{inspect(message)}.
Ensure you defined a `handle_call/3`, `handle_info/2`, or `handle_cast/2` or appropriate handler function.
def handle_call(:message, _from, state) do
...
end
Then ensure you call `GenServer.call/2`, `GenServer.cast/2`, or otherwise send the message correctly.
GenServer.call(pid, :message)
""")
:exit, error ->
flunk("""
Unhandled exit with the following error:
#{inspect(error)}
""")
after
# all warnings and errors are printed to the previous Kino Frame
# to avoid cluttering the test results display.
Process.sleep(10)
Kino.render(Kino.Markdown.new("### Test Results
"))
end
end
end
ExUnit.run()
# Make variables and modules defined in the test available.
# Also allows for exploration using the output of the cell.
# Unfortunately, this results in duplication of warnings.
small = 5
medium = 20
total_points = 3 * medium / 2 + 3 * small / 2
New Feature Added - Rewards
A new feature is added. Users now must complete tasks within a time limit or receive a penalty.
Early tasks earn an additional 60%
of their normal value. So a task worth 10
points is now worth 16
when early.
A user has completed:
- one early medium task
- one early small
- one late large task
What is their total point score? Replace nil
with your answer.
ExUnit.start(auto_run: false)
defmodule Assertion do
use ExUnit.Case
test "Exercise" do
try do
Process.flag(:trap_exit, true)
small = 5
medium = 20
large = 30
total_points = small * 1.6 + medium * 1.6 + large * 0.5
assert total_points == 20 * 1.6 + 5 * 1.6 + 30 * 0.5
catch
error ->
flunk("""
Your solution threw the following error:
#{inspect(error)}
""")
:exit, {error, {GenServer, message_type, [_pid, message, _timeout]}} ->
flunk("""
GenServer crashed with the following error:
#{inspect(error)}
When it recieved: #{inspect(message)} #{message_type}
Likely you need to define the corresponding handler for #{inspect(message)}.
Ensure you defined a `handle_call/3`, `handle_info/2`, or `handle_cast/2` or appropriate handler function.
def handle_call(:message, _from, state) do
...
end
Then ensure you call `GenServer.call/2`, `GenServer.cast/2`, or otherwise send the message correctly.
GenServer.call(pid, :message)
""")
:exit, error ->
flunk("""
Unhandled exit with the following error:
#{inspect(error)}
""")
after
# all warnings and errors are printed to the previous Kino Frame
# to avoid cluttering the test results display.
Process.sleep(10)
Kino.render(Kino.Markdown.new("### Test Results
"))
end
end
end
ExUnit.run()
# Make variables and modules defined in the test available.
# Also allows for exploration using the output of the cell.
# Unfortunately, this results in duplication of warnings.
small = 5
medium = 20
large = 30
total_points = small * 1.6 + medium * 1.6 + large * 0.5
(BONUS): Add Your Own Feature
Add your own feature to the habit tracking app. Use comments to describe your feature and show how it would affect calculating the total score and or the total percentage of completed tasks.
Commit Your Progress
Run the following in your command line from the project folder to track and save your progress in a Git commit.
$ git add .
$ git commit -m "finish habit tracker exercise"