From 04daf191f7e63080b1ddb58bd4fa1ac8264b9f99 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Sun, 18 Jan 2026 11:23:43 -0600 Subject: [PATCH] fix(imports): handle DateTime microseconds and integer string floats Fixed two import-related errors: 1. DateTime microseconds error when updating API token last_used_at: - Error: :utc_datetime expects microseconds to be empty - Fix: Use DateTime.truncate(:second) to remove microseconds - Location: lib/towerops/api_tokens.ex:179 2. Float parsing error for integer strings like "0": - Error: :erlang.binary_to_float("0") - not a textual representation of float - Fix: Use Float.parse/1 instead of String.to_float/1 - Float.parse/1 handles both "0" and "0.0" correctly - Returns {float, remainder} on success, :error on failure - Location: lib/towerops/device_profiles/importer.ex:380 Both errors were causing profile imports to fail with crashes. --- lib/towerops/api_tokens.ex | 2 +- lib/towerops/device_profiles/importer.ex | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/towerops/api_tokens.ex b/lib/towerops/api_tokens.ex index c4fe7182..6ade21b7 100644 --- a/lib/towerops/api_tokens.ex +++ b/lib/towerops/api_tokens.ex @@ -176,7 +176,7 @@ defmodule Towerops.ApiTokens do # We don't care if this fails Task.start(fn -> token - |> Ecto.Changeset.change(last_used_at: DateTime.utc_now()) + |> Ecto.Changeset.change(last_used_at: DateTime.truncate(DateTime.utc_now(), :second)) |> Repo.update() end) end diff --git a/lib/towerops/device_profiles/importer.ex b/lib/towerops/device_profiles/importer.ex index 14e85ee9..523d0e53 100644 --- a/lib/towerops/device_profiles/importer.ex +++ b/lib/towerops/device_profiles/importer.ex @@ -377,7 +377,14 @@ defmodule Towerops.DeviceProfiles.Importer do defp parse_float(nil), do: nil defp parse_float(value) when is_float(value), do: value defp parse_float(value) when is_integer(value), do: value * 1.0 - defp parse_float(value) when is_binary(value), do: String.to_float(value) + + defp parse_float(value) when is_binary(value) do + case Float.parse(value) do + {float, _remainder} -> float + :error -> nil + end + end + defp parse_float(_), do: nil defp extract_sensor_options(sensor) do