Skip to content

Commit

Permalink
add metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
ksevelyar committed Sep 6, 2024
1 parent efa1857 commit b82c00d
Show file tree
Hide file tree
Showing 23 changed files with 593 additions and 25 deletions.
Binary file added .erlang-history/erlang-shell-log.idx
Binary file not shown.
Binary file added .erlang-history/erlang-shell-log.siz
Binary file not shown.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ erl_crash.dump
habits-*.tar

.direnv
.nix-mix
.erlang-history
2 changes: 1 addition & 1 deletion config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ config :habits, Habits.Repo,
config :habits, HabitsWeb.Endpoint,
# Binding to loopback ipv4 address prevents access from other machines.
# Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
http: [ip: {127, 0, 0, 1}, port: 4000],
http: [ip: {127, 0, 0, 1}, port: 5000],
check_origin: false,
code_reloader: true,
debug_errors: true,
Expand Down
4 changes: 2 additions & 2 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@
pkgs.inotify-tools
];

hooks = ''
shellHook = ''
mkdir -p .nix-mix .nix-hex
export MIX_HOME=$PWD/.nix-mix
export HEX_HOME=$PWD/.nix-mix
export PATH=$MIX_HOME/bin:$HEX_HOME/bin:$PATH
mix local.hex --force
export ERL_AFLAGS="-kernel shell_history enabled -kernel shell_history_path '\"$PWD/.erlang-history\"'"
export FRONT=http://habits.lcl:3000
'';
};
});
Expand Down
4 changes: 3 additions & 1 deletion lib/habits/chains/chain.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,22 @@ defmodule Habits.Chains.Chain do
use Ecto.Schema
import Ecto.Changeset

@derive {Jason.Encoder, only: [:name, :id]}
schema "chains" do
field :active, :boolean, default: true
field :name, :string
field :type, Ecto.Enum, values: [:integer, :float, :boolean]
field :description, :string

belongs_to :user, Habits.Users.User
has_many :metrics, Habits.Metrics.Metric

timestamps(type: :utc_datetime)
end

def changeset(chain, attrs) do
chain
|> cast(attrs, [:name, :type, :active])
|> cast(attrs, [:name, :type, :active, :description])
|> validate_required([:name, :type, :active])
end
end
57 changes: 57 additions & 0 deletions lib/habits/metrics.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
defmodule Habits.Metrics do
import Ecto.Query, warn: false
alias Habits.Repo

alias Habits.Metrics.Metric
alias Habits.Chains.Chain

def get_by_date(user_id, date) do
case Date.from_iso8601(date) do
{:ok, date} ->
query =
from c in Chain,
left_join: m in Metric,
on: m.chain_id == c.id and m.date == ^date,
where: c.active == ^true,
where: c.user_id == ^user_id,
select: %{id: m.id, value: m.value, chain: c}

Repo.all(query)

error ->
error
end
end

def upsert(chain, attrs) do
metric_changeset =
chain
|> Ecto.build_assoc(:metrics)
|> Metric.changeset(attrs)

Repo.insert(
metric_changeset,
on_conflict: {:replace, [:value, :updated_at]},
conflict_target: [:chain_id, :date],
returning: true
)
end

def get_metric!(user_id, metric_id) do
query =
from m in Metric,
join: c in Chain,
on: m.chain_id == c.id,
where: m.id == ^metric_id,
where: c.user_id == ^user_id

case Repo.one(query) do
nil -> raise Ecto.NoResultsError, queryable: query
result -> result
end
end

def delete_metric(%Metric{} = metric) do
Repo.delete(metric)
end
end
20 changes: 20 additions & 0 deletions lib/habits/metrics/metric.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
defmodule Habits.Metrics.Metric do
use Ecto.Schema
import Ecto.Changeset

@derive {Jason.Encoder, only: [:value, :date, :chain, :updated_at]}
schema "metrics" do
field :value, :integer
field :date, :date

belongs_to :chain, Habits.Chains.Chain

