Skip to content

Commit

Permalink
Dump/Restore most of model in application state
Browse files Browse the repository at this point in the history
Refs: #57
  • Loading branch information
orontee committed Oct 11, 2023
1 parent b2e0b33 commit f1f4b74
Show file tree
Hide file tree
Showing 7 changed files with 289 additions and 51 deletions.
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

### Added

- Dump/Restore most of model in application state
[#57](https://github.com/orontee/taranis/issues/57)

- Configuration to preserve device being offline
[#57](https://github.com/orontee/taranis/issues/57)

Expand Down
250 changes: 250 additions & 0 deletions src/convert.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
#include "convert.h"
#include "experimental/optional"
#include "model.h"

namespace taranis {

// NAN is written as null by jsoncpp, this will fix deserialization

inline double deserialize_possible_null(const Json::Value &value) {
if (value.isNull()) {
return NAN;
}
return value.asDouble();
}

Json::Value to_json(const Location &location) {
Json::Value value;
value["longitude"] = location.longitude;
value["latitude"] = location.latitude;
value["name"] = location.name;
value["country"] = location.country;
value["state"] = location.state;

return value;
}

Json::Value to_json(const Condition &condition) {
Json::Value value;
value["date"] = static_cast<int64_t>(condition.date);
value["sunrise"] = static_cast<int64_t>(condition.sunrise);
value["sunset"] = static_cast<int64_t>(condition.sunset);
value["temperature"] = condition.temperature;
value["felt_temperature"] = condition.felt_temperature;
value["pressure"] = condition.pressure;
value["humidity"] = condition.humidity;
value["uv_index"] = condition.uv_index;
value["clouds"] = condition.clouds;
value["visibility"] = condition.visibility;
value["probability_of_precipitation"] = condition.probability_of_precipitation;
value["wind_speed"] = condition.wind_speed;
value["wind_degree"] = condition.wind_degree;
value["wind_gust"] = condition.wind_gust;
value["weather"] = condition.weather;
value["weather_description"] = condition.weather_description;
value["weather_icon_name"] = condition.weather_icon_name;
value["rain"] = condition.rain;
value["snow"] = condition.snow;

return value;
}

Json::Value to_json(const DailyCondition &condition) {
Json::Value value;
value["date"] = static_cast<int64_t>(condition.date);
value["sunrise"] = static_cast<int64_t>(condition.sunrise);
value["sunset"] = static_cast<int64_t>(condition.sunset);
value["moonrise"] = static_cast<int64_t>(condition.moonrise);
value["moonset"] = static_cast<int64_t>(condition.moonset);
value["moon_phase"] = condition.moon_phase;
value["pressure"] = condition.pressure;
value["humidity"] = condition.humidity;
value["dew_point"] = condition.dew_point;
value["wind_speed"] = condition.wind_speed;
value["wind_degree"] = condition.wind_degree;
value["wind_gust"] = condition.wind_gust;
value["clouds"] = condition.clouds;
value["probability_of_precipitation"] = condition.probability_of_precipitation;
value["uv_index"] = condition.uv_index;
value["rain"] = condition.rain;
value["snow"] = condition.snow;
value["weather"] = condition.weather;
value["weather_description"] = condition.weather_description;
value["weather_icon_name"] = condition.weather_icon_name;
value["temperature_day"] = condition.temperature_day;
value["temperature_min"] = condition.temperature_min;
value["temperature_max"] = condition.temperature_max;
value["temperature_night"] = condition.temperature_night;
value["temperature_evening"] = condition.temperature_evening;
value["temperature_morning"] = condition.temperature_morning;
value["felt_temperature_day"] = condition.felt_temperature_day;
value["felt_temperature_night"] = condition.felt_temperature_night;
value["felt_temperature_evening"] = condition.felt_temperature_evening;
value["felt_temperature_morning"] = condition.felt_temperature_morning;

return value;
}

Json::Value to_json(const Alert &alert) {
Json::Value value;
value["sender"] = alert.sender;
value["event"] = alert.event;
value["start_date"] = static_cast<int64_t>(alert.start_date);
value["end_date"] = static_cast<int64_t>(alert.end_date);
value["description"] = alert.description;
return value;
}

Json::Value to_json(const HistoryItem &item) {
Json::Value value;
value["location"] = to_json(item.location);
value["favorite"] = item.favorite;

return value;
}

Json::Value to_json(const Model &model) {
Json::Value value;
value["source"] = model.source;
value["unit_system"] = model.unit_system;
value["refresh_date"] = static_cast<int64_t>(model.refresh_date);
value["location"] = to_json(model.location);

if (model.current_condition) {
value["current_condition"] = to_json(*model.current_condition);
} else {
value["current_condition"] = Json::Value::null;
}
for (const auto &forecast : model.hourly_forecast) {
value["hourly_forecast"].append(to_json(forecast));
}
for (const auto &forecast : model.daily_forecast) {
value["daily_forecast"].append(to_json(forecast));
}
for (const auto &alert : model.alerts) {
value["alerts"].append(to_json(alert));
}
for (const auto &item : model.location_history) {
value["location_history"].append(to_json(item));
}
return value;
}

void update_from_json(Location& location, const Json::Value &value) {
location.longitude = deserialize_possible_null(value["longitude"]);
location.latitude = deserialize_possible_null(value["latitude"]);
location.name = value.get("name", "").asString();
location.country = value.get("country", "").asString();
location.state = value.get("state", "").asString();
}

void update_from_json(Condition& condition, const Json::Value &value) {
condition.date = static_cast<std::time_t>(value.get("date", 0).asLargestInt());
condition.sunrise = static_cast<std::time_t>(value.get("sunrise", 0).asLargestInt());
condition.sunset = static_cast<std::time_t>(value.get("sunset", 0).asLargestInt());
condition.temperature = deserialize_possible_null(value["temperature"]);
condition.felt_temperature = deserialize_possible_null(value["felt_temperature"]);
condition.pressure = value.get("pressure", 0).asInt();
condition.humidity = value.get("humidity", 0).asInt();
condition.uv_index = deserialize_possible_null(value["uv_index"]);
condition.clouds = value.get("clouds", 0).asInt();
condition.visibility = value.get("visibility", 0).asInt();
condition.probability_of_precipitation = deserialize_possible_null(value["probability_of_precipitation"]);
condition.wind_speed = deserialize_possible_null(value["wind_speed"]);
condition.wind_degree = value.get("wind_degree", 0).asInt();
condition.wind_gust = deserialize_possible_null(value["wind_gust"]);
condition.weather = static_cast<Weather>(value.get("weather", CLEAR_SKY).asInt());
condition.weather_description = value.get("weather_description", "").asString();
condition.weather_icon_name = value.get("weather_icon_name", "").asString();
condition.rain = deserialize_possible_null(value["rain"]);
condition.snow = deserialize_possible_null(value["snow"]);
}

void update_from_json(DailyCondition& condition, const Json::Value &value) {
condition.date = static_cast<std::time_t>(value.get("date", 0).asLargestInt());
condition.sunrise = static_cast<std::time_t>(value.get("sunrise", 0).asLargestInt());
condition.sunset = static_cast<std::time_t>(value.get("sunset", 0).asLargestInt());
condition.moonrise = static_cast<std::time_t>(value.get("moonrise", 0).asLargestInt());
condition.moonset = static_cast<std::time_t>(value.get("moonset", 0).asLargestInt());
condition.moon_phase = deserialize_possible_null(value["moon_phase"]);
condition.pressure = value.get("pressure", 0).asInt();
condition.humidity = value.get("humidity", 0).asInt();
condition.dew_point = deserialize_possible_null(value["dew_point"]);
condition.wind_speed = deserialize_possible_null(value["wind_speed"]);
condition.wind_degree = value.get("wind_degree", 0).asInt();
condition.wind_gust = deserialize_possible_null(value["wind_gust"]);
condition.clouds = value.get("clouds", 0).asInt();
condition.probability_of_precipitation = deserialize_possible_null(value["probability_of_precipitation"]);
condition.uv_index = deserialize_possible_null(value["uv_index"]);
condition.rain = deserialize_possible_null(value["rain"]);
condition.snow = deserialize_possible_null(value["snow"]);
condition.weather = static_cast<Weather>(value.get("weather", CLEAR_SKY).asInt());
condition.weather_description = value.get("weather_description", "").asString();
condition.weather_icon_name = value.get("weather_icon_name", "").asString();
condition.temperature_day = deserialize_possible_null(value["temperature_day"]);
condition.temperature_min = deserialize_possible_null(value["temperature_min"]);
condition.temperature_max = deserialize_possible_null(value["temperature_max"]);
condition.temperature_night = deserialize_possible_null(value["temperature_night"]);
condition.temperature_evening = deserialize_possible_null(value["temperature_evening"]);
condition.temperature_morning = deserialize_possible_null(value["temperature_morning"]);
condition.felt_temperature_day = deserialize_possible_null(value["felt_temperature_day"]);
condition.felt_temperature_night = deserialize_possible_null(value["felt_temperature_night"]);
condition.felt_temperature_evening = deserialize_possible_null(value["felt_temperature_evening"]);
condition.felt_temperature_morning = deserialize_possible_null(value["felt_temperature_morning"]);
}

void update_from_json(Alert& alert, const Json::Value &value) {
alert.sender = value.get("sender", "").asString();
alert.event = value.get("event", "").asString();
alert.start_date = static_cast<std::time_t>(value.get("start_date", 0).asLargestInt());
alert.end_date = static_cast<std::time_t>(value.get("end_date", 0).asLargestInt());
alert.description = value.get("description", "").asString();
}

void update_from_json(HistoryItem &item, const Json::Value &value) {
auto &location = item.location;
const auto &location_value = value["location"];
update_from_json(location, location_value);

item.favorite = value.get("favorite", false).asBool();
}

void update_from_json(Model &model, const Json::Value &value) {
model.source = value.get("source", "OpenWeather").asString();
model.unit_system = static_cast<UnitSystem>(value.get("unit_system", standard).asInt());
model.refresh_date = static_cast<std::time_t>(value.get("refresh_date", 0).asLargestInt());

auto &location = model.location;
const auto &location_value = value["location"];
update_from_json(location, location_value);

if (value["current_condition"].isNull()) {
model.current_condition = std::experimental::nullopt;
} else {
Condition current_condition{};
const auto &current_condition_value = value["current_condition"];
update_from_json(current_condition, current_condition_value);
model.current_condition = current_condition;
}
for (const auto &condition_value : value["hourly_forecast"]) {
Condition condition{};
update_from_json(condition, condition_value);
model.hourly_forecast.push_back(condition);
}
for (const auto &condition_value : value["daily_forecast"]) {
DailyCondition condition{};
update_from_json(condition, condition_value);
model.daily_forecast.push_back(condition);
}
for (const auto &alert_value : value["alerts"]) {
Alert alert{};
update_from_json(alert, alert_value);
model.alerts.push_back(alert);
}
for (const auto &item_value : value["location_history"]) {
HistoryItem item{};
update_from_json(item, item_value);
model.location_history.push_back(item);
}
}
}
21 changes: 21 additions & 0 deletions src/convert.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include <json/value.h>

#include "model.h"

namespace taranis {
Json::Value to_json(const Location &location);
Json::Value to_json(const Condition &condition);
Json::Value to_json(const DailyCondition &condition);
Json::Value to_json(const Alert &alert);
Json::Value to_json(const HistoryItem &item);
Json::Value to_json(const Model &model);

void update_from_json(Location& location, const Json::Value &value);
void update_from_json(Condition& condition, const Json::Value &value);
void update_from_json(DailyCondition& condition, const Json::Value &value);
void update_from_json(Alert& alert, const Json::Value &value);
void update_from_json(HistoryItem &item, const Json::Value &value);
void update_from_json(Model &model, const Json::Value &value);
}
1 change: 1 addition & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ sources = [about_cc] + files(
'alerts.cc',
'app.cc',
'config.cc',
'convert.cc',
'currentconditionbox.cc',
'events.cc',
'http.cc',
Expand Down
5 changes: 1 addition & 4 deletions src/model.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,8 @@ struct Alert {
};

struct HistoryItem {
const Location location;
Location location;
bool favorite;

HistoryItem(const Location &location, bool favorite)
: location{location}, favorite{favorite} {}
};

struct Model {
Expand Down
56 changes: 11 additions & 45 deletions src/state.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
#include <iomanip>
#include <json/writer.h>

constexpr char LOCATION_HISTORY_KEY[]{"location_history"};
#include "convert.h"

namespace taranis {

constexpr char MODEL_KEY[]{"model"};

void ApplicationState::restore() {
BOOST_LOG_TRIVIAL(debug) << "Restoring application state";

Expand All @@ -27,7 +29,7 @@ void ApplicationState::restore() {
BOOST_LOG_TRIVIAL(error) << "Unexpected application state";
return;
}
this->restore_location_history(root);
this->restore_model(root);
}

void ApplicationState::dump() {
Expand All @@ -41,7 +43,7 @@ void ApplicationState::dump() {
return;
}
Json::Value root;
this->dump_location_history(root);
this->dump_model(root);

output << root << std::endl;
if (not output) {
Expand All @@ -63,50 +65,14 @@ std::string ApplicationState::get_application_state_path() {
return application_state_path;
}

void ApplicationState::restore_location_history(const Json::Value &root) {
this->model->location_history.clear();

for (const auto &value : root[LOCATION_HISTORY_KEY]) {
const auto location_value = value["location"];
if (!location_value.isObject()) {
continue;
}

const auto longitude{location_value.get("longitude", NAN).asDouble()};
const auto latitude{location_value.get("latitude", NAN).asDouble()};
const auto name{location_value.get("name", "").asString()};
const auto country{location_value.get("country", "").asString()};
const auto state{location_value.get("state", "").asString()};
if (std::isnan(longitude) or std::isnan(latitude) or name.empty() or
country.empty()) {
continue;
}
const Location location{longitude, latitude, name, country, state};
const HistoryItem item{location, value.get("favorite", false).asBool()};
this->model->location_history.push_back(item);
}
if (this->model->location_history.size() > 0) {
this->model->location = this->model->location_history.begin()->location;
}
void ApplicationState::restore_model(const Json::Value &root) {
const auto &model_value = root[MODEL_KEY];
update_from_json(*this->model, model_value);
}

void ApplicationState::dump_location_history(Json::Value &root) {
auto &history_value = root[LOCATION_HISTORY_KEY];
const auto &history = this->model->location_history;
int location_index = 0;
for (const auto &item : history) {
history_value[location_index]["location"]["longitude"] =
Json::Value{item.location.longitude};
history_value[location_index]["location"]["latitude"] =
Json::Value{item.location.latitude};
history_value[location_index]["location"]["name"] = item.location.name;
history_value[location_index]["location"]["country"] =
item.location.country;
history_value[location_index]["location"]["state"] = item.location.state;
history_value[location_index]["favorite"] = item.favorite;

++location_index;
}
void ApplicationState::dump_model(Json::Value &root) {
auto &model_value = root[MODEL_KEY];
model_value = to_json(*this->model);
}

} // namespace taranis
Loading

0 comments on commit f1f4b74

Please sign in to comment.