Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Habit Tracker

habit_tracker.livemd

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

Return Home Report An Issue

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"