timestamps(type: :utc_datetime)
end

def changeset(chain, attrs) do
chain
|> cast(attrs, [:value, :chain_id, :date])
|> validate_required([:value, :chain_id, :date])
end
end
6 changes: 5 additions & 1 deletion lib/habits_web/authentication.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ defmodule HabitsWeb.Authentication do
use HabitsWeb, :verified_routes

import Plug.Conn
import Phoenix.Controller

alias Habits.Users

Expand Down Expand Up @@ -76,7 +77,10 @@ defmodule HabitsWeb.Authentication do
if conn.assigns[:current_user] do
conn
else
conn |> put_status(:unauthorized) |> halt()
conn
|> put_status(:unauthorized)
|> json(%{error: "unauthorized"})
|> halt()
end
end
end
6 changes: 6 additions & 0 deletions lib/habits_web/controllers/chain_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ defmodule HabitsWeb.ChainController do
end
end

def show(conn, %{"id" => id}) do
chain = Chains.get_chain!(conn.assigns.current_user.id, id)

render(conn, :show, chain: chain)
end

def update(conn, %{"id" => id, "chain" => chain_params}) do
chain = Chains.get_chain!(conn.assigns.current_user.id, id)

Expand Down
13 changes: 4 additions & 9 deletions lib/habits_web/controllers/chain_json.ex
Original file line number Diff line number Diff line change
@@ -1,26 +1,21 @@
defmodule HabitsWeb.ChainJSON do
alias Habits.Chains.Chain

@doc """
Renders a list of chains.
"""
def index(%{chains: chains}) do
%{data: for(chain <- chains, do: data(chain))}
for(chain <- chains, do: data(chain))
end

@doc """
Renders a single chain.
"""
def show(%{chain: chain}) do
%{data: data(chain)}
data(chain)
end

defp data(%Chain{} = chain) do
%{
id: chain.id,
name: chain.name,
type: chain.type,
active: chain.active
active: chain.active,
description: chain.description
}
end
end
29 changes: 29 additions & 0 deletions lib/habits_web/controllers/metric_controller.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
defmodule HabitsWeb.MetricController do
use HabitsWeb, :controller

alias Habits.Metrics
alias Habits.Chains

action_fallback HabitsWeb.FallbackController

def index(conn, params) do
metrics = Metrics.get_by_date(conn.assigns.current_user.id, params["date"])
render(conn, :index, metrics: metrics)
end

def create(conn, %{"metric" => metric_params}) do
chain = Chains.get_chain!(conn.assigns.current_user.id, metric_params["chain_id"])

with {:ok, metric} <- Metrics.upsert(chain, metric_params) do
render(conn, :create, metric: metric)
end
end

def delete(conn, %{"id" => id}) do
metric = Metrics.get_metric!(conn.assigns.current_user.id, id)

with {:ok, _metric} <- Metrics.delete_metric(metric) do
send_resp(conn, :no_content, "")
end
end
end
22 changes: 22 additions & 0 deletions lib/habits_web/controllers/metric_json.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
defmodule HabitsWeb.MetricJSON do
def index(%{metrics: metrics}) do
for(metric <- metrics, do: data(metric))
end

def show(%{metric: metric}) do
data(metric)
end

def create(%{metric: metric}) do
%{value: metric.value, updated_at: metric.updated_at, id: metric.id}
end

defp data(metric) do
%{
id: metric.id,
value: metric.value,
chain: metric.chain.name,
chain_id: metric.chain.id
}
end
end
4 changes: 2 additions & 2 deletions lib/habits_web/controllers/session_json.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule HabitsWeb.SessionJSON do
%{id: user.id, email: user.email, handle: user.handle}
end

def error(_) do
%{errors: "🐗"}
def error(params) do
%{errors: params.conn.assigns[:error_message]}
end
end
2 changes: 2 additions & 0 deletions lib/habits_web/endpoint.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ defmodule HabitsWeb.Endpoint do
websocket: [connect_info: [session: @session_options]],
longpoll: [connect_info: [session: @session_options]]

