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

refactor: remove annual log map #64

Merged
merged 1 commit into from
Jun 23, 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
40 changes: 21 additions & 19 deletions source/app.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,20 @@ class App : public InputHandlerBase {
std::shared_ptr<EditorBase> m_editor;
AnnualLogData m_data;

std::vector<const date::AnnualMap<bool> *> m_tagMaps;
std::vector<const date::AnnualMap<bool> *> m_sectionMaps;
std::vector<const utils::date::Dates *> m_tagMaps;
std::vector<const utils::date::Dates *> m_sectionMaps;
bool m_skipFirstLine;

std::optional<AsyncGitRepo> m_gitRepo;

void
updateViewSectionsAndTagsAfterLogChange(const std::chrono::year_month_day &dateOfChangedLog) {
m_data.collect(m_repo, dateOfChangedLog, m_skipFirstLine);
m_view->setTagMenuItems(makeMenuTitles(m_data.tagMap));
m_view->setSectionMenuItems(makeMenuTitles(m_data.sectionMap));
m_view->setTagMenuItems(makeMenuTitles(m_data.datesWithTag));
m_view->setSectionMenuItems(makeMenuTitles(m_data.datesWithSection));

updatePointersForHighlightMaps(m_tagMaps, m_data.tagMap);
updatePointersForHighlightMaps(m_sectionMaps, m_data.sectionMap);
updatePointersForHighlightMaps(m_tagMaps, m_data.datesWithTag);
updatePointersForHighlightMaps(m_sectionMaps, m_data.datesWithSection);
if (dateOfChangedLog == m_view->getFocusedDate()) {
if (auto log = m_repo->read(m_view->getFocusedDate())) {
m_view->setPreviewString(log->getContent());
Expand All @@ -96,7 +96,7 @@ class App : public InputHandlerBase {
m_data{AnnualLogData::collect(m_repo, m_displayedYear, skipFirstLine)},
m_skipFirstLine{skipFirstLine} {
m_view->setInputHandler(this);
m_view->setAvailableLogsMap(&m_data.logAvailabilityMap);
m_view->setDatesWithLogs(&m_data.datesWithLogs);
updateViewSectionsAndTagsAfterLogChange(m_view->getFocusedDate());

if (gitRepo) {
Expand Down Expand Up @@ -158,13 +158,13 @@ class App : public InputHandlerBase {
void handleFocusedTagChange(int newTag) {
m_view->selectedSection() = 0;
const auto *const highlighMap = m_tagMaps.at(newTag);
m_view->setHighlightedLogsMap(highlighMap);
m_view->setHighlightedDates(highlighMap);
}

void handleFocusedSectionChange(int newSection) {
m_view->selectedTag() = 0;
const auto *const highlighMap = m_sectionMaps.at(newSection);
m_view->setHighlightedLogsMap(highlighMap);
m_view->setHighlightedDates(highlighMap);
}

void handleUiStarted() {
Expand All @@ -191,7 +191,7 @@ class App : public InputHandlerBase {

void deleteFocusedLog() {
auto date = m_view->getFocusedDate();
if (m_data.logAvailabilityMap.get(date)) {
if (m_data.datesWithLogs.contains(date::monthDay(date))) {
m_view->prompt("Are you sure you want to delete a log file?", [date, this] {
m_repo->remove(date);
updateViewSectionsAndTagsAfterLogChange(date);
Expand All @@ -203,7 +203,7 @@ class App : public InputHandlerBase {
m_displayedYear = std::chrono::year{(int)m_displayedYear + diff};
m_data = AnnualLogData::collect(m_repo, m_displayedYear, m_skipFirstLine);
m_view->showCalendarForYear(m_displayedYear);
m_view->setHighlightedLogsMap(nullptr);
m_view->setHighlightedDates(nullptr);
updateViewSectionsAndTagsAfterLogChange(m_view->getFocusedDate());
}

Expand Down Expand Up @@ -234,8 +234,8 @@ class App : public InputHandlerBase {
return content == date::formatToString(date, kLogBaseTemplate) || content.empty();
}

static const date::AnnualMap<bool> *findOrNull(const date::StringYearMap &map,
const std::string &key) {
static const utils::date::Dates *
findOrNull(const std::map<std::string, utils::date::Dates> &map, const std::string &key) {
auto result = map.find(key);
if (result == map.end()) {
return nullptr;
Expand All @@ -249,13 +249,14 @@ class App : public InputHandlerBase {
* map for said menu item is appropriate. Alternative would getting the string and looking up
* in the map.
*/
static void updatePointersForHighlightMaps(std::vector<const date::AnnualMap<bool> *> &vec,
const date::StringYearMap &map) {
static void
updatePointersForHighlightMaps(std::vector<const utils::date::Dates *> &vec,
const std::map<std::string, utils::date::Dates> &map) {
vec.clear();
vec.reserve(map.size());
vec.push_back(nullptr); // 0 index = no highlighted days
for (auto const &[_, annual_map] : map) {
vec.emplace_back(&annual_map);
for (auto const &[_, dates] : map) {
vec.emplace_back(&dates);
}
}

Expand All @@ -264,12 +265,13 @@ class App : public InputHandlerBase {
* title will be made of a key in the @p map and the number of days it has been
* mantioned (days set).
*/
static std::vector<std::string> makeMenuTitles(const date::StringYearMap &map) {
static std::vector<std::string>
makeMenuTitles(const std::map<std::string, utils::date::Dates> &map) {
std::vector<std::string> strs;
strs.reserve(map.size());
strs.push_back(" ----- ");
for (auto const &[str, annual_map] : map) {
strs.push_back(std::string{"("} + std::to_string(annual_map.daysSet()) + ") - " + str);
strs.push_back(std::string{"("} + std::to_string(annual_map.size()) + ") - " + str);
}
return std::move(strs);
}
Expand Down
27 changes: 15 additions & 12 deletions source/log/annual_log_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,30 @@

namespace caps_log::log {

using utils::date::monthDay;

namespace {
void collectEmpty(AnnualLogData &data, const std::shared_ptr<LogRepositoryBase> &repo,
std::chrono::year_month_day date, bool skipFirstLine) {
const auto input = repo->read(date);

// if there is no log to be processed, return
if (not input) {
data.logAvailabilityMap.set(date, false);
data.datesWithLogs.erase(monthDay(date));
return;
}

data.logAvailabilityMap.set(date, true);
data.datesWithLogs.insert(monthDay(date));

// then parse and set mentions again
const auto parsedSections = input->readSectionTitles(skipFirstLine);
const auto parsedTags = input->readTagTitles();

for (const auto &tag : parsedTags) {
data.tagMap[tag].set(date, true);
data.datesWithTag[tag].insert(monthDay(date));
}
for (const auto &section : parsedSections) {
data.sectionMap[section].set(date, true);
data.datesWithSection[section].insert(monthDay(date));
}
}

Expand Down Expand Up @@ -52,16 +54,17 @@ AnnualLogData AnnualLogData::collect(const std::shared_ptr<LogRepositoryBase> &r
void AnnualLogData::collect(const std::shared_ptr<LogRepositoryBase> &repo,
const std::chrono::year_month_day &date, bool skipFirstLine) {
// remove all information in maps for this date first
std::erase_if(tagMap, [date](auto &item) {
auto &[_, annual_map] = item;
annual_map.set(date, false);
return not annual_map.hasAnyDaySet();
const auto monthDayDate = monthDay(date);
std::erase_if(datesWithTag, [monthDayDate](auto &item) {
auto &[_, dates] = item;
dates.erase(monthDayDate);
return dates.size() == 0;
});

std::erase_if(sectionMap, [date](auto &item) {
auto &[_, annual_map] = item;
annual_map.set(date, false);
return not annual_map.hasAnyDaySet();
std::erase_if(datesWithSection, [monthDayDate](auto &item) {
auto &[_, dates] = item;
dates.erase(monthDayDate);
return dates.size() == 0;
});

// collect as if it was empty
Expand Down
9 changes: 6 additions & 3 deletions source/log/annual_log_data.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

#include "log_repository_base.hpp"
#include "utils/date.hpp"
#include <chrono>
#include <memory>
#include <set>
#include <string>

namespace caps_log::log {

Expand All @@ -13,9 +16,9 @@ namespace caps_log::log {
*/
class AnnualLogData {
public:
utils::date::AnnualMap<bool> logAvailabilityMap;
utils::date::StringYearMap sectionMap;
utils::date::StringYearMap tagMap;
utils::date::Dates datesWithLogs;
std::map<std::string, utils::date::Dates> datesWithSection;
std::map<std::string, utils::date::Dates> datesWithTag;

/**
* Constructs YearOverviewData from logs in a given year.
Expand Down
48 changes: 6 additions & 42 deletions source/utils/date.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <chrono>
#include <iomanip>
#include <map>
#include <set>
#include <sstream>
#include <string>

Expand All @@ -28,6 +29,10 @@ inline std::tm dateToTm(const std::chrono::year_month_day &date) {

} // namespace detail

inline std::chrono::month_day monthDay(std::chrono::year_month_day date) {
return std::chrono::month_day{date.month(), date.day()};
}

inline std::string formatToString(const std::chrono::year_month_day &date,
const std::string &format = "%d. %m. %y.") {
std::ostringstream oss;
Expand Down Expand Up @@ -85,47 +90,6 @@ inline std::string getStringNameForMonth(std::chrono::month month) {
return kMonthNames.at(index);
}

/**
* A type of "map" container that maps dates in one year to T
**/
template <typename T> class AnnualMap {
static constexpr auto kDaysInMonth = 31;
static constexpr auto kMonthsInYear = 12;
std::array<std::array<T, kDaysInMonth>, kMonthsInYear> m_map{};

public:
T &get(const std::chrono::year_month_day &date) {
return m_map[static_cast<unsigned>(date.month()) - 1]
[static_cast<unsigned>(date.day()) - 1];
}
const T &get(const std::chrono::year_month_day &date) const {
return m_map[static_cast<unsigned>(date.month()) - 1]
[static_cast<unsigned>(date.day()) - 1];
}
T &get(unsigned day, unsigned month) { return m_map[month - 1][day - 1]; }
const T &get(unsigned day, unsigned month) const { return m_map[month - 1][day - 1]; }

void set(const std::chrono::year_month_day &date, const T &value) {
m_map[static_cast<unsigned>(date.month()) - 1][static_cast<unsigned>(date.day()) - 1] =
value;
}
T &set(unsigned day, unsigned month, T &val) { return m_map[month - 1][day - 1] = val; }

inline unsigned daysSet() const {
unsigned result = 0;
for (const auto &month : m_map) {
result += std::count(month.begin(), month.end(), true);
}
return result;
}

inline bool hasAnyDaySet() const {
return std::ranges::any_of(m_map, [](const auto &month) {
return std::ranges::any_of(month, [](const auto &day) { return day; });
});
}
};

using StringYearMap = std::map<std::string, AnnualMap<bool>>;
using Dates = std::set<std::chrono::month_day>;

} // namespace caps_log::utils::date
10 changes: 6 additions & 4 deletions source/view/annual_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "ftxui/component/screen_interactive.hpp"
#include "ftxui/dom/elements.hpp"
#include "ftxui_ext/extended_containers.hpp"
#include "utils/date.hpp"
#include "view/input_handler.hpp"
#include "view/windowed_menu.hpp"

Expand Down Expand Up @@ -49,7 +50,7 @@ std::shared_ptr<Promptable> AnnualView::makeFullUIComponent() {
utils::date::formatToString(utils::date::getToday(), "%d. %m. %Y.");
const auto titleText =
fmt::format("Today is: {} -- There are {} log entries for year {}.", dateStr,
m_availabeLogsMap != nullptr ? m_availabeLogsMap->daysSet() : 0,
m_datesWithLogs != nullptr ? m_datesWithLogs->size() : 0,
static_cast<int>(m_calendarButtons->getFocusedDate().year()));
const auto mainSection =
hbox(m_tagsMenu->Render(), m_sectionsMenu->Render(), m_calendarButtons->Render());
Expand Down Expand Up @@ -85,7 +86,8 @@ CalendarOption AnnualView::makeCalendarOptions(const std::chrono::year_month_day

if (today == date) {
element = element | color(Color::Red);
} else if (m_highlightedLogsMap && m_highlightedLogsMap->get(date)) {
} else if (m_highlightedDates &&
m_highlightedDates->contains(utils::date::monthDay(date))) {
element = element | color(Color::Yellow);
} else if (utils::date::isWeekend(date)) {
element = element | color(Color::Blue);
Expand All @@ -95,8 +97,8 @@ CalendarOption AnnualView::makeCalendarOptions(const std::chrono::year_month_day
element = element | inverted;
}

if (m_availabeLogsMap) {
if (m_availabeLogsMap->get(date)) {
if (m_datesWithLogs) {
if (m_datesWithLogs->contains(utils::date::monthDay(date))) {
element = element | underlined;
} else {
element = element | dim;
Expand Down
12 changes: 4 additions & 8 deletions source/view/annual_view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ class AnnualView : public AnnualViewBase {
std::shared_ptr<Promptable> m_rootComponent;

// Maps that help m_calendarButtons highlight certain logs.
const utils::date::AnnualMap<bool> *m_highlightedLogsMap = nullptr;
const utils::date::AnnualMap<bool> *m_availabeLogsMap = nullptr;
const utils::date::Dates *m_highlightedDates = nullptr;
const utils::date::Dates *m_datesWithLogs = nullptr;

// Menu items for m_tagsMenu & m_sectionsMenu
std::vector<std::string> m_tagMenuItems, m_sectionMenuItems;
Expand All @@ -54,12 +54,8 @@ class AnnualView : public AnnualViewBase {

void setInputHandler(InputHandlerBase *handler) override { m_handler = handler; }

void setAvailableLogsMap(const utils::date::AnnualMap<bool> *map) override {
m_availabeLogsMap = map;
}
void setHighlightedLogsMap(const utils::date::AnnualMap<bool> *map) override {
m_highlightedLogsMap = map;
}
void setDatesWithLogs(const utils::date::Dates *map) override { m_datesWithLogs = map; }
void setHighlightedDates(const utils::date::Dates *map) override { m_highlightedDates = map; }

void setTagMenuItems(std::vector<std::string> items) override {
m_tagMenuItems = std::move(items);
Expand Down
5 changes: 3 additions & 2 deletions source/view/annual_view_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ class AnnualViewBase { // NOLINT

// passing only a pointer and having a view have no ownership of
// the map allows for having precoputed maps and switching
virtual void setAvailableLogsMap(const utils::date::AnnualMap<bool> *map) = 0;
virtual void setHighlightedLogsMap(const utils::date::AnnualMap<bool> *map) = 0;
virtual void setDatesWithLogs(const utils::date::Dates *map) = 0;
virtual void setHighlightedDates(const utils::date::Dates *map) = 0;

// can't use a pointer here because some FTXUI menu limitations
virtual void setTagMenuItems(std::vector<std::string> items) = 0;
virtual void setSectionMenuItems(std::vector<std::string> items) = 0;
Expand Down
30 changes: 18 additions & 12 deletions test/annual_log_data_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,36 @@
#include "log/log_file.hpp"

#include "mocks.hpp"
#include "utils/date.hpp"

namespace caps_log::log::test {

TEST(YearOverviewDataTest, Collect) {
const auto dummyDate = std::chrono::year_month_day{std::chrono::year{2020},
std::chrono::month{2}, std::chrono::day{3}};
auto dummyRepo = std::make_shared<DummyRepository>();
dummyRepo->write({dummyDate, "\n# dummy section\n * dummy tag"});
auto data = caps_log::log::AnnualLogData::collect(dummyRepo, dummyDate.year());
auto data = AnnualLogData::collect(dummyRepo, dummyDate.year());

// inital collection
EXPECT_EQ(data.tagMap.size(), 1);
EXPECT_EQ(data.sectionMap.size(), 1);
EXPECT_EQ(data.logAvailabilityMap.daysSet(), 1);
EXPECT_EQ(data.logAvailabilityMap.get(dummyDate), true);
EXPECT_EQ(data.datesWithTag.size(), 1);
EXPECT_EQ(data.datesWithSection.size(), 1);
EXPECT_EQ(data.datesWithLogs.size(), 1);
EXPECT_EQ(data.datesWithLogs.contains(utils::date::monthDay(dummyDate)), true);

EXPECT_EQ(data.tagMap["dummy tag"].daysSet(), 1);
EXPECT_EQ(data.sectionMap["dummy section"].daysSet(), 1);
EXPECT_EQ(data.tagMap["dummy tag"].get(dummyDate), true);
EXPECT_EQ(data.sectionMap["dummy section"].get(dummyDate), true);
EXPECT_EQ(data.datesWithTag["dummy tag"].size(), 1);
EXPECT_EQ(data.datesWithSection["dummy section"].size(), 1);
EXPECT_EQ(data.datesWithTag["dummy tag"].contains(utils::date::monthDay(dummyDate)), true);
EXPECT_EQ(data.datesWithSection["dummy section"].contains(utils::date::monthDay(dummyDate)),
true);

// collection after removal of a log entry
dummyRepo->remove(dummyDate);
data.collect(dummyRepo, dummyDate);

EXPECT_EQ(data.tagMap.size(), 0);
EXPECT_EQ(data.sectionMap.size(), 0);
EXPECT_EQ(data.logAvailabilityMap.daysSet(), 0);
EXPECT_EQ(data.datesWithTag.size(), 0);
EXPECT_EQ(data.datesWithSection.size(), 0);
EXPECT_EQ(data.datesWithLogs.size(), 0);
}

} // namespace caps_log::log::test
Loading
Loading