Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update version #9

Merged
merged 10 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.12)

project(
weather-forecast
VERSION 1.3.5
VERSION 1.3.6
DESCRIPTION "Console weather forecast app that uses OpenMeteo and Yandex Geocoder API"
LANGUAGES CXX
)
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ cd %userprofile%\weather-forecast && .\weather-forecast.exe

## Использование

Программа weather-forecast - консольное приложение для просмотра погоды.
Программа weather-forecast консольное приложение для просмотра погоды.
Предусмотрен показ погоды для локаций, перечисленных в конфигурационном файле, на
текущий момент, а также на утро, день, вечер и ночь некоторого количества дней.
Программа в один момент времени отображает непосредственно прогноз на три дня, для
Expand All @@ -164,7 +164,7 @@ cd %userprofile%\weather-forecast && .\weather-forecast.exe

### Вызов

Программа может быть вызвана без аргументов - будут применены значения по умолчанию.
Программа может быть вызвана без аргументов будут применены значения по умолчанию.
Порядок аргументов не имеет значения.

#### Аргументы командной строки:
Expand Down Expand Up @@ -212,7 +212,7 @@ cd %userprofile%\weather-forecast && .\weather-forecast.exe
* `F5` или `r` - обновление данных.
* `+` - увеличение количества отображаемых дней на единицу, но не более 15.
* `-` - уменьшение количества отображаемых дней на единицу, но не менее 3.
При этом в том случае, если фокус направлен на последни день, происходит
При этом в том случае, если фокус направлен на последний день, происходит
смещение фокуса вверх.
* `w` или `ArrowUp` - смещение фокуса отображения вверх (меньшая дата) на единицу.
* `s` или `ArrowDown` - смещение фокуса отображения вверх (меньшая дата) на единицу.
Expand Down
6 changes: 3 additions & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Документация проекта

Данный документ - сборник документации по проекту Weather Forecast. Здесь
будут все ссылки на документацию.
Данный документ сборник документации по проекту Weather Forecast.
Здесь будут все ссылки на документацию.

## Документация разработки