plug CORSPlug, origin: [System.get_env("FRONT")]

# Serve at "/" the static files from "priv/static" directory.
#
# You should set gzip to true if you are running phx.digest
Expand Down
5 changes: 3 additions & 2 deletions lib/habits_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ defmodule HabitsWeb.Router do
import HabitsWeb.Authentication, only: [fetch_current_user: 2, require_authenticated_user: 2]

pipeline :api do
plug :fetch_session
plug :accepts, ["json"]
end

pipeline :user_api do
plug :fetch_session
plug :fetch_current_user
plug :require_authenticated_user
end
Expand All @@ -26,7 +26,8 @@ defmodule HabitsWeb.Router do
resources "/users", UserController, only: [:update, :show]
resources "/sessions", SessionController, only: [:delete, :show], singleton: true

resources "/chains", ChainController, except: [:new, :edit]
resources "/chains", ChainController, only: [:show, :index, :create, :update, :delete]
resources "/metrics", MetricController, only: [:index, :create, :delete]
end

# Enable LiveDashboard and Swoosh mailbox preview in development
Expand Down
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ defmodule Habits.MixProject do
{:gettext, "~> 0.20"},
{:jason, "~> 1.2"},
{:dns_cluster, "~> 0.1.1"},
{:bandit, "~> 1.5"}
{:bandit, "~> 1.5"},
{:cors_plug, "~> 3.0"}
]
end

Expand Down
1 change: 1 addition & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"bcrypt_elixir": {:hex, :bcrypt_elixir, "3.1.0", "0b110a9a6c619b19a7f73fa3004aa11d6e719a67e672d1633dc36b6b2290a0f7", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "2ad2acb5a8bc049e8d5aa267802631912bb80d5f4110a178ae7999e69dca1bf7"},
"castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"},
"comeonin": {:hex, :comeonin, "5.4.0", "246a56ca3f41d404380fc6465650ddaa532c7f98be4bda1b4656b3a37cc13abe", [:mix], [], "hexpm", "796393a9e50d01999d56b7b8420ab0481a7538d0caf80919da493b4a6e51faf1"},
"cors_plug": {:hex, :cors_plug, "3.0.3", "7c3ac52b39624bc616db2e937c282f3f623f25f8d550068b6710e58d04a0e330", [:mix], [{:plug, "~> 1.13", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3f2d759e8c272ed3835fab2ef11b46bddab8c1ab9528167bd463b6452edf830d"},
"db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"},
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
"dns_cluster": {:hex, :dns_cluster, "0.1.3", "0bc20a2c88ed6cc494f2964075c359f8c2d00e1bf25518a6a6c7fd277c9b0c66", [:mix], [], "hexpm", "46cb7c4a1b3e52c7ad4cbe33ca5079fbde4840dedeafca2baf77996c2da1bc33"},
Expand Down
7 changes: 6 additions & 1 deletion priv/repo/migrations/20240730173131_create_chains.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ defmodule Habits.Repo.Migrations.CreateChains do
add :name, :string, null: false
add :description, :string
add :type, :string, null: false
add :active, :boolean, default: true, null: false
add :user_id, references(:users, on_delete: :delete_all), null: false

add :active, :boolean, default: true, null: false
add :time_range, :string
add :days_of_month, :string
add :days_of_week, :string
add :months, :string

timestamps(type: :utc_datetime)
end

Expand Down
15 changes: 15 additions & 0 deletions priv/repo/migrations/20240808091302_create_metrics.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
defmodule Habits.Repo.Migrations.CreateMetrics do
use Ecto.Migration

def change do
create table(:metrics) do
add :value, :integer, null: false
add :chain_id, references(:chains, on_delete: :delete_all), null: false
add :date, :date, null: false

timestamps(type: :utc_datetime)
end

create unique_index(:metrics, [:chain_id, :date])
end
end
Loading

0 comments on commit b82c00d

Please sign in to comment.