init: inital commit
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
defmodule Localiser.Domain.Floors do
|
||||
import Ecto.Query
|
||||
|
||||
alias Localiser.Repo
|
||||
alias Localiser.Domain.Schema.Floor
|
||||
|
||||
def list_floors do
|
||||
Repo.all(Floor)
|
||||
end
|
||||
|
||||
def get_floor!(id), do: Repo.get!(Floor, id)
|
||||
|
||||
def create_floor(attrs) do
|
||||
%Floor{}
|
||||
|> Floor.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
def update_floor(%Floor{} = floor, attrs) do
|
||||
floor
|
||||
|> Floor.changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def delete_floor(%Floor{} = floor) do
|
||||
Repo.delete(floor)
|
||||
end
|
||||
|
||||
def list_floors_with_rooms do
|
||||
Floor
|
||||
|> preload(:rooms)
|
||||
|> Repo.all()
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,34 @@
|
||||
defmodule Localiser.Domain.Rooms do
|
||||
import Ecto.Query
|
||||
|
||||
alias Localiser.Repo
|
||||
alias Localiser.Domain.Schema.Room
|
||||
|
||||
def list_rooms do
|
||||
Repo.all(Room)
|
||||
end
|
||||
|
||||
def list_rooms_for_floor(floor_id) do
|
||||
Room
|
||||
|> where([r], r.floor_id == ^floor_id)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
def get_room!(id), do: Repo.get!(Room, id)
|
||||
|
||||
def create_room(attrs) do
|
||||
%Room{}
|
||||
|> Room.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
def update_room(%Room{} = room, attrs) do
|
||||
room
|
||||
|> Room.changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def delete_room(%Room{} = room) do
|
||||
Repo.delete(room)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
defmodule Localiser.Domain.Schema.Floor do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Localiser.Domain.Schema.Room
|
||||
|
||||
schema "floors" do
|
||||
field :name, :string
|
||||
|
||||
has_many :rooms, Room
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(floor, attrs) do
|
||||
floor
|
||||
|> cast(attrs, [:name])
|
||||
|> validate_required([:name])
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
defmodule Localiser.Domain.Schema.Room do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Localiser.Domain.Schema.Floor
|
||||
alias Localiser.Domain.Schema.Sensor
|
||||
|
||||
schema "rooms" do
|
||||
field :name, :string
|
||||
field :width, :float
|
||||
field :height, :float
|
||||
field :offset_x, :float
|
||||
field :offset_y, :float
|
||||
|
||||
belongs_to :floor, Floor
|
||||
has_many :sensors, Sensor
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(room, attrs) do
|
||||
room
|
||||
|> cast(attrs, [:name, :floor_id, :width, :height, :offset_x, :offset_y])
|
||||
|> validate_required([:name, :floor_id])
|
||||
|> assoc_constraint(:floor)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,27 @@
|
||||
defmodule Localiser.Domain.Schema.Sensor do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Localiser.Domain.Schema.Room
|
||||
alias Localiser.Domain.Schema.SensorCalibration
|
||||
|
||||
schema "sensors" do
|
||||
field :sensor_id, :string
|
||||
field :x, :float
|
||||
field :y, :float
|
||||
|
||||
belongs_to :room, Room
|
||||
has_many :calibrations, SensorCalibration
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(sensor, attrs) do
|
||||
sensor
|
||||
|> cast(attrs, [:sensor_id, :room_id, :x, :y])
|
||||
|> validate_required([:sensor_id])
|
||||
|> unique_constraint(:sensor_id)
|
||||
|> assoc_constraint(:room)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,26 @@
|
||||
defmodule Localiser.Domain.Schema.SensorCalibration do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Localiser.Domain.Schema.Sensor
|
||||
|
||||
@timestamps_opts [inserted_at: :inserted_at, updated_at: false, type: :utc_datetime]
|
||||
|
||||
schema "sensor_calibrations" do
|
||||
field :rssi_ref, :integer
|
||||
field :path_loss_exp, :float
|
||||
field :calibrated_at, :utc_datetime
|
||||
|
||||
belongs_to :sensor, Sensor
|
||||
|
||||
timestamps(@timestamps_opts)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(calibration, attrs) do
|
||||
calibration
|
||||
|> cast(attrs, [:sensor_id, :rssi_ref, :path_loss_exp, :calibrated_at])
|
||||
|> validate_required([:sensor_id, :rssi_ref, :path_loss_exp, :calibrated_at])
|
||||
|> assoc_constraint(:sensor)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
defmodule Localiser.Domain.Schema.Tag do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
schema "tags" do
|
||||
field :tag_id, :string
|
||||
field :name, :string
|
||||
field :color, :string
|
||||
field :metadata, :map
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(tag, attrs) do
|
||||
tag
|
||||
|> cast(attrs, [:tag_id, :name, :color, :metadata])
|
||||
|> validate_required([:tag_id])
|
||||
|> unique_constraint(:tag_id)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
defmodule Localiser.Domain.Schema.User do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
schema "users" do
|
||||
field :username, :string
|
||||
field :password_hash, :string, redact: true
|
||||
field :password, :string, virtual: true, redact: true
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(user, attrs) do
|
||||
user
|
||||
|> cast(attrs, [:username, :password])
|
||||
|> validate_required([:username, :password])
|
||||
|> validate_length(:password, min: 8)
|
||||
|> unique_constraint(:username)
|
||||
|> hash_password()
|
||||
end
|
||||
|
||||
defp hash_password(%Ecto.Changeset{valid?: true, changes: %{password: pw}} = changeset) do
|
||||
put_change(changeset, :password_hash, Argon2.hash_pwd_salt(pw))
|
||||
end
|
||||
|
||||
defp hash_password(changeset), do: changeset
|
||||
end
|
||||
@@ -0,0 +1,118 @@
|
||||
defmodule Localiser.Domain.Sensors do
|
||||
import Ecto.Query
|
||||
|
||||
alias Localiser.Repo
|
||||
alias Localiser.Domain.Schema.Sensor
|
||||
alias Localiser.Domain.Schema.SensorCalibration
|
||||
|
||||
def list_sensors do
|
||||
Repo.all(Sensor)
|
||||
end
|
||||
|
||||
def list_sensors_for_room(room_id) do
|
||||
Sensor
|
||||
|> where([s], s.room_id == ^room_id)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
def list_sensors_for_floor(floor_id) do
|
||||
Sensor
|
||||
|> join(:inner, [s], r in assoc(s, :room))
|
||||
|> where([_s, r], r.floor_id == ^floor_id)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
def get_sensor!(id), do: Repo.get!(Sensor, id)
|
||||
|
||||
def get_sensor_by_sensor_id(sensor_id) do
|
||||
Repo.get_by(Sensor, sensor_id: sensor_id)
|
||||
end
|
||||
|
||||
def create_sensor(attrs) do
|
||||
%Sensor{}
|
||||
|> Sensor.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
def update_sensor(%Sensor{} = sensor, attrs) do
|
||||
sensor
|
||||
|> Sensor.changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def delete_sensor(%Sensor{} = sensor) do
|
||||
Repo.delete(sensor)
|
||||
end
|
||||
|
||||
def enroll_sensor(%Sensor{} = sensor, room_id) do
|
||||
sensor
|
||||
|> Sensor.changeset(%{room_id: room_id})
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def add_calibration(%Sensor{} = sensor, attrs) do
|
||||
attrs = Map.put(attrs, :sensor_id, sensor.id)
|
||||
|
||||
%SensorCalibration{}
|
||||
|> SensorCalibration.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
def list_calibrations(%Sensor{} = sensor) do
|
||||
SensorCalibration
|
||||
|> where([c], c.sensor_id == ^sensor.id)
|
||||
|> order_by([c], desc: c.calibrated_at)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
def latest_calibration(%Sensor{} = sensor) do
|
||||
SensorCalibration
|
||||
|> where([c], c.sensor_id == ^sensor.id)
|
||||
|> order_by([c], desc: c.calibrated_at)
|
||||
|> limit(1)
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
# Sensor lifecycle / enrollment helpers
|
||||
|
||||
# Called when an ESP board self-announces on MQTT. Inserts a new unplaced sensor
|
||||
# record, or bumps updated_at if the sensor_id already exists.
|
||||
def upsert_announced(sensor_id) do
|
||||
result =
|
||||
%Sensor{}
|
||||
|> Sensor.changeset(%{sensor_id: sensor_id})
|
||||
|> Repo.insert(
|
||||
on_conflict: [set: [updated_at: DateTime.utc_now()]],
|
||||
conflict_target: :sensor_id,
|
||||
returning: true
|
||||
)
|
||||
|
||||
case result do
|
||||
{:ok, sensor} ->
|
||||
Phoenix.PubSub.broadcast(Localiser.PubSub, "sensors", {:sensor_announced, sensor})
|
||||
{:ok, sensor}
|
||||
|
||||
error ->
|
||||
error
|
||||
end
|
||||
end
|
||||
|
||||
# Places (or re-places) a sensor at a specific position within a room.
|
||||
# Broadcasts {:sensor_enrolled, sensor, room} for Sensor.Supervisor / Sensor.Server.
|
||||
def place_sensor(%Sensor{} = sensor, room_id, {x, y}) do
|
||||
with {:ok, sensor} <- update_sensor(sensor, %{room_id: room_id, x: x, y: y}) do
|
||||
room = Repo.preload(sensor, :room).room
|
||||
Phoenix.PubSub.broadcast(Localiser.PubSub, "sensors", {:sensor_enrolled, sensor, room})
|
||||
{:ok, sensor}
|
||||
end
|
||||
end
|
||||
|
||||
# Removes a sensor from the room layout without deleting the DB record.
|
||||
# Broadcasts {:sensor_unenrolled, sensor_id} for Sensor.Supervisor.
|
||||
def remove_from_layout(%Sensor{} = sensor) do
|
||||
with {:ok, sensor} <- update_sensor(sensor, %{room_id: nil, x: nil, y: nil}) do
|
||||
Phoenix.PubSub.broadcast(Localiser.PubSub, "sensors", {:sensor_unenrolled, sensor.sensor_id})
|
||||
{:ok, sensor}
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,30 @@
|
||||
defmodule Localiser.Domain.Tags do
|
||||
alias Localiser.Repo
|
||||
alias Localiser.Domain.Schema.Tag
|
||||
|
||||
def list_tags do
|
||||
Repo.all(Tag)
|
||||
end
|
||||
|
||||
def get_tag!(id), do: Repo.get!(Tag, id)
|
||||
|
||||
def get_tag_by_tag_id(tag_id) do
|
||||
Repo.get_by(Tag, tag_id: tag_id)
|
||||
end
|
||||
|
||||
def create_tag(attrs) do
|
||||
%Tag{}
|
||||
|> Tag.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
def update_tag(%Tag{} = tag, attrs) do
|
||||
tag
|
||||
|> Tag.changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def delete_tag(%Tag{} = tag) do
|
||||
Repo.delete(tag)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,32 @@
|
||||
defmodule Localiser.Domain.Users do
|
||||
alias Localiser.Repo
|
||||
alias Localiser.Domain.Schema.User
|
||||
|
||||
def get_user!(id), do: Repo.get!(User, id)
|
||||
|
||||
def get_user_by_username(username) do
|
||||
Repo.get_by(User, username: username)
|
||||
end
|
||||
|
||||
def create_user(attrs) do
|
||||
%User{}
|
||||
|> User.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
def authenticate_user(username, password) do
|
||||
user = get_user_by_username(username)
|
||||
|
||||
cond do
|
||||
user && Argon2.verify_pass(password, user.password_hash) ->
|
||||
{:ok, user}
|
||||
|
||||
user ->
|
||||
{:error, :invalid_credentials}
|
||||
|
||||
true ->
|
||||
Argon2.no_user_verify()
|
||||
{:error, :invalid_credentials}
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user