* [Проблема](dev/problem.md)
* [Требования](dev/requirements.md)
* [Архитектура](dev/architecture.md)
* [Модуль ArgParser, v1.1.0](https://github.com/bialger/ArgParser/blob/v1.1.0/lib/argparser/docs/README.md)
* [Модуль ArgParser, v1.3.5](https://github.com/bialger/ArgParser/blob/v1.3.5/lib/argparser/docs/README.md)
10 changes: 4 additions & 6 deletions docs/dev/architecture.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Архитектура продукта

В этом документе описывается архитектура продукта - парсера аргументов командной
В этом документе описывается архитектура продукта парсера аргументов командной
строки, разработанная на основании [требований](requirements.md).

## Системная архитектура

Продукт состоит из нескольких связанных подсистем. Несколько из них - внешние
библиотеки.
Продукт состоит из нескольких связанных подсистем.
Несколько из них — внешние библиотеки.

* Используется модуль UI для взаимодействия с пользовательским вводом любого рода и
вывода информации.
* Используется модуль [ArgParser](https://github.com/bialger/ArgParser/tree/v1.1.0) - подсистема
* Используется модуль [ArgParser](https://github.com/bialger/ArgParser/tree/v1.3.5) — подсистема
для обработки аргументов командной строки.
* Используется модуль Forecast для выполнения и обработки запросов прогноза.
* Используется библиотека [C++ Requests](https://github.com/libcpr/cpr) для выполнения HTTP-запросов.
Expand Down Expand Up @@ -70,8 +70,6 @@ classDiagram
+string kProgramName$
+CompositeString kDefaultConfigPath$
+CompositeString kDefaultLogPath$
-string kIntervalDescription$
-string kDaysCountDescription$
-ostream& out_
-istream& in_
-ConditionalOutput error_output_
Expand Down
6 changes: 3 additions & 3 deletions docs/dev/requirements.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Требования к продукту

В этом документе описаны требования к продукту - приложению для показа прогноза
В этом документе описаны требования к продукту приложению для показа прогноза
погоды в консоли.

## Функциональные требования
Expand All @@ -15,14 +15,14 @@

### Требования к формату выходных данных

* Выходные данные - периодически меняющееся ASCII-изображение в терминале,
* Выходные данные периодически меняющееся ASCII-изображение в терминале,
отображающее прогноз погоды.

### Требования к функциональности продукта

* Приложение должно корректно отображать прогноз погоды для выбранного города.
* Приложение должно иметь визуально приятный интерфейс.
* Приложение должно иметь "оффлайн-режим" - при отсутствии Интернет-соединения
* Приложение должно иметь "оффлайн-режим" при отсутствии Интернет-соединения
программа не должна завершаться с ошибкой.
* Отображать прогноз погоды на несколько дней вперед (значение по умолчанию задается конфигом)
* Обновлять с некоторой частотой (задается конфигом)
Expand Down
2 changes: 1 addition & 1 deletion lib/argparser/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
include(FetchContent)
FetchContent_Declare(argparser GIT_REPOSITORY https://github.com/bialger/ArgParser GIT_TAG v1.1.0)
FetchContent_Declare(argparser GIT_REPOSITORY https://github.com/bialger/ArgParser GIT_TAG v1.3.5)
FetchContent_MakeAvailable(argparser)
17 changes: 9 additions & 8 deletions lib/forecast/Forecaster.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <regex>
#include <utility>

#include "Forecaster.hpp"

Expand All @@ -7,17 +8,17 @@
Forecaster::Forecaster(int32_t days_count,
int32_t location_index,
const std::vector<std::string>& locations,
const std::string& api_key,
std::string api_key,
const std::string& config_dir,
ConditionalOutput error_output,
ConditionalOutput log_output) : geocoder_cache_("geocoder", config_dir),
ConditionalOutput log_output) : locations_(locations),
days_count_(days_count),
location_index_(location_index),
locations_(locations),
api_key_(api_key),
api_key_(std::move(api_key)),
current_weather_("Now"),
geocoder_cache_("geocoder", config_dir),
error_output_(error_output),
log_output_(log_output),
current_weather_("Now") {
log_output_(log_output) {
forecast_ = std::vector<WeatherDay>(WeatherDay::kDaysInForecast);
}

Expand Down Expand Up @@ -236,7 +237,7 @@ int32_t Forecaster::ProcessPosition(const json& answer) {
WriteCurrentTime(log_output_);
log_output_ << "FORECASTER: Processing position\n";

int32_t result = geocoder_.SetCoordinates(answer);
const int32_t result = geocoder_.SetCoordinates(answer);

if (result != 0) {
DisplayError("Unknown error occurred while geocoding.\n", error_output_);
Expand All @@ -256,7 +257,7 @@ int32_t Forecaster::ProcessPosition(const json& answer) {

int32_t Forecaster::ProcessForecast(const json& answer) {
for (int32_t day_number = 0; day_number < WeatherDay::kDaysInForecast; ++day_number) {
bool result = forecast_[day_number].SetForecast(answer, day_number);
const bool result = forecast_[day_number].SetForecast(answer, day_number);

if (!result) {
geocoder_cache_.PutJsonToCache("error", answer);
Expand Down
2 changes: 1 addition & 1 deletion lib/forecast/Forecaster.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Forecaster {
Forecaster(int32_t days_count,
int32_t location_index,
const std::vector<std::string>& locations,
const std::string& api_key,
std::string api_key,
const std::string& config_dir,
ConditionalOutput error_output,
ConditionalOutput log_output);
Expand Down
2 changes: 1 addition & 1 deletion lib/forecast/Geocoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ int32_t Geocoder::SetCoordinates(const json& geocode) {
return 1;
}

std::vector<std::string> positions = Split(
const std::vector<std::string> positions = Split(
geocode["response"]["GeoObjectCollection"]["featureMember"][0]["GeoObject"]["Point"]["pos"].get<std::string>()
);

Expand Down
8 changes: 3 additions & 5 deletions lib/forecast/JsonCache.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#include <fstream>

#include "JsonCache.hpp"
#include "lib/utils/utils.hpp"
#include "lib/utils/utils.hpp"

const json JsonCache::kNotFound = "{}";

Expand All @@ -15,12 +13,12 @@ JsonCache::JsonCache(const std::string& cache_group, const std::string& common_c
}
}

void JsonCache::PutJsonToCache(const std::string& cache_name, const json& data) {
void JsonCache::PutJsonToCache(const std::string& cache_name, const json& data) const {
std::ofstream cache_file(GetCacheFilename(cache_name).c_str());
cache_file << data;
}

json JsonCache::GetJsonFromCache(const std::string& cache_name) {
json JsonCache::GetJsonFromCache(const std::string& cache_name) const {
if (!std::filesystem::is_regular_file(GetCacheFilename(cache_name))) {
return kNotFound;
}
Expand All @@ -30,6 +28,6 @@ json JsonCache::GetJsonFromCache(const std::string& cache_name) {
return data;
}

std::string JsonCache::GetCacheFilename(const std::string& cache_name) {
std::string JsonCache::GetCacheFilename(const std::string& cache_name) const {
return cache_dir_ + "/" + cache_name + ".json";
}
8 changes: 4 additions & 4 deletions lib/forecast/JsonCache.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@ class JsonCache {
static const json kNotFound;

JsonCache() = delete;
JsonCache(const std::string& cache_group, const std::string& cache_dir);
JsonCache(const std::string& cache_group, const std::string& common_cache_dir);

void PutJsonToCache(const std::string& cache_name, const json& data);
json GetJsonFromCache(const std::string& cache_name);
void PutJsonToCache(const std::string& cache_name, const json& data) const;
json GetJsonFromCache(const std::string& cache_name) const;

private:
std::string cache_group_;
std::string cache_dir_;

std::string GetCacheFilename(const std::string& cache_name);
std::string GetCacheFilename(const std::string& cache_name) const;
};

#endif //JSONCACHE_HPP_
8 changes: 4 additions & 4 deletions lib/forecast/WeatherDay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ WeatherTimeUnit WeatherDay::GetCurrentWeather(const json& forecast) {
return time_unit;
}

int32_t hour = std::stoi(current[kOpenMeteoNames.time].get<std::string>().substr(11, 2));
const int32_t hour = std::stoi(current[kOpenMeteoNames.time].get<std::string>().substr(11, 2));

if (hour < 0 || hour >= kHoursInDay) {
return time_unit;
Expand All @@ -89,11 +89,11 @@ bool WeatherDay::SetForecast(const json& forecast, int32_t day_number) {
}

date_ = daily_values_[kOpenMeteoNames.dates][day_number].get<std::string>();
double uv_index = daily_values_[kOpenMeteoNames.uv_index][day_number].get<double>();
const double uv_index = daily_values_[kOpenMeteoNames.uv_index][day_number].get<double>();

const size_t start_index = day_number * kHoursInDay;
const size_t end_index = start_index + kHoursInDay - 1;
const size_t kHoursInUnit = kHoursInDay / kUnitsInDay;
constexpr size_t kHoursInUnit = kHoursInDay / kUnitsInDay;

for (size_t unit_index = 0; unit_index < kUnitsInDay; ++unit_index) {
WeatherTimeUnit& unit = units_[unit_index];
Expand All @@ -110,7 +110,7 @@ bool WeatherDay::SetForecast(const json& forecast, int32_t day_number) {
double humidity_summa = 0;

for (size_t i = 0; i < kHoursInUnit; ++i) {
size_t element_index = start_index + unit_index * kHoursInUnit + i;
const size_t element_index = start_index + unit_index * kHoursInUnit + i;

if (element_index > end_index) {
return false;
Expand Down
12 changes: 7 additions & 5 deletions lib/forecast/WeatherTimeUnit.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "WeatherTimeUnit.hpp"

#include <utility>

const std::map<int32_t, std::string> WeatherTimeUnit::kWeatherCodeToString = {
{0, "Clear sky"},
{1, "Mainly clear"},
Expand Down Expand Up @@ -55,7 +57,7 @@ const std::map<std::string, std::string> WeatherTimeUnit::kChargeUnits = {
{kShownNames.humidity, "%"}
};

WeatherTimeUnit::WeatherTimeUnit(const std::string& name) : name_(name) {}
WeatherTimeUnit::WeatherTimeUnit(std::string name) : name_(std::move(name)) {}

std::map<std::string, std::string> WeatherTimeUnit::GetAllAsMap() const {
std::map<std::string, std::string> result;
Expand All @@ -71,13 +73,13 @@ std::map<std::string, std::string> WeatherTimeUnit::GetAllAsMap() const {
uv_level = "Very high";
}

std::string str_visibility = std::string(128, '\0');
auto str_visibility = std::string(128, '\0');
std::snprintf(str_visibility.data(), str_visibility.size(), "%.2f", visibility);
std::string str_pressure = std::string(128, '\0');
auto str_pressure = std::string(128, '\0');
std::snprintf(str_pressure.data(), str_pressure.size(), "%.1f", pressure);
std::string str_uv_index = std::string(128, '\0');
auto str_uv_index = std::string(128, '\0');
std::snprintf(str_uv_index.data(), str_uv_index.size(), "%.2f", uv_index);
std::string str_precipitation = std::string(128, '\0');
auto str_precipitation = std::string(128, '\0');
std::snprintf(str_precipitation.data(), str_precipitation.size(), "%.1f", precipitation);

result[kShownNames.weather_code] = kWeatherCodeToString.at(weather_type);
Expand Down
2 changes: 1 addition & 1 deletion lib/forecast/WeatherTimeUnit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class WeatherTimeUnit {
double uv_index{};
int32_t humidity{};

explicit WeatherTimeUnit(const std::string& name);
explicit WeatherTimeUnit(std::string name);

[[nodiscard]] std::map<std::string, std::string> GetAllAsMap() const;
[[nodiscard]] std::string GetName() const;
Expand Down
25 changes: 13 additions & 12 deletions lib/ui/ConfigParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "ConfigParser.hpp"

#include "lib/utils/utils.hpp"
#include "lib/forecast/Forecaster.hpp"

const std::string ConfigParser::kDefaultLocation = "First location in config";

Expand Down Expand Up @@ -32,9 +33,9 @@ bool ConfigParser::IsValidConfig(const std::string& str_config) {
return false;
}

json api_key_file = config["api_key_file"];
json locations = config["locations"];
json defaults = config["defaults"];
const json& api_key_file = config["api_key_file"];
const json& locations = config["locations"];
const json& defaults = config["defaults"];

if (!api_key_file.is_string() || !locations.is_array() || !defaults.is_object()) {
return false;
Expand All @@ -55,10 +56,10 @@ int32_t ConfigParser::ParseConfig() {
return 1;
}

json config = json::parse(str_config);
json api_key_file = config["api_key_file"];
json locations = config["locations"];
json defaults = config["defaults"];
const json config = json::parse(str_config);
const json& api_key_file = config["api_key_file"];
const json& locations = config["locations"];
const json& defaults = config["defaults"];

std::string api_key_path = config_dir_ + "/" + api_key_file.get<std::string>();

Expand All @@ -67,7 +68,7 @@ int32_t ConfigParser::ParseConfig() {
return 1;
}

int32_t result = SetApiKey(api_key_path);
const int32_t result = SetApiKey(api_key_path);

if (result != 0) {
DisplayError("Invalid API key!\n", error_output_);
Expand All @@ -86,7 +87,7 @@ int32_t ConfigParser::ParseConfig() {
days_count_ = defaults["days_count"].get<int32_t>();
}

size_t listed_locations_size = locations.size();
const size_t listed_locations_size = locations.size();
interval_ = std::clamp(interval_, kLowerLimitIntervalSize + 1, kUpperLimitIntervalSize - 1);
days_count_ = std::clamp(days_count_, Forecaster::kLowerLimitDaysCount + 1, Forecaster::kUpperLimitDaysCount - 1);
location_index_ = std::clamp(location_index_, 0, static_cast<int32_t>(listed_locations_size + locations_.size() - 1));
Expand All @@ -100,15 +101,15 @@ int32_t ConfigParser::ParseConfig() {

int32_t ConfigParser::SetApiKey(const std::string& api_key_path) {
std::ifstream api_key_file_(api_key_path);
std::string api_key = std::string((std::istreambuf_iterator<char>(api_key_file_)),
std::istreambuf_iterator<char>());
auto api_key = std::string((std::istreambuf_iterator<char>(api_key_file_)),
std::istreambuf_iterator<char>());

if (api_key.size() < 36) {
return 1;
}

api_key = api_key.substr(0, 36);
std::regex yandex_api_regex = std::regex(R"(^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$)");
const auto yandex_api_regex = std::regex(R"(^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$)");

if (!std::regex_match(api_key, yandex_api_regex)) {
return 1;
Expand Down
1 change: 0 additions & 1 deletion lib/ui/ConfigParser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include <vector>

#include "lib/utils/utils.hpp"
#include "lib/forecast/Forecaster.hpp"

#include <nlohmann/json.hpp>

Expand Down
Loading