~aleteoryx/muditaos

4b06b003bc7c20904159d4939ff84e96e34104f2 — tomaszrogala 5 years ago 4cc196c
[EGD-3842] Modify events records tests and add providers fields to DB (#1138)

M changelog.md => changelog.md +2 -0
@@ 34,6 34,8 @@
* Add vertical text scrolling.
* Add text cursor starting position handling.
* Add logs dumping to a file on the RT1051 platform.
* `[calendar]` Added calendar events endpoints handling.
* `[calendar]` Added parser ics.

### Changed


M module-apps/application-calendar/models/AllEventsModel.cpp => module-apps/application-calendar/models/AllEventsModel.cpp +2 -2
@@ 67,8 67,8 @@ auto AllEventsModel::handleQueryResponse(db::QueryResult *queryResult) -> bool
    auto response = dynamic_cast<db::query::events::GetAllLimitedResult *>(queryResult);
    assert(response != nullptr);

    auto records = *response->getResult();
    list->setElementsCount(*response->getCountResult());
    auto records = response->getResult();
    list->setElementsCount(response->getCountResult());

    auto app = dynamic_cast<app::ApplicationCalendar *>(application);
    assert(application != nullptr);

M module-apps/application-calendar/models/DayEventsModel.cpp => module-apps/application-calendar/models/DayEventsModel.cpp +1 -1
@@ 69,7 69,7 @@ auto DayEventsModel::handleQueryResponse(db::QueryResult *queryResult) -> bool
            return false;
        }

        auto records = *response->getResult();
        auto records = response->getResult();
        if (auto app = dynamic_cast<app::ApplicationCalendar *>(application); app != nullptr) {
            if (response->getCountResult() == 0) {
                LOG_DEBUG("Empty records");

M module-apps/application-calendar/windows/CalendarMainWindow.cpp => module-apps/application-calendar/windows/CalendarMainWindow.cpp +2 -2
@@ 212,7 212,7 @@ namespace gui
        std::fill(std::begin(isDayEmpty), std::end(isDayEmpty), true);
        if (auto response = dynamic_cast<db::query::events::GetFilteredResult *>(queryResult)) {
            const auto records = response->getResult();
            for (auto &rec : *records) {
            for (auto &rec : records) {
                date::year_month_day recordDate = TimePointToYearMonthDay(rec.date_from);
                uint32_t dayNumb                = static_cast<unsigned>(recordDate.day());
                isDayEmpty[dayNumb - 1]         = false;


@@ 224,7 224,7 @@ namespace gui
            const auto records = response->getResult();
            auto day           = monthBox->getFocusItemIndex() + 1;
            auto filter = TimePointFromYearMonthDay(monthModel->getYear() / monthModel->getMonth() / date::day(day));
            if (!records->empty()) {
            if (!records.empty()) {
                auto data = std::make_unique<DayMonthData>();
                data->setData("", filter);
                application->switchWindow(style::window::calendar::name::all_events_window, std::move(data));

M module-db/Interface/EventsRecord.cpp => module-db/Interface/EventsRecord.cpp +58 -45
@@ 19,8 19,9 @@

EventsRecord::EventsRecord(const EventsTableRow &tableRow)
    : Record{tableRow.ID}, UID{tableRow.UID}, title{tableRow.title}, date_from{tableRow.date_from},
      date_till{tableRow.date_till}, reminder{tableRow.reminder}, repeat{tableRow.repeat}, reminder_fired{
                                                                                               tableRow.reminder_fired}
      date_till{tableRow.date_till}, reminder{tableRow.reminder}, repeat{tableRow.repeat},
      reminder_fired{tableRow.reminder_fired}, provider_type{tableRow.provider_type}, provider_id{tableRow.provider_id},
      provider_iCalUid{tableRow.provider_iCalUid}
{}

EventsRecordInterface::EventsRecordInterface(EventsDB *eventsDb) : eventsDb(eventsDb)


@@ 28,14 29,17 @@ EventsRecordInterface::EventsRecordInterface(EventsDB *eventsDb) : eventsDb(even

bool EventsRecordInterface::Add(const EventsRecord &rec)
{
    auto entry = EventsTableRow{{.ID = rec.ID},
                                .UID            = rec.UID,
                                .title          = rec.title,
                                .date_from      = rec.date_from,
                                .date_till      = rec.date_till,
                                .reminder       = rec.reminder,
                                .repeat         = rec.repeat,
                                .reminder_fired = rec.reminder_fired};
    auto entry = EventsTableRow{{rec.ID},
                                .UID              = rec.UID,
                                .title            = rec.title,
                                .date_from        = rec.date_from,
                                .date_till        = rec.date_till,
                                .reminder         = rec.reminder,
                                .repeat           = rec.repeat,
                                .reminder_fired   = rec.reminder_fired,
                                .provider_type    = rec.provider_type,
                                .provider_id      = rec.provider_id,
                                .provider_iCalUid = rec.provider_iCalUid};

    Repeat repeatOption = Repeat(rec.repeat);
    if (repeatOption > Repeat::yearly) {


@@ 66,17 70,17 @@ bool EventsRecordInterface::Add(const EventsRecord &rec)
    return false;
}

std::unique_ptr<std::vector<EventsRecord>> EventsRecordInterface::Select(TimePoint filter_from,
                                                                         TimePoint filter_till,
                                                                         uint32_t offset,
                                                                         uint32_t limit)
std::vector<EventsRecord> EventsRecordInterface::Select(TimePoint filter_from,
                                                        TimePoint filter_till,
                                                        uint32_t offset,
                                                        uint32_t limit)
{
    auto rows = eventsDb->events.selectByDatePeriod(filter_from, filter_till, offset, limit);

    auto records = std::make_unique<std::vector<EventsRecord>>();
    auto records = std::vector<EventsRecord>();

    for (auto &r : rows) {
        records->push_back(r);
        records.push_back(r);
    }

    return records;


@@ 105,11 109,10 @@ std::unique_ptr<std::vector<EventsRecord>> EventsRecordInterface::GetLimitOffset
    for (auto &r : rows) {
        records->push_back(r);
    }

    return records;
}

std::unique_ptr<std::vector<EventsRecord>> EventsRecordInterface::GetLimitOffsetByDate(uint32_t offset, uint32_t limit)
std::vector<EventsRecord> EventsRecordInterface::GetLimitOffsetByDate(uint32_t offset, uint32_t limit)
{
    if (limit == 0) {
        limit = GetCount();


@@ 117,10 120,10 @@ std::unique_ptr<std::vector<EventsRecord>> EventsRecordInterface::GetLimitOffset

    auto rows = eventsDb->events.getLimitOffsetByDate(offset, limit);

    auto records = std::make_unique<std::vector<EventsRecord>>();
    auto records = std::vector<EventsRecord>();

    for (auto &r : rows) {
        records->push_back(r);
        records.push_back(r);
    }

    return records;


@@ 134,14 137,17 @@ bool EventsRecordInterface::Update(const EventsRecord &rec)
        return false;
    }

    auto entry = EventsTableRow{{.ID = rec.ID},
                                .UID            = rec.UID,
                                .title          = rec.title,
                                .date_from      = rec.date_from,
                                .date_till      = rec.date_till,
                                .reminder       = rec.reminder,
                                .repeat         = rec.repeat,
                                .reminder_fired = rec.reminder_fired};
    auto entry = EventsTableRow{{rec.ID},
                                .UID              = rec.UID,
                                .title            = rec.title,
                                .date_from        = rec.date_from,
                                .date_till        = rec.date_till,
                                .reminder         = rec.reminder,
                                .repeat           = rec.repeat,
                                .reminder_fired   = rec.reminder_fired,
                                .provider_type    = rec.provider_type,
                                .provider_id      = rec.provider_id,
                                .provider_iCalUid = rec.provider_iCalUid};

    bool result = eventsDb->events.update(entry);



@@ 182,14 188,17 @@ bool EventsRecordInterface::UpdateByUID(const EventsRecord &rec)
        return false;
    }

    auto entry = EventsTableRow{{.ID = rec.ID},
                                .UID            = rec.UID,
                                .title          = rec.title,
                                .date_from      = rec.date_from,
                                .date_till      = rec.date_till,
                                .reminder       = rec.reminder,
                                .repeat         = rec.repeat,
                                .reminder_fired = rec.reminder_fired};
    auto entry = EventsTableRow{{rec.ID},
                                .UID              = rec.UID,
                                .title            = rec.title,
                                .date_from        = rec.date_from,
                                .date_till        = rec.date_till,
                                .reminder         = rec.reminder,
                                .repeat           = rec.repeat,
                                .reminder_fired   = rec.reminder_fired,
                                .provider_type    = rec.provider_type,
                                .provider_id      = rec.provider_id,
                                .provider_iCalUid = rec.provider_iCalUid};

    bool result = eventsDb->events.updateByUID(entry);



@@ 241,7 250,13 @@ bool EventsRecordInterface::RemoveByField(EventsRecordField field, const char *s

EventsRecord EventsRecordInterface::GetByID(uint32_t id)
{
    EventsRecord event = static_cast<EventsRecord>(eventsDb->events.getById(id));
    EventsRecord event{eventsDb->events.getById(id)};
    return event;
}

EventsRecord EventsRecordInterface::GetByUID(const std::string &uid)
{
    EventsRecord event{eventsDb->events.getByUID(uid)};
    return event;
}



@@ 255,14 270,13 @@ uint32_t EventsRecordInterface::GetCountFiltered(TimePoint from, TimePoint till)
    return eventsDb->events.countFromFilter(from, till);
}

std::unique_ptr<std::vector<EventsRecord>> EventsRecordInterface::SelectFirstUpcoming(TimePoint filter_from,
                                                                                      TimePoint filter_till)
std::vector<EventsRecord> EventsRecordInterface::SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till)
{
    auto rows = eventsDb->events.SelectFirstUpcoming(filter_from, filter_till);

    auto records = std::make_unique<std::vector<EventsRecord>>();
    auto records = std::vector<EventsRecord>();
    for (auto &r : rows) {
        records->push_back(r);
        records.push_back(r);
    }

    return records;


@@ 318,7 332,7 @@ std::unique_ptr<db::query::events::GetAllResult> EventsRecordInterface::runQuery
{
    auto numberOfEvents = GetCount();
    auto records        = GetLimitOffset(0, numberOfEvents);
    auto response       = std::make_unique<db::query::events::GetAllResult>(std::move(records));
    auto response       = std::make_unique<db::query::events::GetAllResult>(*records);
    response->setRequestQuery(query);
    return response;
}


@@ 330,8 344,7 @@ std::unique_ptr<db::query::events::GetAllLimitedResult> EventsRecordInterface::r
    auto getAllLimitedQuery = static_cast<db::query::events::GetAllLimited *>(query.get());
    auto records            = GetLimitOffsetByDate(getAllLimitedQuery->offset, getAllLimitedQuery->limit);
    auto count              = GetCount();
    auto response =
        std::make_unique<db::query::events::GetAllLimitedResult>(std::move(records), std::make_unique<uint32_t>(count));
    auto response           = std::make_unique<db::query::events::GetAllLimitedResult>(std::move(records), count);
    response->setRequestQuery(query);
    return response;
}


@@ 404,7 417,7 @@ std::unique_ptr<db::query::events::SelectFirstUpcomingResult> EventsRecordInterf
    auto getFirstUpcomingQuery = static_cast<db::query::events::SelectFirstUpcoming *>(query.get());

    auto records  = SelectFirstUpcoming(getFirstUpcomingQuery->filter_from, getFirstUpcomingQuery->filter_till);
    auto response = std::make_unique<db::query::events::SelectFirstUpcomingResult>(std::move(records));
    auto response = std::make_unique<db::query::events::SelectFirstUpcomingResult>(records);
    response->setRequestQuery(query);
    return response;
}

M module-db/Interface/EventsRecord.hpp => module-db/Interface/EventsRecord.hpp +7 -6
@@ 46,6 46,9 @@ struct EventsRecord : public Record
    uint32_t reminder = 0;
    uint32_t repeat   = 0;
    TimePoint reminder_fired;
    std::string provider_type;
    std::string provider_id;
    std::string provider_iCalUid;

    EventsRecord()  = default;
    ~EventsRecord() = default;


@@ 72,19 75,17 @@ class EventsRecordInterface : public RecordInterface<EventsRecord, EventsRecordF
    bool Update(const EventsRecord &rec) override final;
    bool UpdateByUID(const EventsRecord &rec);
    EventsRecord GetByID(uint32_t id) override final;
    EventsRecord GetByUID(const std::string &uid);
    uint32_t GetCount() override final;
    uint32_t GetCountFiltered(TimePoint from, TimePoint till);
    std::unique_ptr<std::vector<EventsRecord>> Select(TimePoint filter_from,
                                                      TimePoint filter_till,
                                                      uint32_t offset,
                                                      uint32_t limit);
    std::vector<EventsRecord> Select(TimePoint filter_from, TimePoint filter_till, uint32_t offset, uint32_t limit);
    std::unique_ptr<std::vector<EventsRecord>> GetLimitOffset(uint32_t offset, uint32_t limit) override final;
    std::unique_ptr<std::vector<EventsRecord>> GetLimitOffsetByField(uint32_t offset,
                                                                     uint32_t limit,
                                                                     EventsRecordField field,
                                                                     const char *str) override final;
    std::unique_ptr<std::vector<EventsRecord>> GetLimitOffsetByDate(uint32_t offset, uint32_t limit);
    std::unique_ptr<std::vector<EventsRecord>> SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till);
    std::vector<EventsRecord> GetLimitOffsetByDate(uint32_t offset, uint32_t limit);
    std::vector<EventsRecord> SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till);

    std::unique_ptr<db::QueryResult> runQuery(std::shared_ptr<db::Query> query) override;


M module-db/Tables/EventsTable.cpp => module-db/Tables/EventsTable.cpp +487 -334
@@ 29,323 29,440 @@ bool EventsTable::add(EventsTableRow entry)
    if (entry.UID.empty()) {
        entry.UID = createUID();
    }
    return db->execute(
        "INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, reminder_fired) "
        "SELECT '%q','%q', '%q','%q', %lu, %lu, '%q' "
        "WHERE NOT EXISTS "
        "(SELECT 1 FROM events e "
        "WHERE e.title='%q' "
        "AND e.date_from='%q' "
        "AND e.date_till='%q' "
        "AND e.reminder=%lu "
        "AND e.repeat=%lu );",
        entry.UID.c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from).c_str(),
        TimePointToString(entry.date_till).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from).c_str(),
        TimePointToString(entry.date_till).c_str(),
        entry.reminder,
        entry.repeat);
    return db->execute("INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, "
                       "reminder_fired, provider_type, provider_id, provider_iCalUid) "
                       "SELECT '%q','%q', '%q','%q', %lu, %lu, '%q','%q', '%q','%q'"
                       "WHERE NOT EXISTS "
                       "(SELECT 1 FROM events e "
                       "WHERE e.title='%q' "
                       "AND e.date_from='%q' "
                       "AND e.date_till='%q' "
                       "AND e.reminder=%lu "
                       "AND e.repeat=%lu );",
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from).c_str(),
                       TimePointToString(entry.date_till).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from).c_str(),
                       TimePointToString(entry.date_till).c_str(),
                       entry.reminder,
                       entry.repeat);
}

bool EventsTable::addDaily(EventsTableRow entry)
{
    return db->execute(
        "INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, reminder_fired) VALUES"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q');",
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from).c_str(),
        TimePointToString(entry.date_till).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from + date::days{1}).c_str(),
        TimePointToString(entry.date_till + date::days{1}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from + date::days{2}).c_str(),
        TimePointToString(entry.date_till + date::days{2}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from + date::days{3}).c_str(),
        TimePointToString(entry.date_till + date::days{3}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from + date::days{4}).c_str(),
        TimePointToString(entry.date_till + date::days{4}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from + date::days{5}).c_str(),
        TimePointToString(entry.date_till + date::days{5}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from + date::days{6}).c_str(),
        TimePointToString(entry.date_till + date::days{6}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str());
    if (entry.UID.empty()) {
        entry.UID = createUID();
    }
    return db->execute("INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, "
                       "reminder_fired, provider_type, provider_id, provider_iCalUid) VALUES"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q');",
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from).c_str(),
                       TimePointToString(entry.date_till).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{1}).c_str(),
                       TimePointToString(entry.date_till + date::days{1}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{2}).c_str(),
                       TimePointToString(entry.date_till + date::days{2}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{3}).c_str(),
                       TimePointToString(entry.date_till + date::days{3}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{4}).c_str(),
                       TimePointToString(entry.date_till + date::days{4}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{5}).c_str(),
                       TimePointToString(entry.date_till + date::days{5}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{6}).c_str(),
                       TimePointToString(entry.date_till + date::days{6}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str());
}

bool EventsTable::addWeekly(EventsTableRow entry)
{
    return db->execute(
        "INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, reminder_fired) VALUES"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q');",
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from).c_str(),
        TimePointToString(entry.date_till).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from + date::days{7}).c_str(),
        TimePointToString(entry.date_till + date::days{7}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from + date::days{14}).c_str(),
        TimePointToString(entry.date_till + date::days{14}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from + date::days{21}).c_str(),
        TimePointToString(entry.date_till + date::days{21}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str());
    if (entry.UID.empty()) {
        entry.UID = createUID();
    }
    return db->execute("INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, "
                       "reminder_fired, provider_type, provider_id, provider_iCalUid) VALUES"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q');",
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from).c_str(),
                       TimePointToString(entry.date_till).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{7}).c_str(),
                       TimePointToString(entry.date_till + date::days{7}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{14}).c_str(),
                       TimePointToString(entry.date_till + date::days{14}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{21}).c_str(),
                       TimePointToString(entry.date_till + date::days{21}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str());
}

bool EventsTable::addTwoWeeks(EventsTableRow entry)
{
    return db->execute(
        "INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, reminder_fired) VALUES"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q');",
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from).c_str(),
        TimePointToString(entry.date_till).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from + date::days{14}).c_str(),
        TimePointToString(entry.date_till + date::days{14}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from + date::days{28}).c_str(),
        TimePointToString(entry.date_till + date::days{28}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from + date::days{42}).c_str(),
        TimePointToString(entry.date_till + date::days{42}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str());
    if (entry.UID.empty()) {
        entry.UID = createUID();
    }
    return db->execute("INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, "
                       "reminder_fired, provider_type, provider_id, provider_iCalUid) VALUES"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q');",
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from).c_str(),
                       TimePointToString(entry.date_till).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{14}).c_str(),
                       TimePointToString(entry.date_till + date::days{14}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{28}).c_str(),
                       TimePointToString(entry.date_till + date::days{28}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{42}).c_str(),
                       TimePointToString(entry.date_till + date::days{42}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str());
}

bool EventsTable::addMonth(EventsTableRow entry)
{
    return db->execute(
        "INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, reminder_fired) VALUES"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q');",
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from).c_str(),
        TimePointToString(entry.date_till).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from, date::months{1}).c_str(),
        TimePointToString(entry.date_till, date::months{1}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from, date::months{2}).c_str(),
        TimePointToString(entry.date_till, date::months{2}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from, date::months{3}).c_str(),
        TimePointToString(entry.date_till, date::months{3}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from, date::months{4}).c_str(),
        TimePointToString(entry.date_till, date::months{4}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from, date::months{5}).c_str(),
        TimePointToString(entry.date_till, date::months{5}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from, date::months{6}).c_str(),
        TimePointToString(entry.date_till, date::months{6}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from, date::months{7}).c_str(),
        TimePointToString(entry.date_till, date::months{7}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from, date::months{8}).c_str(),
        TimePointToString(entry.date_till, date::months{8}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from, date::months{9}).c_str(),
        TimePointToString(entry.date_till, date::months{9}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from, date::months{10}).c_str(),
        TimePointToString(entry.date_till, date::months{10}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from, date::months{11}).c_str(),
        TimePointToString(entry.date_till, date::months{11}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from, date::months{12}).c_str(),
        TimePointToString(entry.date_till, date::months{12}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str());
    if (entry.UID.empty()) {
        entry.UID = createUID();
    }
    return db->execute("INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, "
                       "reminder_fired, provider_type, provider_id, provider_iCalUid) VALUES"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q');",
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from).c_str(),
                       TimePointToString(entry.date_till).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{1}).c_str(),
                       TimePointToString(entry.date_till, date::months{1}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{2}).c_str(),
                       TimePointToString(entry.date_till, date::months{2}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{3}).c_str(),
                       TimePointToString(entry.date_till, date::months{3}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{4}).c_str(),
                       TimePointToString(entry.date_till, date::months{4}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{5}).c_str(),
                       TimePointToString(entry.date_till, date::months{5}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{6}).c_str(),
                       TimePointToString(entry.date_till, date::months{6}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{7}).c_str(),
                       TimePointToString(entry.date_till, date::months{7}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{8}).c_str(),
                       TimePointToString(entry.date_till, date::months{8}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{9}).c_str(),
                       TimePointToString(entry.date_till, date::months{9}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{10}).c_str(),
                       TimePointToString(entry.date_till, date::months{10}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{11}).c_str(),
                       TimePointToString(entry.date_till, date::months{11}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{12}).c_str(),
                       TimePointToString(entry.date_till, date::months{12}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str());
}

bool EventsTable::addYear(EventsTableRow entry)
{
    return db->execute(
        "INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, reminder_fired) VALUES"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q'),"
        "('%q','%q', '%q','%q', %u, %u,'%q');",
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from).c_str(),
        TimePointToString(entry.date_till).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from + date::years{1}).c_str(),
        TimePointToString(entry.date_till + date::years{1}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from + date::years{2}).c_str(),
        TimePointToString(entry.date_till + date::years{2}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from + date::years{3}).c_str(),
        TimePointToString(entry.date_till + date::years{3}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str(),
        createUID().c_str(),
        entry.title.c_str(),
        TimePointToString(entry.date_from + date::years{4}).c_str(),
        TimePointToString(entry.date_till + date::years{4}).c_str(),
        entry.reminder,
        entry.repeat,
        TimePointToString(entry.reminder_fired).c_str());
    if (entry.UID.empty()) {
        entry.UID = createUID();
    }
    return db->execute("INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, "
                       "reminder_fired, provider_type, provider_id, provider_iCalUid) VALUES"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q');",
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from).c_str(),
                       TimePointToString(entry.date_till).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::years{1}).c_str(),
                       TimePointToString(entry.date_till + date::years{1}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::years{2}).c_str(),
                       TimePointToString(entry.date_till + date::years{2}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::years{3}).c_str(),
                       TimePointToString(entry.date_till + date::years{3}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::years{4}).c_str(),
                       TimePointToString(entry.date_till + date::years{4}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str());
}

std::vector<bool> parseOptions(const uint32_t &dataDB)
std::vector<bool> EventsTable::parseOptions(uint32_t repeatOptionValue)
{
    const uint32_t startBit        = 16;
    const uint32_t numberOfOptions = 7;


@@ 354,7 471,7 @@ std::vector<bool> parseOptions(const uint32_t &dataDB)
        weekDayOptions.push_back(false);
    }
    for (uint32_t i = startBit; i < startBit + numberOfOptions; i++) {
        if (dataDB & (1 << i)) {
        if (repeatOptionValue & (1 << i)) {
            LOG_DEBUG("Set option array %d", static_cast<int>(i));
            weekDayOptions[i - startBit] = true;
        }


@@ 372,35 489,40 @@ bool EventsTable::addCustom(EventsTableRow entry)
    weekDayOptions          = parseOptions(entry.repeat);
    uint32_t incrementation = 0;

    result =
        result &&
        db->execute(
            "INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, reminder_fired) VALUES"
            "('%q','%q', '%q','%q', %u, %u, '%q');",
            createUID().c_str(),
            entry.title.c_str(),
            TimePointToString(entry.date_from).c_str(),
            TimePointToString(entry.date_till).c_str(),
            entry.reminder,
            entry.repeat,
            TimePointToString(entry.reminder_fired).c_str());
    result = result && db->execute("INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, "
                                   "reminder_fired, provider_type, provider_id, provider_iCalUid) VALUES"
                                   "('%q','%q', '%q','%q', %u, %u, '%q','%q', '%q','%q');",
                                   entry.UID.c_str(),
                                   entry.title.c_str(),
                                   TimePointToString(entry.date_from).c_str(),
                                   TimePointToString(entry.date_till).c_str(),
                                   entry.reminder,
                                   entry.repeat,
                                   TimePointToString(entry.reminder_fired).c_str(),
                                   entry.provider_type.c_str(),
                                   entry.provider_id.c_str(),
                                   entry.provider_iCalUid.c_str());

    auto dateFrom = getFirstWeekDay(entry.date_from);
    auto dateTill = getFirstWeekDay(entry.date_till);

    for (uint32_t i = 1; i <= numberOfWeeks; i++) {
        for (auto option : weekDayOptions) {
            if (option) {
                result = result && db->execute("INSERT or IGNORE INTO events (uid, title, date_from, date_till, "
                                               "reminder, repeat, reminder_fired) VALUES"
                                               "('%q','%q', '%q','%q', %u, %u, '%q');",
                                               createUID().c_str(),
                                               entry.title.c_str(),
                                               TimePointToString(dateFrom + date::days{incrementation}).c_str(),
                                               TimePointToString(dateTill + date::days{incrementation}).c_str(),
                                               entry.reminder,
                                               entry.repeat,
                                               TimePointToString(entry.reminder_fired).c_str());
                result =
                    result &&
                    db->execute("INSERT or IGNORE INTO events (uid, title, date_from, date_till, "
                                "reminder, repeat, reminder_fired, provider_type, provider_id, provider_iCalUid) VALUES"
                                "('%q','%q', '%q','%q', %u, %u, '%q','%q', '%q','%q');",
                                entry.UID.c_str(),
                                entry.title.c_str(),
                                TimePointToString(dateFrom + date::days{incrementation}).c_str(),
                                TimePointToString(dateTill + date::days{incrementation}).c_str(),
                                entry.reminder,
                                entry.repeat,
                                TimePointToString(entry.reminder_fired).c_str(),
                                entry.provider_type.c_str(),
                                entry.provider_id.c_str(),
                                entry.provider_iCalUid.c_str());
            }
            ++incrementation;
        }


@@ 440,26 562,34 @@ bool EventsTable::removeByField(EventsTableFields field, const char *str)
bool EventsTable::update(EventsTableRow entry)
{
    return db->execute("UPDATE events SET title= '%q', date_from = '%q', date_till = '%q', reminder "
                       "= %u, repeat = %u, reminder_fired = '%q' WHERE _id = %u;",
                       "= %u, repeat = %u, reminder_fired = '%q', provider_type = '%q', provider_id = '%q', "
                       "provider_iCalUid = '%q' WHERE _id = %u;",
                       entry.title.c_str(),
                       TimePointToString(entry.date_from).c_str(),
                       TimePointToString(entry.date_till).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.ID);
}

bool EventsTable::updateByUID(EventsTableRow entry)
{
    return db->execute("UPDATE events SET title= '%q', date_from = '%q', date_till = '%q', reminder "
                       "= %u, repeat = %u, reminder_fired = '%q' WHERE uid = '%q';",
                       "= %u, repeat = %u, reminder_fired = '%q', provider_type = '%q', provider_id = '%q', "
                       "provider_iCalUid = '%q' WHERE uid = '%q';",
                       entry.title.c_str(),
                       TimePointToString(entry.date_from).c_str(),
                       TimePointToString(entry.date_till).c_str(),
                       entry.reminder,
                       static_cast<uint32_t>(entry.repeat),
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str());
}



@@ 481,7 611,10 @@ EventsTableRow EventsTable::getById(uint32_t id)
        TimePointFromString((*retQuery)[4].getString().c_str()), // date_till
        (*retQuery)[5].getUInt32(),                              // reminder
        (*retQuery)[6].getUInt32(),                              // repeat
        TimePointFromString((*retQuery)[7].getString().c_str())  // reminder_fired
        TimePointFromString((*retQuery)[7].getString().c_str()), // reminder_fired
        (*retQuery)[8].getString(),                              // provider type
        (*retQuery)[9].getString(),                              // provider id
        (*retQuery)[10].getString()                              // provider iCalUid
    };
}



@@ 503,7 636,10 @@ EventsTableRow EventsTable::getByUID(const std::string &UID)
        TimePointFromString((*retQuery)[4].getString().c_str()), // date_till
        (*retQuery)[5].getUInt32(),                              // reminder
        (*retQuery)[6].getUInt32(),                              // repeat
        TimePointFromString((*retQuery)[7].getString().c_str())  // reminder_fired
        TimePointFromString((*retQuery)[7].getString().c_str()), // reminder_fired
        (*retQuery)[8].getString(),                              // provider type
        (*retQuery)[9].getString(),                              // provider id
        (*retQuery)[10].getString()                              // provider iCalUid
    };
}



@@ 534,7 670,10 @@ std::vector<EventsTableRow> EventsTable::selectByDatePeriod(TimePoint date_filte
            TimePointFromString((*retQuery)[4].getString().c_str()), // date_till
            (*retQuery)[5].getUInt32(),                              // reminder
            (*retQuery)[6].getUInt32(),                              // repeat
            TimePointFromString((*retQuery)[7].getString().c_str())  // reminder_fired
            TimePointFromString((*retQuery)[7].getString().c_str()), // reminder_fired
            (*retQuery)[8].getString(),                              // provider type
            (*retQuery)[9].getString(),                              // provider id
            (*retQuery)[10].getString()                              // provider iCalUid
        });

    } while (retQuery->nextRow());


@@ 562,7 701,10 @@ std::vector<EventsTableRow> EventsTable::getLimitOffset(uint32_t offset, uint32_
            TimePointFromString((*retQuery)[4].getString().c_str()), // date_till
            (*retQuery)[5].getUInt32(),                              // reminder
            (*retQuery)[6].getUInt32(),                              // repeat
            TimePointFromString((*retQuery)[7].getString().c_str())  // reminder_fired
            TimePointFromString((*retQuery)[7].getString().c_str()), // reminder_fired
            (*retQuery)[8].getString(),                              // provider type
            (*retQuery)[9].getString(),                              // provider id
            (*retQuery)[10].getString()                              // provider iCalUid

        });
    } while (retQuery->nextRow());


@@ 590,7 732,10 @@ std::vector<EventsTableRow> EventsTable::getLimitOffsetByDate(uint32_t offset, u
            TimePointFromString((*retQuery)[4].getString().c_str()), // date_till
            (*retQuery)[5].getUInt32(),                              // reminder
            (*retQuery)[6].getUInt32(),                              // repeat
            TimePointFromString((*retQuery)[7].getString().c_str())  // reminder_fired
            TimePointFromString((*retQuery)[7].getString().c_str()), // reminder_fired
            (*retQuery)[8].getString(),                              // provider type
            (*retQuery)[9].getString(),                              // provider id
            (*retQuery)[10].getString()                              // provider iCalUid
        });
    } while (retQuery->nextRow());



@@ 609,6 754,11 @@ std::vector<EventsTableRow> EventsTable::getLimitOffsetByField(uint32_t offset,
    return ret;
}

bool EventsTable::drop()
{
    return db->execute("DROP TABLE events;");
}

uint32_t EventsTable::count()
{
    auto queryRet = db->query("SELECT COUNT(*) FROM events;");


@@ 661,14 811,17 @@ std::vector<EventsTableRow> EventsTable::SelectFirstUpcoming(TimePoint filter_fr

    do {
        ret.push_back(EventsTableRow{
            (*retQuery)[0].getUInt32(),                              // ID
            (*retQuery)[1].getString(),                              // UID
            (*retQuery)[2].getString(),                              // title
            TimePointFromString((*retQuery)[3].getString().c_str()), // date_from
            TimePointFromString((*retQuery)[4].getString().c_str()), // date_till
            (*retQuery)[5].getUInt32(),                              // reminder
            (*retQuery)[6].getUInt32(),                              // repeat
            TimePointFromString((*retQuery)[7].getString().c_str())  // reminder_fired
            (*retQuery)[1].getUInt32(),                              // ID
            (*retQuery)[2].getString(),                              // UID
            (*retQuery)[3].getString(),                              // title
            TimePointFromString((*retQuery)[4].getString().c_str()), // date_from
            TimePointFromString((*retQuery)[5].getString().c_str()), // date_till
            (*retQuery)[6].getUInt32(),                              // reminder
            (*retQuery)[7].getUInt32(),                              // repeat
            TimePointFromString((*retQuery)[8].getString().c_str()), // reminder_fired
            (*retQuery)[9].getString(),                              // provider type
            (*retQuery)[10].getString(),                             // provider id
            (*retQuery)[11].getString()                              // provider iCalUid
        });
    } while (retQuery->nextRow());


M module-db/Tables/EventsTable.hpp => module-db/Tables/EventsTable.hpp +8 -0
@@ 19,6 19,9 @@ struct EventsTableRow : public Record
    uint32_t reminder        = 0;
    uint32_t repeat          = 0;
    TimePoint reminder_fired = TIME_POINT_INVALID;
    std::string provider_type;
    std::string provider_id;
    std::string provider_iCalUid;
};

enum class EventsTableFields


@@ 40,12 43,14 @@ class EventsTable : public Table<EventsTableRow, EventsTableFields>
    bool addTwoWeeks(EventsTableRow entry);
    bool addMonth(EventsTableRow entry);
    bool addYear(EventsTableRow entry);
    std::vector<bool> parseOptions(uint32_t repeatOptionValue);
    bool addCustom(EventsTableRow entry);
    bool removeById(uint32_t id) override final;
    bool removeByUID(const std::string &UID);
    bool removeByField(EventsTableFields field, const char *str) override final;
    bool update(EventsTableRow entry) override final;
    bool updateByUID(EventsTableRow entry);
    bool drop();
    EventsTableRow getByUID(const std::string &UID);
    EventsTableRow getById(uint32_t id) override final;
    std::vector<EventsTableRow> selectByDatePeriod(TimePoint filter_from,


@@ 74,5 79,8 @@ class EventsTable : public Table<EventsTableRow, EventsTableFields>
                                   "reminder INTEGER,"
                                   "repeat INTEGER,"
                                   "reminder_fired DATETIME,"
                                   "provider_type TEXT,"
                                   "provider_id TEXT,"
                                   "provider_iCalUid TEXT,"
                                   "UNIQUE (title, date_from, date_till));";
};

M module-db/queries/calendar/QueryEventsGetAll.cpp => module-db/queries/calendar/QueryEventsGetAll.cpp +3 -3
@@ 13,12 13,12 @@ namespace db::query::events
        return "GetAll";
    }

    GetAllResult::GetAllResult(std::unique_ptr<std::vector<EventsRecord>> records) : records(std::move(records))
    GetAllResult::GetAllResult(std::vector<EventsRecord> records) : records{std::move(records)}
    {}

    auto GetAllResult::getResult() -> std::unique_ptr<std::vector<EventsRecord>>
    auto GetAllResult::getResult() -> std::vector<EventsRecord>
    {
        return std::move(records);
        return records;
    }

    auto GetAllResult::debugInfo() const -> std::string

M module-db/queries/calendar/QueryEventsGetAll.hpp => module-db/queries/calendar/QueryEventsGetAll.hpp +3 -3
@@ 19,11 19,11 @@ namespace db::query::events

    class GetAllResult : public QueryResult
    {
        std::unique_ptr<std::vector<EventsRecord>> records;
        std::vector<EventsRecord> records;

      public:
        GetAllResult(std::unique_ptr<std::vector<EventsRecord>> records);
        [[nodiscard]] auto getResult() -> std::unique_ptr<std::vector<EventsRecord>>;
        GetAllResult(std::vector<EventsRecord> records);
        [[nodiscard]] auto getResult() -> std::vector<EventsRecord>;

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

M module-db/queries/calendar/QueryEventsGetAllLimited.cpp => module-db/queries/calendar/QueryEventsGetAllLimited.cpp +7 -7
@@ 14,18 14,18 @@ namespace db::query::events
        return "GetAllLimited";
    }

    GetAllLimitedResult::GetAllLimitedResult(std::unique_ptr<std::vector<EventsRecord>> records,
                                             std::unique_ptr<uint32_t> count)
        : records(std::move(records)), recordsCount(std::move(count))
    GetAllLimitedResult::GetAllLimitedResult(std::vector<EventsRecord> records, uint32_t count)
        : records{std::move(records)}, recordsCount{count}
    {}

    auto GetAllLimitedResult::getResult() -> std::unique_ptr<std::vector<EventsRecord>>
    auto GetAllLimitedResult::getResult() -> std::vector<EventsRecord>
    {
        return std::move(records);
        return records;
    }
    auto GetAllLimitedResult::getCountResult() -> std::unique_ptr<uint32_t>

    auto GetAllLimitedResult::getCountResult() const noexcept -> uint32_t
    {
        return std::move(recordsCount);
        return recordsCount;
    }

    auto GetAllLimitedResult::debugInfo() const -> std::string

M module-db/queries/calendar/QueryEventsGetAllLimited.hpp => module-db/queries/calendar/QueryEventsGetAllLimited.hpp +5 -5
@@ 21,13 21,13 @@ namespace db::query::events

    class GetAllLimitedResult : public QueryResult
    {
        std::unique_ptr<std::vector<EventsRecord>> records;
        std::unique_ptr<uint32_t> recordsCount;
        std::vector<EventsRecord> records;
        uint32_t recordsCount;

      public:
        GetAllLimitedResult(std::unique_ptr<std::vector<EventsRecord>> records, std::unique_ptr<uint32_t> count);
        [[nodiscard]] auto getResult() -> std::unique_ptr<std::vector<EventsRecord>>;
        [[nodiscard]] auto getCountResult() -> std::unique_ptr<uint32_t>;
        GetAllLimitedResult(std::vector<EventsRecord> records, uint32_t count);
        [[nodiscard]] auto getResult() -> std::vector<EventsRecord>;
        [[nodiscard]] auto getCountResult() const noexcept -> uint32_t;

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

M module-db/queries/calendar/QueryEventsGetFiltered.cpp => module-db/queries/calendar/QueryEventsGetFiltered.cpp +4 -4
@@ 14,13 14,13 @@ namespace db::query::events
        return "GetFiltered";
    }

    GetFilteredResult::GetFilteredResult(std::unique_ptr<std::vector<EventsRecord>> records, uint32_t count)
        : records(std::move(records)), recordsCount(count)
    GetFilteredResult::GetFilteredResult(std::vector<EventsRecord> records, uint32_t count)
        : records{std::move(records)}, recordsCount{count}
    {}

    auto GetFilteredResult::getResult() -> std::unique_ptr<std::vector<EventsRecord>>
    auto GetFilteredResult::getResult() -> std::vector<EventsRecord>
    {
        return std::move(records);
        return records;
    }

    auto GetFilteredResult::getCountResult() -> uint32_t

M module-db/queries/calendar/QueryEventsGetFiltered.hpp => module-db/queries/calendar/QueryEventsGetFiltered.hpp +3 -3
@@ 24,13 24,13 @@ namespace db::query::events

    class GetFilteredResult : public QueryResult
    {
        std::unique_ptr<std::vector<EventsRecord>> records;
        std::vector<EventsRecord> records;
        uint32_t recordsCount;

      public:
        GetFilteredResult(std::unique_ptr<std::vector<EventsRecord>> records, uint32_t count);
        GetFilteredResult(std::vector<EventsRecord> records, uint32_t count);
        auto getCountResult() -> uint32_t;
        [[nodiscard]] auto getResult() -> std::unique_ptr<std::vector<EventsRecord>>;
        [[nodiscard]] auto getResult() -> std::vector<EventsRecord>;

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

M module-db/queries/calendar/QueryEventsSelectFirstUpcoming.cpp => module-db/queries/calendar/QueryEventsSelectFirstUpcoming.cpp +4 -4
@@ 14,13 14,13 @@ namespace db::query::events
        return "SelectFirstUpcoming";
    }

    SelectFirstUpcomingResult::SelectFirstUpcomingResult(std::unique_ptr<std::vector<EventsRecord>> records)
        : records(std::move(records))
    SelectFirstUpcomingResult::SelectFirstUpcomingResult(std::vector<EventsRecord> records)
        : records{std::move(records)}
    {}

    auto SelectFirstUpcomingResult::getResult() -> std::unique_ptr<std::vector<EventsRecord>>
    auto SelectFirstUpcomingResult::getResult() -> std::vector<EventsRecord>
    {
        return std::move(records);
        return records;
    }

    auto SelectFirstUpcomingResult::debugInfo() const -> std::string

M module-db/queries/calendar/QueryEventsSelectFirstUpcoming.hpp => module-db/queries/calendar/QueryEventsSelectFirstUpcoming.hpp +3 -3
@@ 24,11 24,11 @@ namespace db::query::events
    /// Result of SelectFirstUpcoming query
    class SelectFirstUpcomingResult : public QueryResult
    {
        std::unique_ptr<std::vector<EventsRecord>> records;
        std::vector<EventsRecord> records;

      public:
        SelectFirstUpcomingResult(std::unique_ptr<std::vector<EventsRecord>> records);
        [[nodiscard]] auto getResult() -> std::unique_ptr<std::vector<EventsRecord>>;
        SelectFirstUpcomingResult(std::vector<EventsRecord> records);
        [[nodiscard]] auto getResult() -> std::vector<EventsRecord>;

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

M module-db/tests/EventsRecord_tests.cpp => module-db/tests/EventsRecord_tests.cpp +857 -210
@@ 12,18 12,29 @@
#include "module-db/queries/calendar/QueryEventsGetFiltered.hpp"
#include "module-db/queries/calendar/QueryEventsAdd.hpp"
#include "module-db/queries/calendar/QueryEventsRemove.hpp"
#include "module-db/queries/calendar/QueryEventsRemoveICS.hpp"
#include "module-db/queries/calendar/QueryEventsEdit.hpp"
#include "module-db/queries/calendar/QueryEventsEditICS.hpp"
#include "module-db/queries/calendar/QueryEventsSelectFirstUpcoming.hpp"

#include <vfs.hpp>
#include <purefs/filesystem_paths.hpp>

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>

static auto remove_events(EventsDB &db) -> bool
{
    auto count   = db.events.count();
    auto records = db.events.getLimitOffset(0, count);
    bool ret     = true;
    for (auto rec : records) {
        ret = ret && db.events.removeById(rec.ID);
    }
    return ret;
}

TEST_CASE("Events Record tests")
{
    Database::initialize();


@@ 43,98 54,193 @@ TEST_CASE("Events Record tests")
        REQUIRE(testRec.repeat == 0);
    }

    EventsTableRow testRow = {{1},
                              .UID              = "test1",
                              .title            = "Event1",
                              .date_from        = TimePointFromString("2019-10-20 14:25:00"),
                              .date_till        = TimePointFromString("2019-10-20 15:36:00"),
                              .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                              .repeat           = static_cast<uint32_t>(Repeat::never),
                              .reminder_fired   = TimePointFromString("2019-10-20 14:20:00"),
                              .provider_type    = "PurePhone",
                              .provider_id      = "testID",
                              .provider_iCalUid = "test6"};

    EventsTableRow testRow2 = {{2},
                               .UID              = "test2",
                               .title            = "Event2",
                               .date_from        = TimePointFromString("2019-10-21 14:24:00"),
                               .date_till        = TimePointFromString("2019-10-21 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TIME_POINT_INVALID,
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    EventsTableRow testRow3 = {{3},
                               .UID              = "test3",
                               .title            = "Event3",
                               .date_from        = TimePointFromString("2019-10-22 14:25:00"),
                               .date_till        = TimePointFromString("2019-10-22 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TimePointFromString("2019-10-22 14:20:00"),
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    EventsTableRow testRow4 = {{4},
                               .UID              = "test4",
                               .title            = "Event4",
                               .date_from        = TimePointFromString("2019-10-23 14:25:00"),
                               .date_till        = TimePointFromString("2019-10-23 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TimePointFromString("2019-10-23 14:20:00"),
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    EventsTableRow testRow5 = {{5},
                               .UID              = "test5",
                               .title            = "Event5",
                               .date_from        = TimePointFromString("2019-10-24 14:25:00"),
                               .date_till        = TimePointFromString("2019-10-24 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TIME_POINT_INVALID,
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    EventsTableRow testRow6 = {{6},
                               .UID              = "test6",
                               .title            = "Event6",
                               .date_from        = TimePointFromString("2019-10-24 14:25:00"),
                               .date_till        = TimePointFromString("2019-10-24 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TimePointFromString("2019-10-24 14:20:00"),
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    auto check_record = [](const EventsRecord &actual, const EventsRecord &expected) {
        CHECK(actual.ID == expected.ID);
        CHECK(actual.UID == expected.UID);
        CHECK(actual.title == expected.title);
        CHECK(TimePointToString(actual.date_from) == TimePointToString(expected.date_from));
        CHECK(TimePointToString(actual.date_till) == TimePointToString(expected.date_till));
        CHECK(actual.reminder == expected.reminder);
        CHECK(actual.repeat == expected.repeat);
        CHECK(actual.reminder_fired == expected.reminder_fired);
        CHECK(actual.provider_type == expected.provider_type);
        CHECK(actual.provider_id == expected.provider_id);
        CHECK(actual.provider_iCalUid == expected.provider_iCalUid);
        CHECK(actual.isValid());
    };

    SECTION("Constructor from EventsTableRow")
    {
        EventsTableRow tableRow{{.ID = 10},
                                "test",
                                "Event3",
                                TimePointFromString("2019-10-20 14:24:00"),
                                TimePointFromString("2019-10-20 15:36:00"),
                                1,
                                2,
                                TIME_POINT_INVALID};
        EventsRecord testRec(tableRow);
        REQUIRE(testRec.title == "Event3");
        REQUIRE(testRec.date_from == TimePointFromString("2019-10-20 14:24:00"));
        REQUIRE(testRec.date_till == TimePointFromString("2019-10-20 15:36:00"));
        REQUIRE(testRec.reminder == 1);
        REQUIRE(testRec.repeat == 2);
        REQUIRE(testRec.isValid());
        EventsRecord testRecord(testRow);
        check_record(testRecord, testRow);
    }

    EventsRecord testRec;
    EventsRecordInterface eventsRecordInterface(&eventsDb);

    auto numberOfEvents = eventsRecordInterface.GetCount();
    REQUIRE(numberOfEvents == 0);

    EventsTableRow tableRow{{.ID = 10},
                            "test",
                            "Event1",
                            TimePointFromString("2019-10-20 14:24:00"),
                            TimePointFromString("2019-10-20 15:36:00"),
                            1,
                            2,
                            TIME_POINT_INVALID};

    auto rec = EventsRecord(tableRow);
    REQUIRE(rec.title == "Event1");
    REQUIRE(rec.date_from == TimePointFromString("2019-10-20 14:24:00"));
    REQUIRE(rec.date_till == TimePointFromString("2019-10-20 15:36:00"));
    REQUIRE(rec.reminder == 1);
    REQUIRE(rec.repeat == 2);
    REQUIRE(rec.isValid());

    REQUIRE(eventsRecordInterface.Add(rec));
    auto testRecord  = EventsRecord(testRow);
    auto testRecord2 = EventsRecord(testRow2);
    auto testRecord3 = EventsRecord(testRow3);
    auto testRecord4 = EventsRecord(testRow4);
    auto testRecord5 = EventsRecord(testRow5);
    auto testRecord6 = EventsRecord(testRow6);

    REQUIRE(eventsRecordInterface.Add(testRecord));
    REQUIRE(eventsRecordInterface.Add(testRecord2));
    REQUIRE(eventsRecordInterface.Add(testRecord3));
    REQUIRE(eventsRecordInterface.Add(testRecord4));
    REQUIRE(eventsRecordInterface.Add(testRecord5));
    REQUIRE(eventsRecordInterface.Add(testRecord6));

    numberOfEvents = eventsRecordInterface.GetCount();
    REQUIRE(numberOfEvents == 4);
    REQUIRE(numberOfEvents == 6);

    SECTION("Get entry by ID")
    {
        auto entry = eventsRecordInterface.GetByID(1);
        REQUIRE(entry.ID == 1);
        REQUIRE(entry.title == "Event1");
        REQUIRE(entry.date_from == TimePointFromString("2019-10-20 14:24:00"));
        REQUIRE(entry.date_till == TimePointFromString("2019-10-20 15:36:00"));
        REQUIRE(entry.reminder == 1);
        REQUIRE(entry.repeat == 2);
        REQUIRE(entry.isValid());
        auto entry = eventsRecordInterface.GetByID(testRecord3.ID);
        check_record(entry, testRecord3);
    }

    SECTION("Get count filtered")
    {
        auto count = eventsRecordInterface.GetCountFiltered(testRecord3.date_from, testRecord5.date_from);
        CHECK(count == 2);
    }

    SECTION("Get count")
    {
        auto count = eventsRecordInterface.GetCount();
        CHECK(count == 6);
    }

    SECTION("Get entry - invalid ID")
    {
        auto entry = eventsRecordInterface.GetByID(100);
        REQUIRE(entry.ID == DB_ID_NONE);
        REQUIRE(entry.title == "");
        REQUIRE(entry.date_from == TIME_POINT_INVALID);
        REQUIRE(entry.date_till == TIME_POINT_INVALID);
        REQUIRE(entry.reminder == 0);
        REQUIRE(entry.repeat == 0);
        REQUIRE_FALSE(entry.isValid());
        CHECK(entry.ID == DB_ID_NONE);
        CHECK(entry.title == "");
        CHECK(entry.date_from == TIME_POINT_INVALID);
        CHECK(entry.date_till == TIME_POINT_INVALID);
        CHECK(entry.reminder == 0);
        CHECK(entry.repeat == 0);
        CHECK(entry.reminder_fired == TIME_POINT_INVALID);
        CHECK(entry.provider_type == "");
        CHECK(entry.provider_id == "");
        CHECK(entry.provider_iCalUid == "");
        CHECK_FALSE(entry.isValid());
    }

    EventsTableRow tableRow2{{.ID = 10},
                             "test",
                             "Event2",
                             TimePointFromString("2025-10-20 14:24:00"),
                             TimePointFromString("2025-10-20 15:36:00"),
                             1,
                             2,
                             TIME_POINT_INVALID};

    auto rec2 = EventsRecord(tableRow2);
    REQUIRE(rec2.title == "Event2");
    REQUIRE(rec2.date_from == TimePointFromString("2025-10-20 14:24:00"));
    REQUIRE(rec2.date_till == TimePointFromString("2025-10-20 15:36:00"));
    REQUIRE(rec2.reminder == 1);
    REQUIRE(rec2.repeat == 2);
    REQUIRE(rec2.isValid());

    REQUIRE(eventsRecordInterface.Add(rec2));
    SECTION("Entry remove")
    {
        REQUIRE(eventsRecordInterface.RemoveByID(testRecord4.ID));
        numberOfEvents = eventsRecordInterface.GetCount();
        REQUIRE(numberOfEvents == 5);
        CHECK_NOTHROW(eventsRecordInterface.GetByID(testRecord4.ID));
    }

    numberOfEvents = eventsRecordInterface.GetCount();
    REQUIRE(numberOfEvents == 8);
    SECTION("Entry remove by uid")
    {
        REQUIRE(eventsRecordInterface.RemoveByUID(testRecord4.UID));
        numberOfEvents = eventsRecordInterface.GetCount();
        REQUIRE(numberOfEvents == 5);
        CHECK_NOTHROW(eventsRecordInterface.GetByID(testRecord4.ID));
    }

    SECTION("Entry update")
    {
        auto entryToUpdate      = eventsRecordInterface.GetByID(testRecord5.ID);
        entryToUpdate.title     = "newTitle";
        entryToUpdate.date_from = TimePointFromString("2019-12-31 23:59:00");
        entryToUpdate.date_till = TimePointFromString("2019-12-31 23:59:00");
        REQUIRE(eventsRecordInterface.Update(entryToUpdate));

        auto entry = eventsRecordInterface.GetByID(entryToUpdate.ID);
        check_record(entry, entryToUpdate);
    }

    SECTION("Entry update by uid")
    {
        auto entryToUpdate      = eventsRecordInterface.GetByID(testRecord3.ID);
        entryToUpdate.title     = "newTitle";
        entryToUpdate.date_from = TimePointFromString("2019-12-31 23:59:00");
        entryToUpdate.date_till = TimePointFromString("2019-12-31 23:59:00");
        REQUIRE(eventsRecordInterface.UpdateByUID(entryToUpdate));

        auto entry = eventsRecordInterface.GetByID(entryToUpdate.ID);
        check_record(entry, entryToUpdate);
    }

    SECTION("Get entries")
    {


@@ 143,6 249,12 @@ TEST_CASE("Events Record tests")
        {
            auto retOffsetLimit = eventsRecordInterface.GetLimitOffset(0, numberOfEvents);
            REQUIRE(retOffsetLimit->size() == numberOfEvents);
            CHECK((*retOffsetLimit)[0].ID == testRecord.ID);
            CHECK((*retOffsetLimit)[1].ID == testRecord2.ID);
            CHECK((*retOffsetLimit)[2].ID == testRecord3.ID);
            CHECK((*retOffsetLimit)[3].ID == testRecord4.ID);
            CHECK((*retOffsetLimit)[4].ID == testRecord5.ID);
            CHECK((*retOffsetLimit)[5].ID == testRecord6.ID);
        }

        SECTION("Get table rows using bigger limit parameters")


@@ 153,8 265,8 @@ TEST_CASE("Events Record tests")

        SECTION("Get table rows using invalid offset/limit parameters(should return empty object)")
        {
            auto retOffsetLimit = eventsRecordInterface.GetLimitOffset(5, 4);
            REQUIRE(retOffsetLimit->size() == 3);
            auto retOffsetLimit = eventsRecordInterface.GetLimitOffset(6, 10);
            REQUIRE(retOffsetLimit->size() == 0);
        }

        SECTION("0 - get all")


@@ 163,227 275,762 @@ TEST_CASE("Events Record tests")
            REQUIRE(retOffsetLimit->size() == numberOfEvents);
        }

        SECTION("Get table rows by SELECT")
        {
            auto retOffsetLimit = eventsRecordInterface.Select(
                TimePointFromString("2019-10-19 14:24:00"), TimePointFromString("2019-10-27 15:36:00"), 0, UINT32_MAX);
            REQUIRE(retOffsetLimit.size() == numberOfEvents);
        }

        SECTION("Get table rows by SELECT limited")
        {
            auto retOffsetLimit = eventsRecordInterface.Select(
                TimePointFromString("2019-10-19 14:24:00"), TimePointFromString("2019-10-27 15:36:00"), 0, 3);
            REQUIRE(retOffsetLimit.size() == 3);
        }

        SECTION("Get table rows by SELECT limited by date")
        {
            auto retOffsetLimit = eventsRecordInterface.Select(
                TimePointFromString("2019-10-19 14:24:00"), TimePointFromString("2019-10-22 15:36:00"), 0, UINT32_MAX);
            REQUIRE(retOffsetLimit.size() == 2);
        }

        SECTION("Get table rows by SELECT invalid")
        {
            auto retOffsetLimit = eventsRecordInterface.Select(
                TimePointFromString("2010-10-20 14:24:00"), TimePointFromString("2010-10-20 15:36:00"), 0, UINT32_MAX);
            REQUIRE(retOffsetLimit->size() == 0);
            REQUIRE(retOffsetLimit.size() == 0);
        }

        SECTION("Get records using valid offset/limit date parameters")
        {
            auto recordWithEarliestDate      = eventsRecordInterface.GetByID(testRecord3.ID);
            recordWithEarliestDate.date_from = TimePointFromString("1990-10-23 14:25:00");
            REQUIRE(eventsRecordInterface.Update(recordWithEarliestDate));
            auto retOffsetLimit = eventsRecordInterface.GetLimitOffsetByDate(0, numberOfEvents);
            REQUIRE(retOffsetLimit.size() == numberOfEvents);
            CHECK((retOffsetLimit)[0].ID == testRecord3.ID);
            CHECK((retOffsetLimit)[1].ID == testRecord.ID);
            CHECK((retOffsetLimit)[2].ID == testRecord2.ID);
            CHECK((retOffsetLimit)[3].ID == testRecord4.ID);
            CHECK((retOffsetLimit)[4].ID == testRecord5.ID);
            CHECK((retOffsetLimit)[5].ID == testRecord6.ID);
        }

        SECTION("Get records using valid offset/limit date parameters")
        {
            auto recordWithEarliestDate      = eventsRecordInterface.GetByID(testRecord3.ID);
            recordWithEarliestDate.date_from = TimePointFromString("1990-10-23 14:25:00");
            recordWithEarliestDate.date_till = TimePointFromString("1990-10-23 14:25:00");
            REQUIRE(eventsRecordInterface.Update(recordWithEarliestDate));

            auto retOffsetLimit = eventsRecordInterface.GetLimitOffsetByDate(0, numberOfEvents);
            REQUIRE(retOffsetLimit.size() == numberOfEvents);
            CHECK((retOffsetLimit)[0].ID == recordWithEarliestDate.ID);
            CHECK((retOffsetLimit)[1].ID == testRecord.ID);
            CHECK((retOffsetLimit)[2].ID == testRecord2.ID);
            CHECK((retOffsetLimit)[3].ID == testRecord4.ID);
            CHECK((retOffsetLimit)[4].ID == testRecord5.ID);
            CHECK((retOffsetLimit)[5].ID == testRecord6.ID);
        }
    }

    SECTION("Entry update value")
    SECTION("Entry add repeated")
    {
        auto entryPre      = eventsRecordInterface.GetByID(1);
        entryPre.title     = "newTitle";
        entryPre.date_from = TimePointFromString("2019-12-31 23:59:00");
        entryPre.date_till = TimePointFromString("2019-12-31 23:59:00");
        REQUIRE(eventsRecordInterface.Update(entryPre));

        auto entry = eventsRecordInterface.GetByID(1);
        REQUIRE(entry.ID == entryPre.ID);
        REQUIRE(entry.title == entryPre.title);
        REQUIRE(entry.date_from == entryPre.date_from);
        REQUIRE(entry.date_till == entryPre.date_till);
        REQUIRE(entry.reminder == entryPre.reminder);
        REQUIRE(entry.repeat == entryPre.repeat);
        if (eventsRecordInterface.GetCount() > 0) {
            REQUIRE(remove_events(eventsDb));
        }
        REQUIRE(eventsRecordInterface.GetCount() == 0);

        uint32_t testRecordActualId = numberOfEvents + 1;
        testRecord3.ID              = testRecordActualId;
        auto expectedRecordData     = testRecord3;

        auto set_repeat = [&](Repeat option, uint32_t count) {
            expectedRecordData.repeat = static_cast<uint32_t>(option);
            REQUIRE(eventsRecordInterface.Add(expectedRecordData));
            auto entries = eventsRecordInterface.GetLimitOffset(0, 0);
            REQUIRE(entries->size() == count);
            return *entries;
        };

        SECTION("Daily, count = 7")
        {
            uint32_t expectedEventsCount = 7;
            auto entries                 = set_repeat(Repeat::daily, expectedEventsCount);

            for (auto entry : entries) {
                check_record(entry, expectedRecordData);
                expectedRecordData.date_from = expectedRecordData.date_from + 24h;
                expectedRecordData.date_till = expectedRecordData.date_till + 24h;
            }
        }

        SECTION("Weekly, count = 4")
        {
            uint32_t expectedEventsCount = 4;
            auto entries                 = set_repeat(Repeat::weekly, expectedEventsCount);

            for (auto entry : entries) {
                check_record(entry, expectedRecordData);
                expectedRecordData.date_from = expectedRecordData.date_from + (7 * 24h);
                expectedRecordData.date_till = expectedRecordData.date_till + (7 * 24h);
            }
        }

        SECTION("Biweekly, count = 4")
        {
            uint32_t expectedEventsCount = 4;
            auto entries                 = set_repeat(Repeat::biweekly, expectedEventsCount);

            for (auto entry : entries) {
                check_record(entry, expectedRecordData);
                expectedRecordData.date_from = expectedRecordData.date_from + (2 * 7 * 24h);
                expectedRecordData.date_till = expectedRecordData.date_till + (2 * 7 * 24h);
            }
        }

        SECTION("Monthly, count = 12")
        {
            uint32_t expectedEventsCount = 12;
            expectedRecordData.date_from = TimePointFromString("2019-01-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-01-05 17:00:00");
            auto entries                 = set_repeat(Repeat::monthly, expectedEventsCount);
            check_record(entries[0], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-02-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-02-05 17:00:00");
            check_record(entries[1], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-03-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-03-05 17:00:00");
            check_record(entries[2], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-04-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-04-05 17:00:00");
            check_record(entries[3], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-05-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-05-05 17:00:00");
            check_record(entries[4], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-06-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-06-05 17:00:00");
            check_record(entries[5], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-07-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-07-05 17:00:00");
            check_record(entries[6], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-08-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-08-05 17:00:00");
            check_record(entries[7], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-09-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-09-05 17:00:00");
            check_record(entries[8], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-10-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-10-05 17:00:00");
            check_record(entries[9], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-11-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-11-05 17:00:00");
            check_record(entries[10], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-12-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-12-05 17:00:00");
            check_record(entries[11], expectedRecordData);
        }

        SECTION("Yearly, count = 4")
        {
            uint32_t expectedEventsCount = 4;
            expectedRecordData.date_from = TimePointFromString("2019-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-02-28 17:00:00");
            auto entries                 = set_repeat(Repeat::yearly, expectedEventsCount);
            check_record(entries[0], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2020-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2020-02-28 17:00:00");
            check_record(entries[1], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2020-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2020-02-28 17:00:00");
            check_record(entries[2], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2020-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2020-02-28 17:00:00");
            check_record(entries[3], expectedRecordData);
        }
    }

    REQUIRE(numberOfEvents == 8);
    SECTION("Entry update repeated")
    {
        if (eventsRecordInterface.GetCount() > 0) {
            REQUIRE(remove_events(eventsDb));
        }
        REQUIRE(eventsRecordInterface.GetCount() == 0);
        uint32_t testRecordActualId = numberOfEvents + 1;
        testRecord.ID               = testRecordActualId;
        REQUIRE(eventsRecordInterface.Add(testRecord));

        auto expectedRecordData = eventsRecordInterface.GetByID(testRecord.ID);

        auto set_repeat = [&](Repeat option, uint32_t count) {
            expectedRecordData.repeat = static_cast<uint32_t>(option);
            REQUIRE(eventsRecordInterface.Update(expectedRecordData));
            auto entries = eventsRecordInterface.GetLimitOffset(0, 0);
            REQUIRE(entries->size() == count);
            return *entries;
        };

        SECTION("Daily, count = 7")
        {
            uint32_t expectedEventsCount = 7;
            auto entries                 = set_repeat(Repeat::daily, expectedEventsCount);

            for (auto entry : entries) {
                check_record(entry, expectedRecordData);
                expectedRecordData.date_from = expectedRecordData.date_from + 24h;
                expectedRecordData.date_till = expectedRecordData.date_till + 24h;
            }
        }

        SECTION("Weekly, count = 4")
        {
            uint32_t expectedEventsCount = 4;
            auto entries                 = set_repeat(Repeat::weekly, expectedEventsCount);

            for (auto entry : entries) {
                check_record(entry, expectedRecordData);
                expectedRecordData.date_from = expectedRecordData.date_from + (7 * 24h);
                expectedRecordData.date_till = expectedRecordData.date_till + (7 * 24h);
            }
        }

        SECTION("Biweekly, count = 4")
        {
            uint32_t expectedEventsCount = 4;
            auto entries                 = set_repeat(Repeat::biweekly, expectedEventsCount);

            for (auto entry : entries) {
                check_record(entry, expectedRecordData);
                expectedRecordData.date_from = expectedRecordData.date_from + (2 * 7 * 24h);
                expectedRecordData.date_till = expectedRecordData.date_till + (2 * 7 * 24h);
            }
        }

        SECTION("Monthly, count = 12")
        {
            uint32_t expectedEventsCount = 12;
            expectedRecordData.date_from = TimePointFromString("2019-01-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-01-05 17:00:00");
            auto entries                 = set_repeat(Repeat::monthly, expectedEventsCount);
            check_record(entries[0], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-02-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-02-05 17:00:00");
            check_record(entries[1], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-03-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-03-05 17:00:00");
            check_record(entries[2], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-04-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-04-05 17:00:00");
            check_record(entries[3], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-05-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-05-05 17:00:00");
            check_record(entries[4], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-06-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-06-05 17:00:00");
            check_record(entries[5], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-07-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-07-05 17:00:00");
            check_record(entries[6], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-08-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-08-05 17:00:00");
            check_record(entries[7], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-09-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-09-05 17:00:00");
            check_record(entries[8], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-10-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-10-05 17:00:00");
            check_record(entries[9], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-11-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-11-05 17:00:00");
            check_record(entries[10], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-12-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-12-05 17:00:00");
            check_record(entries[11], expectedRecordData);
        }

        SECTION("Yearly, count = 4")
        {
            uint32_t expectedEventsCount = 4;
            expectedRecordData.date_from = TimePointFromString("2019-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-02-28 17:00:00");
            auto entries                 = set_repeat(Repeat::yearly, expectedEventsCount);
            check_record(entries[0], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2020-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2020-02-28 17:00:00");
            check_record(entries[1], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2021-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2021-02-28 17:00:00");
            check_record(entries[2], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2022-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2022-02-28 17:00:00");
            check_record(entries[3], expectedRecordData);
        }
    }

    SECTION("Entry updateICS repeated")
    {
        if (eventsRecordInterface.GetCount() > 0) {
            REQUIRE(remove_events(eventsDb));
        }
        REQUIRE(eventsRecordInterface.GetCount() == 0);

        REQUIRE(eventsRecordInterface.Add(testRecord));
        auto expectedRecordData = eventsRecordInterface.GetByUID(testRecord.UID);

        auto set_repeat = [&](Repeat option, uint32_t count) {
            expectedRecordData.repeat = static_cast<uint32_t>(option);
            REQUIRE(eventsRecordInterface.UpdateByUID(expectedRecordData));
            auto entries = eventsRecordInterface.GetLimitOffset(0, 0);
            REQUIRE(entries->size() == count);
            return *entries;
        };

        SECTION("Daily, count = 7")
        {
            uint32_t expectedEventsCount = 7;
            auto entries                 = set_repeat(Repeat::daily, expectedEventsCount);

            for (auto entry : entries) {
                check_record(entry, expectedRecordData);
                expectedRecordData.date_from = expectedRecordData.date_from + 24h;
                expectedRecordData.date_till = expectedRecordData.date_till + 24h;
            }
        }

        SECTION("Weekly, count = 4")
        {
            uint32_t expectedEventsCount = 4;
            auto entries                 = set_repeat(Repeat::weekly, expectedEventsCount);

            for (auto entry : entries) {
                check_record(entry, expectedRecordData);
                expectedRecordData.date_from = expectedRecordData.date_from + (7 * 24h);
                expectedRecordData.date_till = expectedRecordData.date_till + (7 * 24h);
            }
        }

        SECTION("Biweekly, count = 4")
        {
            uint32_t expectedEventsCount = 4;
            auto entries                 = set_repeat(Repeat::biweekly, expectedEventsCount);

            for (auto entry : entries) {
                check_record(entry, expectedRecordData);
                expectedRecordData.date_from = expectedRecordData.date_from + (2 * 7 * 24h);
                expectedRecordData.date_till = expectedRecordData.date_till + (2 * 7 * 24h);
            }
        }

        SECTION("Monthly, count = 12")
        {
            uint32_t expectedEventsCount = 12;
            expectedRecordData.date_from = TimePointFromString("2019-01-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-01-05 17:00:00");
            auto entries                 = set_repeat(Repeat::monthly, expectedEventsCount);
            check_record(entries[0], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-02-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-02-05 17:00:00");
            check_record(entries[1], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-03-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-03-05 17:00:00");
            check_record(entries[2], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-04-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-04-05 17:00:00");
            check_record(entries[3], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-05-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-05-05 17:00:00");
            check_record(entries[4], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-06-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-06-05 17:00:00");
            check_record(entries[5], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-07-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-07-05 17:00:00");
            check_record(entries[6], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-08-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-08-05 17:00:00");
            check_record(entries[7], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-09-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-09-05 17:00:00");
            check_record(entries[8], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-10-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-10-05 17:00:00");
            check_record(entries[9], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-11-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-11-05 17:00:00");
            check_record(entries[10], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-12-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-12-05 17:00:00");
            check_record(entries[11], expectedRecordData);
        }

        SECTION("Yearly, count = 4")
        {
            uint32_t expectedEventsCount = 4;
            expectedRecordData.date_from = TimePointFromString("2019-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-02-28 17:00:00");
            auto entries                 = set_repeat(Repeat::yearly, expectedEventsCount);
            check_record(entries[0], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2020-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2020-02-28 17:00:00");
            check_record(entries[1], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2021-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2021-02-28 17:00:00");
            check_record(entries[2], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2022-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2022-02-28 17:00:00");
            check_record(entries[3], expectedRecordData);
        }
    }

    SECTION("Select first upcoming event")
    {
        TimePoint start_date = TimePointFromString("2025-10-27 14:24:00");
        TimePoint start_date = TimePointFromString("2019-10-19 14:24:00");
        auto nextUpcoming    = eventsRecordInterface.SelectFirstUpcoming(start_date, start_date);
        REQUIRE(nextUpcoming->size() == 1);
        EventsRecord nextEventsRecord = nextUpcoming->at(0);
        REQUIRE(nextEventsRecord.ID == 7);
        REQUIRE(nextUpcoming.size() == 1);
        EventsRecord nextEventsRecord = nextUpcoming.at(0);
        REQUIRE(nextEventsRecord.ID == 2);
        nextEventsRecord.reminder_fired = TimePointFromString("2018-12-31 23:59:00");
        REQUIRE(eventsRecordInterface.Update(nextEventsRecord));

        start_date   = TimePointFromString("2025-11-10 14:24:00");
        start_date   = TimePointFromString("2019-10-23 14:24:00");
        nextUpcoming = eventsRecordInterface.SelectFirstUpcoming(start_date, start_date);
        REQUIRE(nextUpcoming->size() == 0);
        REQUIRE(nextUpcoming.size() == 1);
        REQUIRE(nextEventsRecord.ID == 2);
    }

    EventsTableRow tableRow3{{.ID = 3},
                             "test",
                             "Event3",
                             TimePointFromString("2021-10-20 14:24:00"),
                             TimePointFromString("2021-10-20 15:36:00"),
                             1,
                             2,
                             TIME_POINT_INVALID};
    auto rec3 = EventsRecord(tableRow3);
    REQUIRE(rec3.title == "Event3");
    REQUIRE(rec3.date_from == TimePointFromString("2021-10-20 14:24:00"));
    REQUIRE(rec3.date_till == TimePointFromString("2021-10-20 15:36:00"));
    REQUIRE(rec3.reminder == 1);
    REQUIRE(rec3.repeat == 2);
    REQUIRE(rec3.isValid());

    REQUIRE(eventsRecordInterface.Add(rec3));

    auto getQuery = [&](uint32_t id, std::string title, TimePoint date_from, TimePoint date_till) {
    ///====TEST QUERY====
    auto getQuery = [&](uint32_t id, EventsRecord expected_record) {
        auto query  = std::make_shared<db::query::events::Get>(id);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::GetResult *>(ret.get());
        REQUIRE(result != nullptr);
        auto record = result->getResult();
        REQUIRE(record.isValid());
        REQUIRE(record.title == title);
        check_record(record, expected_record);
    };

    SECTION("Check Get via query")
    {
        getQuery(testRecord.ID, testRecord);
        getQuery(testRecord3.ID, testRecord3);
        getQuery(testRecord6.ID, testRecord6);
    }

        return record.date_from;
    [[maybe_unused]] auto getAll = [&](std::vector<EventsRecord> expected_records, uint32_t expectedAllRecsNumber) {
        auto query  = std::make_shared<db::query::events::GetAll>();
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::GetAllResult *>(ret.get());
        REQUIRE(result != nullptr);
        auto records = result->getResult();
        REQUIRE(records.size() == expectedAllRecsNumber);

        auto expected_record = expected_records.begin();
        std::for_each(records.begin(), records.end(), [&expected_record](const EventsRecord &record) {
            CHECK(record.ID == expected_record->ID);
            CHECK(record.UID == expected_record->UID);
            CHECK(record.title == expected_record->title);
            CHECK(TimePointToString(record.date_from) == TimePointToString(expected_record->date_from));
            CHECK(TimePointToString(record.date_till) == TimePointToString(expected_record->date_till));
            CHECK(record.reminder == expected_record->reminder);
            CHECK(record.repeat == expected_record->repeat);
            CHECK(record.reminder_fired == expected_record->reminder_fired);
            CHECK(record.provider_type == expected_record->provider_type);
            CHECK(record.provider_id == expected_record->provider_id);
            CHECK(record.provider_iCalUid == expected_record->provider_iCalUid);
            CHECK(record.isValid());
            expected_record++;
        });
    };

    auto getFiltered = [&](uint32_t id,
                           std::string title,
                           TimePoint date_from,
                           TimePoint date_till,
                           TimePoint filter_from,
                           TimePoint filter_till) {
    SECTION("Get All via query")
    {
        std::vector<EventsRecord> expectedRecords;
        expectedRecords.push_back(testRecord);
        expectedRecords.push_back(testRecord2);
        expectedRecords.push_back(testRecord3);
        expectedRecords.push_back(testRecord4);
        expectedRecords.push_back(testRecord5);
        expectedRecords.push_back(testRecord6);
        getAll(expectedRecords, numberOfEvents);
    }

    auto getFiltered = [&](TimePoint filter_from, TimePoint filter_till, std::vector<EventsRecord> expected_records) {
        auto query  = std::make_shared<db::query::events::GetFiltered>(filter_from, filter_till);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::GetFilteredResult *>(ret.get());
        REQUIRE(result != nullptr);
        auto records = result->getResult();
        REQUIRE(records->size() == 0);
        REQUIRE(records.size() == expected_records.size());
        auto count = result->getCountResult();
        REQUIRE(count == expected_records.size());

        auto expected_record = expected_records.begin();
        std::for_each(records.begin(), records.end(), [&expected_record](const EventsRecord &record) {
            CHECK(record.ID == expected_record->ID);
            CHECK(record.UID == expected_record->UID);
            CHECK(record.title == expected_record->title);
            CHECK(TimePointToString(record.date_from) == TimePointToString(expected_record->date_from));
            CHECK(TimePointToString(record.date_till) == TimePointToString(expected_record->date_till));
            CHECK(record.reminder == expected_record->reminder);
            CHECK(record.repeat == expected_record->repeat);
            CHECK(record.reminder_fired == expected_record->reminder_fired);
            CHECK(record.provider_type == expected_record->provider_type);
            CHECK(record.provider_id == expected_record->provider_id);
            CHECK(record.provider_iCalUid == expected_record->provider_iCalUid);
            CHECK(record.isValid());
            expected_record++;
        });
    };

    auto getAllLimited = [&](uint32_t offset, uint32_t limit) {
    SECTION("GetFiltered via query")
    {
        auto startDate = testRecord2.date_from;
        auto endDate   = testRecord5.date_till;
        std::vector<EventsRecord> expectedFilteredRecords;
        expectedFilteredRecords.push_back(testRecord2);
        expectedFilteredRecords.push_back(testRecord3);
        expectedFilteredRecords.push_back(testRecord4);
        getFiltered(startDate, endDate, expectedFilteredRecords);
    }

    auto getAllLimited = [&](uint32_t offset,
                             uint32_t limit,
                             std::vector<EventsRecord> expected_records,
                             uint32_t expectedAllRecsNumber) {
        auto query  = std::make_shared<db::query::events::GetAllLimited>(offset, limit);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::GetAllLimitedResult *>(ret.get());
        REQUIRE(result != nullptr);
        auto records = result->getResult();
        REQUIRE(records->size() == limit);
        REQUIRE(records.size() == limit);
        auto count = result->getCountResult();
        return count;
        CHECK(count == expectedAllRecsNumber);

        auto expected_record = expected_records.begin();
        std::for_each(records.begin(), records.end(), [&expected_record](const EventsRecord &record) {
            CHECK(record.ID == expected_record->ID);
            CHECK(record.UID == expected_record->UID);
            CHECK(record.title == expected_record->title);
            CHECK(TimePointToString(record.date_from) == TimePointToString(expected_record->date_from));
            CHECK(TimePointToString(record.date_till) == TimePointToString(expected_record->date_till));
            CHECK(record.reminder == expected_record->reminder);
            CHECK(record.repeat == expected_record->repeat);
            CHECK(record.reminder_fired == expected_record->reminder_fired);
            CHECK(record.provider_type == expected_record->provider_type);
            CHECK(record.provider_id == expected_record->provider_id);
            CHECK(record.provider_iCalUid == expected_record->provider_iCalUid);
            CHECK(record.isValid());
            expected_record++;
        });
    };

    auto AddQuery = [&](uint32_t id, std::string title, TimePoint date_from, TimePoint date_till) {
        EventsTableRow tableRow2{{.ID = id}, "test", title, date_from, date_till, 1, 2, TIME_POINT_INVALID};
        auto record = EventsRecord(tableRow2);
    SECTION("Get All Limited via query")
    {
        std::vector<EventsRecord> expectedRecords;
        expectedRecords.push_back(testRecord2);
        expectedRecords.push_back(testRecord3);
        expectedRecords.push_back(testRecord4);
        expectedRecords.push_back(testRecord5);
        uint32_t limit = 4;
        getAllLimited(testRecord2.ID - 1, limit, expectedRecords, numberOfEvents);
    }

    auto AddQuery = [&](EventsRecord record) {
        auto query  = std::make_shared<db::query::events::Add>(record);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::AddResult *>(ret.get());
        REQUIRE(result != nullptr);
        REQUIRE(result->getResult());

        auto entry = eventsRecordInterface.GetByID(record.ID);
        check_record(entry, record);
    };

    SECTION("Add via query")
    {
        EventsTableRow testRow7 = {{7},
                                   .UID              = "test7",
                                   .title            = "Event7",
                                   .date_from        = TimePointFromString("2019-11-24 14:25:00"),
                                   .date_till        = TimePointFromString("2019-12-24 15:36:00"),
                                   .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                                   .repeat           = static_cast<uint32_t>(Repeat::never),
                                   .reminder_fired   = TimePointFromString("2019-10-24 14:20:00"),
                                   .provider_type    = "PurePhone",
                                   .provider_id      = "testID",
                                   .provider_iCalUid = "test6"};

        auto testRecord7 = EventsRecord(testRow7);
        AddQuery(testRecord7);
    }

    auto RemoveQuery = [&](uint32_t id) {
        auto query  = std::make_shared<db::query::events::Remove>(id);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::RemoveResult *>(ret.get());
        REQUIRE(result != nullptr);
        REQUIRE(result->getResult());
        REQUIRE_NOTHROW(eventsRecordInterface.GetByID(id));
    };

    auto EditQuery = [&](uint32_t id,
                         std::string title,
                         TimePoint date_from,
                         TimePoint date_till,
                         uint32_t reminder,
                         uint32_t repeat) {
        EventsTableRow tableRow2{{.ID = id}, "test", title, date_from, date_till, reminder, repeat, TIME_POINT_INVALID};
        auto record = EventsRecord(tableRow2);
        auto query  = std::make_shared<db::query::events::Edit>(record);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::EditResult *>(ret.get());
        REQUIRE(result != nullptr);

        auto queryGet  = std::make_shared<db::query::events::Get>(id);
        auto retGet    = eventsRecordInterface.runQuery(queryGet);
        auto resultGet = dynamic_cast<db::query::events::GetResult *>(retGet.get());
        REQUIRE(result != nullptr);
        auto recordGet = resultGet->getResult();
        REQUIRE(recordGet.isValid());
        REQUIRE(recordGet.title == title);
        REQUIRE(recordGet.reminder == reminder);
        REQUIRE(recordGet.repeat == repeat);
    };
    SECTION("Remove via query")
    {
        RemoveQuery(testRecord2.ID);
        REQUIRE_NOTHROW(eventsRecordInterface.GetByID(testRecord2.ID));
        std::vector<EventsRecord> expectedRecords;
        expectedRecords.push_back(testRecord);
        expectedRecords.push_back(testRecord3);
        expectedRecords.push_back(testRecord4);
        expectedRecords.push_back(testRecord5);
        expectedRecords.push_back(testRecord6);
        getAll(expectedRecords, numberOfEvents - 1);
    }

    auto selectFirstUpcomingEvent = [&](TimePoint filter_from, TimePoint filter_till) {
        auto query  = std::make_shared<db::query::events::SelectFirstUpcoming>(filter_from, filter_till);
    auto RemoveQueryICS = [&](std::string uid) {
        auto query  = std::make_shared<db::query::events::RemoveICS>(uid);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::SelectFirstUpcomingResult *>(ret.get());
        auto result = dynamic_cast<db::query::events::RemoveICSResult *>(ret.get());
        REQUIRE(result != nullptr);
        auto records = result->getResult();
        return records;
        REQUIRE(result->getResult());
        REQUIRE_NOTHROW(eventsRecordInterface.GetByUID(uid));
    };

    SECTION("Get via query")
    {
        getQuery(3, "Event1", TimePointFromString("2021-10-20 14:24:00"), TimePointFromString("2021-10-20 15:36:00"));
    }

    SECTION("GetFiltered via query")
    SECTION("RemoveICS via query")
    {
        getFiltered(1,
                    "Event1",
                    TimePointFromString("2019-10-20 14:24:00"),
                    TimePointFromString("2019-10-20 15:36:00"),
                    TimePointFromString("2018-10-20 14:24:00"),
                    TimePointFromString("2019-10-20 15:36:00"));
        RemoveQueryICS(testRecord2.UID);
        REQUIRE_NOTHROW(eventsRecordInterface.GetByUID(testRecord2.UID));
        std::vector<EventsRecord> expectedRecords;
        expectedRecords.push_back(testRecord);
        expectedRecords.push_back(testRecord3);
        expectedRecords.push_back(testRecord4);
        expectedRecords.push_back(testRecord5);
        expectedRecords.push_back(testRecord6);
        getAll(expectedRecords, numberOfEvents - 1);
    }

    SECTION("Add via query")
    {
        AddQuery(3, "Event3", TimePointFromString("2026-10-20 14:24:00"), TimePointFromString("2022-10-20 15:36:00"));
        getQuery(4, "Event1", TimePointFromString("2026-10-20 14:24:00"), TimePointFromString("2022-10-20 15:36:00"));
    }
    auto EditQuery = [&](uint32_t id, EventsRecord record) {
        auto entryToUpdate             = eventsRecordInterface.GetByID(id);
        entryToUpdate.title            = record.title;
        entryToUpdate.date_from        = record.date_from;
        entryToUpdate.date_till        = record.date_till;
        entryToUpdate.reminder         = record.reminder;
        entryToUpdate.repeat           = record.repeat;
        entryToUpdate.provider_type    = record.provider_type;
        entryToUpdate.provider_id      = record.provider_id;
        entryToUpdate.provider_iCalUid = record.provider_iCalUid;

    SECTION("Remove via query")
    {
        RemoveQuery(2);
        auto query  = std::make_shared<db::query::events::GetAll>();
        auto query  = std::make_shared<db::query::events::Edit>(entryToUpdate);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::GetAllResult *>(ret.get());
        auto result = dynamic_cast<db::query::events::EditResult *>(ret.get());
        REQUIRE(result != nullptr);
        auto records = result->getResult();
        REQUIRE(records->size() == 11);
    }

        auto entry = eventsRecordInterface.GetByID(entryToUpdate.ID);
        record.ID  = entry.ID;
        record.UID = entry.UID;
        check_record(entry, record);
    };

    SECTION("Update via query")
    {
        EditQuery(
            2, "Event1", TimePointFromString("2021-10-20 14:24:00"), TimePointFromString("2021-10-20 15:36:00"), 1, 2);
        EditQuery(testRecord.ID, testRecord);
        EditQuery(testRecord3.ID, testRecord3);
        EditQuery(testRecord6.ID, testRecord6);
    }

    SECTION("Get All via query")
    {
        auto query  = std::make_shared<db::query::events::GetAll>();
    auto EditQueryICS = [&](std::string uid, EventsRecord recordUpdateData) {
        auto entryToUpdate             = eventsRecordInterface.GetByUID(uid);
        entryToUpdate.title            = recordUpdateData.title;
        entryToUpdate.date_from        = recordUpdateData.date_from;
        entryToUpdate.date_till        = recordUpdateData.date_till;
        entryToUpdate.reminder         = recordUpdateData.reminder;
        entryToUpdate.repeat           = recordUpdateData.repeat;
        entryToUpdate.provider_type    = recordUpdateData.provider_type;
        entryToUpdate.provider_id      = recordUpdateData.provider_id;
        entryToUpdate.provider_iCalUid = recordUpdateData.provider_iCalUid;

        auto query  = std::make_shared<db::query::events::EditICS>(entryToUpdate);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::GetAllResult *>(ret.get());
        auto result = dynamic_cast<db::query::events::EditICSResult *>(ret.get());
        REQUIRE(result != nullptr);
        auto records = result->getResult();
        REQUIRE(records->size() == 12);
    }

    SECTION("Get All Limited via query")
        auto entry           = eventsRecordInterface.GetByID(entryToUpdate.ID);
        recordUpdateData.ID  = entry.ID;
        recordUpdateData.UID = entry.UID;
        check_record(entry, recordUpdateData);
    };

    SECTION("Update ICS via query")
    {
        AddQuery(3, "Event1", TimePointFromString("2026-10-20 14:24:00"), TimePointFromString("2022-10-20 15:36:00"));
        AddQuery(3, "Event2", TimePointFromString("2020-10-20 14:24:00"), TimePointFromString("2022-10-20 15:36:00"));
        AddQuery(3, "Event3", TimePointFromString("2025-10-20 14:24:00"), TimePointFromString("2022-10-20 15:36:00"));
        AddQuery(3, "Event4", TimePointFromString("2019-10-20 14:24:00"), TimePointFromString("2022-10-20 15:36:00"));
        auto count1     = getAllLimited(0, 2);
        auto count2     = getAllLimited(2, 5);
        auto &eventsTbl = eventsDb.events;
        auto count3     = eventsTbl.count();
        REQUIRE(*count1 == *count2);
        REQUIRE(*count2 == count3);
        EditQueryICS(testRecord.UID, testRecord);
        EditQueryICS(testRecord3.UID, testRecord3);
        EditQueryICS(testRecord6.UID, testRecord6);
    }

    [[maybe_unused]] auto selectFirstUpcomingEvent = [&](TimePoint filter_from, TimePoint filter_till) {
        auto query  = std::make_shared<db::query::events::SelectFirstUpcoming>(filter_from, filter_till);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::SelectFirstUpcomingResult *>(ret.get());
        REQUIRE(result != nullptr);
        auto records = result->getResult();
        return records;
    };

    SECTION("Select first upcoming via query")
    {
        auto date_from = TimePointFromString("2025-10-27 14:24:00");
        auto date_from = TimePointFromString("2019-10-19 14:24:00");
        auto date_till = TimePointFromString("2026-10-20 14:24:00");
        auto records   = selectFirstUpcomingEvent(date_from, date_till);
        REQUIRE(records->size() == 1);
        auto firstEvent = records->at(0);
        REQUIRE(firstEvent.ID == 7);
        REQUIRE(records.size() == 1);
        auto firstEvent = records.at(0);
        REQUIRE(firstEvent.ID == 2);

        date_from = TimePointFromString("2025-11-10 14:24:00");
        date_till = TimePointFromString("2026-10-20 14:24:00");
        records   = selectFirstUpcomingEvent(date_from, date_till);
        REQUIRE(records->size() == 0);
        REQUIRE(records.size() == 0);
    }

    Database::deinitialize();

M module-db/tests/EventsTable_tests.cpp => module-db/tests/EventsTable_tests.cpp +749 -264
@@ 5,7 5,6 @@

#include "Database/Database.hpp"
#include "Databases/EventsDB.hpp"

#include "Tables/EventsTable.hpp"

#include <vfs.hpp>


@@ 14,9 13,22 @@
#include <algorithm>
#include <iostream>
#include <purefs/filesystem_paths.hpp>
#include <unistd.h>

static auto remove_events(EventsDB &db) -> bool
{
    auto count   = db.events.count();
    auto records = db.events.getLimitOffset(0, count);
    bool ret     = true;
    for (auto rec : records) {
        ret = ret && db.events.removeById(rec.ID);
    }
    return ret;
}

TEST_CASE("Events Table tests")
{

    Database::initialize();

    const auto eventsPath = (purefs::dir::getUserDiskPath() / "events.db").c_str();


@@ 26,320 38,793 @@ TEST_CASE("Events Table tests")
    REQUIRE(eventsDb.isInitialized());

    auto &eventsTbl = eventsDb.events;

    if (eventsTbl.count() > 0) {
        REQUIRE(remove_events(eventsDb));
    }

    REQUIRE(eventsTbl.count() == 0);

    SECTION("Default Constructor")
    {
        EventsTableRow testRow;
        REQUIRE(testRow.ID == DB_ID_NONE);
        REQUIRE(testRow.UID == "");
        REQUIRE(testRow.title == "");
        REQUIRE(testRow.date_from == TIME_POINT_INVALID);
        REQUIRE(testRow.date_till == TIME_POINT_INVALID);
        REQUIRE(testRow.reminder == 0);
        REQUIRE(testRow.repeat == 0);
        REQUIRE(testRow.reminder_fired == TIME_POINT_INVALID);
        REQUIRE_FALSE(testRow.isValid());
        CHECK(testRow.ID == DB_ID_NONE);
        CHECK(testRow.UID == "");
        CHECK(testRow.title == "");
        CHECK(testRow.date_from == TIME_POINT_INVALID);
        CHECK(testRow.date_till == TIME_POINT_INVALID);
        CHECK(testRow.reminder == 0);
        CHECK(testRow.repeat == 0);
        CHECK(testRow.reminder_fired == TIME_POINT_INVALID);
        CHECK(testRow.provider_id == "");
        CHECK_FALSE(testRow.isValid());
    }

    REQUIRE(eventsTbl.add({{.ID = 0},
                           .UID            = "test",
                           .title          = "Event3",
                           .date_from      = TimePointFromString("2019-10-20 14:24:00"),
                           .date_till      = TimePointFromString("2019-10-20 15:36:00"),
                           .reminder       = 1,
                           .repeat         = 2,
                           .reminder_fired = TimePointFromString("2019-10-20 15:36:00")}));

    REQUIRE(eventsTbl.add({{.ID = 0},
                           .UID            = "test",
                           .title          = "Event4",
                           .date_from      = TimePointFromString("2021-10-20 12:24:00"),
                           .date_till      = TimePointFromString("2021-10-20 15:36:00"),
                           .reminder       = 0,
                           .repeat         = 3,
                           .reminder_fired = TimePointFromString("2019-10-20 15:36:00")}));

    REQUIRE(eventsTbl.count() == 2);

    SECTION("Get entry by ID")
    EventsTableRow testRow1 = {{1},
                               .UID              = "test1",
                               .title            = "Event1",
                               .date_from        = TimePointFromString("2019-10-20 14:25:00"),
                               .date_till        = TimePointFromString("2019-10-20 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TimePointFromString("2019-10-20 14:20:00"),
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    EventsTableRow testRow2 = {{2},
                               .UID              = "test2",
                               .title            = "Event2",
                               .date_from        = TimePointFromString("2019-10-21 14:24:00"),
                               .date_till        = TimePointFromString("2019-10-21 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TimePointFromString("2019-10-21 14:20:00"),
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    EventsTableRow testRow3 = {{3},
                               .UID              = "test3",
                               .title            = "Event3",
                               .date_from        = TimePointFromString("2019-10-22 14:25:00"),
                               .date_till        = TimePointFromString("2019-10-22 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TimePointFromString("2019-10-22 14:20:00"),
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    EventsTableRow testRow4 = {{4},
                               .UID              = "test4",
                               .title            = "Event4",
                               .date_from        = TimePointFromString("2019-10-23 14:25:00"),
                               .date_till        = TimePointFromString("2019-10-23 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TimePointFromString("2019-10-23 14:20:00"),
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    EventsTableRow testRow5 = {{5},
                               .UID              = "test5",
                               .title            = "Event5",
                               .date_from        = TimePointFromString("2019-10-24 14:25:00"),
                               .date_till        = TimePointFromString("2019-10-24 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TimePointFromString("2019-10-24 14:20:00"),
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    EventsTableRow testRow6 = {{6},
                               .UID              = "test6",
                               .title            = "Event6",
                               .date_from        = TimePointFromString("2019-10-24 14:25:00"),
                               .date_till        = TimePointFromString("2019-10-24 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TimePointFromString("2019-10-24 14:20:00"),
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    /// add
    CHECK(eventsTbl.add(testRow1));
    CHECK(eventsTbl.add(testRow2));
    CHECK(eventsTbl.add(testRow3));
    CHECK(eventsTbl.add(testRow4));
    CHECK(eventsTbl.add(testRow5));
    CHECK(eventsTbl.add(testRow6));
    CHECK(eventsTbl.count() == 6);

    auto check_tableRow = [](const EventsTableRow &actual, const EventsTableRow &expected) {
        CHECK(actual.ID == expected.ID);
        CHECK(actual.UID == expected.UID);
        CHECK(actual.title == expected.title);
        CHECK(TimePointToString(actual.date_from) == TimePointToString(expected.date_from));
        CHECK(TimePointToString(actual.date_till) == TimePointToString(expected.date_till));
        CHECK(actual.reminder == expected.reminder);
        CHECK(actual.repeat == expected.repeat);
        CHECK(actual.reminder_fired == expected.reminder_fired);
        CHECK(actual.provider_type == expected.provider_type);
        CHECK(actual.provider_id == expected.provider_id);
        CHECK(actual.provider_iCalUid == expected.provider_iCalUid);
        CHECK(actual.isValid());
    };

    SECTION("Check add daily")
    {
        auto entry = eventsTbl.getById(2);
        REQUIRE(entry.ID == 2);
        REQUIRE(entry.title == "Event4");
        REQUIRE(entry.date_from == TimePointFromString("2021-10-20 12:24:00"));
        REQUIRE(entry.date_till == TimePointFromString("2021-10-20 15:36:00"));
        REQUIRE(entry.reminder == 0);
        REQUIRE(entry.repeat == 3);
        REQUIRE(entry.isValid());
        if (eventsTbl.count() > 0) {
            REQUIRE(remove_events(eventsDb));
        }
        CHECK(eventsTbl.count() == 0);

        uint32_t numberOfEvents = 7;
        TimePoint startDate     = TimePointFromString("2019-10-20 14:30:00");
        TimePoint endDate       = TimePointFromString("2019-10-20 15:30:00");
        testRow1.date_from      = startDate;
        testRow1.date_till      = endDate;
        CHECK(eventsTbl.addDaily(testRow1));
        CHECK(eventsTbl.count() == numberOfEvents);

        auto entries = eventsTbl.selectByDatePeriod(
            TimePointFromString("2019-10-19 10:00:00"), TimePointFromString("2020-10-30 10:00:00"), 0, UINT32_MAX);
        CHECK(entries.size() == numberOfEvents);

        for (auto entry : entries) {
            CHECK(entry.title == testRow1.title);
            CHECK(TimePointToString(entry.date_from) == TimePointToString(startDate));
            CHECK(TimePointToString(entry.date_till) == TimePointToString(endDate));
            CHECK(entry.reminder == testRow1.reminder);
            CHECK(entry.repeat == testRow1.repeat);
            CHECK(entry.reminder_fired == testRow1.reminder_fired);
            CHECK(entry.provider_type == testRow1.provider_type);
            CHECK(entry.provider_id == testRow1.provider_id);
            CHECK(entry.provider_iCalUid == testRow1.provider_iCalUid);
            CHECK(entry.isValid());
            startDate += 24h;
            endDate += 24h;
        }
    }

    SECTION("Remove entry by ID")
    SECTION("Check add weekly")
    {
        auto response = eventsTbl.removeById(2);
        REQUIRE(response);
        REQUIRE_FALSE(eventsTbl.count() == 2);
        REQUIRE_NOTHROW(eventsTbl.getById(2));
        auto entry = eventsTbl.getById(1);
        REQUIRE(entry.ID == 1);
        REQUIRE(entry.title == "Event3");
        REQUIRE(entry.date_from == TimePointFromString("2019-10-20 14:24:00"));
        REQUIRE(entry.date_till == TimePointFromString("2019-10-20 15:36:00"));
        REQUIRE(entry.reminder == 1);
        REQUIRE(entry.repeat == 2);
        REQUIRE(entry.isValid());
        if (eventsTbl.count() > 0) {
            REQUIRE(remove_events(eventsDb));
        }
        CHECK(eventsTbl.count() == 0);

        uint32_t numberOfEvents = 4;
        TimePoint startDate     = TimePointFromString("2019-10-20 14:30:00");
        TimePoint endDate       = TimePointFromString("2019-10-20 15:30:00");
        testRow1.date_from      = startDate;
        testRow1.date_till      = endDate;
        CHECK(eventsTbl.addWeekly(testRow1));
        CHECK(eventsTbl.count() == numberOfEvents);

        auto entries = eventsTbl.selectByDatePeriod(
            TimePointFromString("2019-10-19 10:00:00"), TimePointFromString("2020-10-30 10:00:00"), 0, UINT32_MAX);
        CHECK(entries.size() == numberOfEvents);

        for (auto entry : entries) {
            CHECK(entry.title == testRow1.title);
            CHECK(TimePointToString(entry.date_from) == TimePointToString(startDate));
            CHECK(TimePointToString(entry.date_till) == TimePointToString(endDate));
            CHECK(entry.reminder == testRow1.reminder);
            CHECK(entry.repeat == testRow1.repeat);
            CHECK(entry.reminder_fired == testRow1.reminder_fired);
            CHECK(entry.provider_type == testRow1.provider_type);
            CHECK(entry.provider_id == testRow1.provider_id);
            CHECK(entry.provider_iCalUid == testRow1.provider_iCalUid);
            CHECK(entry.isValid());
            startDate += (24h * 7);
            endDate += (24h * 7);
        }
    }

    SECTION("Update entry by ID")
    SECTION("Check add biweekly")
    {
        auto entry = EventsTableRow({{.ID = 2},
                                     "test",
                                     "TestUpdateEvent",
                                     TimePointFromString("2019-10-20 15:00:00"),
                                     TimePointFromString("2019-10-20 18:54:00"),
                                     0,
                                     2,
                                     TIME_POINT_INVALID});
        REQUIRE(eventsTbl.update(entry));
        auto record = eventsTbl.getById(2);
        REQUIRE(record.ID == 2);
        REQUIRE(record.title == "TestUpdateEvent");
        REQUIRE(record.date_from == TimePointFromString("2019-10-20 15:00:00"));
        REQUIRE(record.date_till == TimePointFromString("2019-10-20 18:54:00"));
        REQUIRE(record.reminder == 0);
        REQUIRE(record.repeat == 2);
        REQUIRE(record.isValid());
        if (eventsTbl.count() > 0) {
            REQUIRE(remove_events(eventsDb));
        }
        CHECK(eventsTbl.count() == 0);

        uint32_t numberOfEvents = 4;
        TimePoint startDate     = TimePointFromString("2019-10-20 14:30:00");
        TimePoint endDate       = TimePointFromString("2019-10-20 15:30:00");
        testRow1.date_from      = startDate;
        testRow1.date_till      = endDate;
        CHECK(eventsTbl.addTwoWeeks(testRow1));
        CHECK(eventsTbl.count() == numberOfEvents);

        auto entries = eventsTbl.selectByDatePeriod(
            TimePointFromString("2019-10-19 10:00:00"), TimePointFromString("2020-10-30 10:00:00"), 0, UINT32_MAX);
        CHECK(entries.size() == numberOfEvents);

        for (auto entry : entries) {
            CHECK(entry.title == testRow1.title);
            CHECK(TimePointToString(entry.date_from) == TimePointToString(startDate));
            CHECK(TimePointToString(entry.date_till) == TimePointToString(endDate));
            CHECK(entry.reminder == testRow1.reminder);
            CHECK(entry.repeat == testRow1.repeat);
            CHECK(entry.reminder_fired == testRow1.reminder_fired);
            CHECK(entry.provider_type == testRow1.provider_type);
            CHECK(entry.provider_id == testRow1.provider_id);
            CHECK(entry.provider_iCalUid == testRow1.provider_iCalUid);
            CHECK(entry.isValid());
            startDate += (24h * 7 * 2);
            endDate += (24h * 7 * 2);
        }
    }

    SECTION("Select entry by date")
    SECTION("Check add monthly")
    {
        REQUIRE(eventsTbl.add({{.ID = 0},
                               .UID            = "test",
                               .title          = "Event5",
                               .date_from      = TimePointFromString("2019-10-20 14:24:00"),
                               .date_till      = TimePointFromString("2019-10-20 15:36:00"),
                               .reminder       = 1,
                               .repeat         = 2,
                               .reminder_fired = TimePointFromString("2019-10-20 15:36:00")}));
        REQUIRE(eventsTbl.add({{.ID = 1},
                               .UID            = "test",
                               .title          = "Event6",
                               .date_from      = TimePointFromString("2021-04-19 12:24:00"),
                               .date_till      = TimePointFromString("2021-04-20 15:36:00"),
                               .reminder       = 0,
                               .repeat         = 3,
                               .reminder_fired = TimePointFromString("2019-10-20 15:36:00")}));
        REQUIRE(eventsTbl.add({{.ID = 2},
                               .UID            = "test",
                               .title          = "Event7",
                               .date_from      = TimePointFromString("2019-10-20 15:24:00"),
                               .date_till      = TimePointFromString("2019-10-20 15:36:00"),
                               .reminder       = 1,
                               .repeat         = 2,
                               .reminder_fired = TimePointFromString("2019-10-20 15:36:00")}));
        REQUIRE(eventsTbl.add({{.ID = 3},
                               .UID            = "test",
                               .title          = "Event8",
                               .date_from      = TimePointFromString("2021-04-19 12:24:00"),
                               .date_till      = TimePointFromString("2019-10-20 15:36:00"),
                               .reminder       = 0,
                               .repeat         = 3,
                               .reminder_fired = TimePointFromString("2019-10-20 15:36:00")}));
        REQUIRE(eventsTbl.add({{.ID = 4},
                               .UID            = "test",
                               .title          = "Event9",
                               .date_from      = TimePointFromString("2025-10-20 15:24:00"),
                               .date_till      = TimePointFromString("2025-10-20 15:36:00"),
                               .reminder       = 1,
                               .repeat         = 2,
                               .reminder_fired = TimePointFromString("2019-10-20 15:36:00")}));
        REQUIRE(eventsTbl.add({{.ID = 5},
                               .UID            = "test",
                               .title          = "Event10",
                               .date_from      = TimePointFromString("2021-12-19 12:24:00"),
                               .date_till      = TimePointFromString("2021-12-20 15:36:00"),
                               .reminder       = 0,
                               .repeat         = 3,
                               .reminder_fired = TimePointFromString("2019-10-20 15:36:00")}));
        if (eventsTbl.count() > 0) {
            REQUIRE(remove_events(eventsDb));
        }
        CHECK(eventsTbl.count() == 0);

        uint32_t numberOfEvents = 12;
        const std::array<TimePoint, 24> dates{
            TimePointFromString("2019-01-20 14:30:00"), TimePointFromString("2019-01-20 15:30:00"),
            TimePointFromString("2019-02-20 14:30:00"), TimePointFromString("2019-02-20 15:30:00"),
            TimePointFromString("2019-03-20 14:30:00"), TimePointFromString("2019-03-20 15:30:00"),
            TimePointFromString("2019-04-20 14:30:00"), TimePointFromString("2019-04-20 15:30:00"),
            TimePointFromString("2019-05-20 14:30:00"), TimePointFromString("2019-05-20 15:30:00"),
            TimePointFromString("2019-06-20 14:30:00"), TimePointFromString("2019-06-20 15:30:00"),
            TimePointFromString("2019-07-20 14:30:00"), TimePointFromString("2019-07-20 15:30:00"),
            TimePointFromString("2019-08-20 14:30:00"), TimePointFromString("2019-08-20 15:30:00"),
            TimePointFromString("2019-09-20 14:30:00"), TimePointFromString("2019-09-20 15:30:00"),
            TimePointFromString("2019-10-20 14:30:00"), TimePointFromString("2019-10-20 15:30:00"),
            TimePointFromString("2019-11-20 14:30:00"), TimePointFromString("2019-11-20 15:30:00"),
            TimePointFromString("2019-12-20 14:30:00"), TimePointFromString("2019-12-20 15:30:00")};

        testRow1.date_from = dates[0];
        testRow1.date_till = dates[1];
        CHECK(eventsTbl.addMonth(testRow1));
        CHECK(eventsTbl.count() == numberOfEvents);

        auto entries = eventsTbl.selectByDatePeriod(
            TimePointFromString("2000-01-01 00:00:00"), TimePointFromString("2012-12-31 23:59:00"), 0, UINT32_MAX);
            TimePointFromString("1970-10-19 10:00:00"), TimePointFromString("2040-10-30 10:00:00"), 0, UINT32_MAX);
        CHECK(entries.size() == numberOfEvents);

        REQUIRE(entries.size() == 0);
    }
    for (uint32_t i = 1; i <= 8; ++i) {
        auto response = eventsTbl.removeById(i);
        REQUIRE(response);
        uint32_t index = 0;
        for (auto entry : entries) {
            CHECK(entry.title == testRow1.title);
            CHECK(TimePointToString(entry.date_from) == TimePointToString(dates[index]));
            CHECK(TimePointToString(entry.date_till) == TimePointToString(dates[index + 1]));
            CHECK(entry.reminder == testRow1.reminder);
            CHECK(entry.repeat == testRow1.repeat);
            CHECK(entry.reminder_fired == testRow1.reminder_fired);
            CHECK(entry.provider_type == testRow1.provider_type);
            CHECK(entry.provider_id == testRow1.provider_id);
            CHECK(entry.provider_iCalUid == testRow1.provider_iCalUid);
            CHECK(entry.isValid());
            index += 2;
        }
    }
    REQUIRE(eventsTbl.count() == 0);

    SECTION("Get all limited by date")
    SECTION("Check add yearly")
    {
        for (uint32_t i = 1; i <= 3; ++i) {
            auto response = eventsTbl.removeById(i);
            REQUIRE(response);
        if (eventsTbl.count() > 0) {
            REQUIRE(remove_events(eventsDb));
        }
        REQUIRE(eventsTbl.count() == 0);

        const std::array<TimePoint, 6> paramDate{TimePointFromString("2018-10-20 15:24"),
                                                 TimePointFromString("2019-10-30 14:24"),
                                                 TimePointFromString("2020-10-20 14:24"),
                                                 TimePointFromString("2021-12-20 14:24"),
                                                 TimePointFromString("2022-10-20 14:24"),
                                                 TimePointFromString("2023-10-20 14:24")};
        const std::array<uint32_t, 6> paramID{3, 4, 5, 6, 7, 8};
        const std::array<std::string, 6> paramName{"Event1", "Event2", "Event3", "Event4", "Event5", "Event6"};
        REQUIRE(eventsTbl.add({{.ID = 1},
                               .UID            = "test",
                               .title          = "Event1",
                               .date_from      = paramDate[0],
                               .date_till      = TimePointFromString("2030-10-20 15:24"),
                               .reminder       = 0,
                               .repeat         = 0,
                               .reminder_fired = TimePointFromString("2019-10-20 15:36:00")}));
        REQUIRE(eventsTbl.add({{.ID = 2},
                               .UID            = "test",
                               .title          = "Event2",
                               .date_from      = paramDate[5],
                               .date_till      = TimePointFromString("2030-10-20 15:24"),
                               .reminder       = 0,
                               .repeat         = 0,
                               .reminder_fired = TimePointFromString("2019-10-20 15:36:00")}));
        REQUIRE(eventsTbl.add({{.ID = 3},
                               .UID            = "test",
                               .title          = "Event3",
                               .date_from      = paramDate[2],
                               .date_till      = TimePointFromString("2030-10-20 15:24"),
                               .reminder       = 0,
                               .repeat         = 0,
                               .reminder_fired = TimePointFromString("2019-10-20 15:36:00")}));
        REQUIRE(eventsTbl.add({{.ID = 4},
                               .UID            = "test",
                               .title          = "Event4",
                               .date_from      = paramDate[3],
                               .date_till      = TimePointFromString("2030-10-20 15:24"),
                               .reminder       = 0,
                               .repeat         = 0,
                               .reminder_fired = TimePointFromString("2019-10-20 15:36:00")}));
        REQUIRE(eventsTbl.add({{.ID = 5},
                               .UID            = "test",
                               .title          = "Event5",
                               .date_from      = paramDate[4],
                               .date_till      = TimePointFromString("2030-10-20 15:24"),
                               .reminder       = 0,
                               .repeat         = 0,
                               .reminder_fired = TimePointFromString("2019-10-20 15:36:00")}));
        REQUIRE(eventsTbl.add({{.ID = 6},
                               .UID            = "test",
                               .title          = "Event6",
                               .date_from      = paramDate[1],
                               .date_till      = TimePointFromString("2030-10-20 15:24"),
                               .reminder       = 0,
                               .repeat         = 0,
                               .reminder_fired = TimePointFromString("2019-10-20 15:36:00")}));
        uint32_t numberOfEvents = 4;
        std::array<TimePoint, 8> dates{TimePointFromString("2019-02-20 14:30:00"),
                                       TimePointFromString("2019-02-20 15:30:00"),
                                       TimePointFromString("2020-02-20 14:30:00"),
                                       TimePointFromString("2020-02-20 15:30:00"),
                                       TimePointFromString("2021-02-20 14:30:00"),
                                       TimePointFromString("2021-02-20 15:30:00"),
                                       TimePointFromString("2022-02-20 14:30:00"),
                                       TimePointFromString("2022-02-20 15:30:00")};

        testRow1.date_from = dates[0];
        testRow1.date_till = dates[1];
        CHECK(eventsTbl.addYear(testRow1));
        CHECK(eventsTbl.count() == numberOfEvents);

        auto entries = eventsTbl.selectByDatePeriod(
            TimePointFromString("1970-10-19 10:00:00"), TimePointFromString("2045-10-30 10:00:00"), 0, UINT32_MAX);
        CHECK(entries.size() == numberOfEvents);

        auto entries = eventsTbl.getLimitOffsetByDate(0, 6);
        REQUIRE(entries.size() == 6);
        uint32_t index = 0;
        for (auto entry : entries) {
            REQUIRE(entry.ID == paramID[index]);
            REQUIRE(entry.title == paramName[index]);
            REQUIRE(entry.date_from == paramDate[index]);
            REQUIRE(entry.date_till == TimePointFromString("2030-10-20 15:24"));
            REQUIRE(entry.reminder == 0);
            REQUIRE(entry.repeat == 0);
            REQUIRE(entry.isValid());
            ++index;
            CHECK(entry.title == testRow1.title);
            CHECK(TimePointToString(entry.date_from) == TimePointToString(dates[index]));
            CHECK(TimePointToString(entry.date_till) == TimePointToString(dates[index + 1]));
            CHECK(entry.reminder == testRow1.reminder);
            CHECK(entry.repeat == testRow1.repeat);
            CHECK(entry.reminder_fired == testRow1.reminder_fired);
            CHECK(entry.provider_type == testRow1.provider_type);
            CHECK(entry.provider_id == testRow1.provider_id);
            CHECK(entry.provider_iCalUid == testRow1.provider_iCalUid);
            CHECK(entry.isValid());
            index += 2;
        }
    }

    enum class weekDayOption
    {
        monday    = 65536,
        tuesday   = 131072,
        wednesday = 262144,
        thursday  = 524288,
        friday    = 1048576,
        saturday  = 2097152,
        sunday    = 4194304,
    };

    SECTION("Check add custom")
    {
        auto check_custom_repeat = [&](uint32_t customRepeatOption,
                                       uint32_t numberOfEvents,
                                       TimePoint originalStartDate,
                                       TimePoint originalEndDate) {
            if (eventsTbl.count() > 0) {
                REQUIRE(remove_events(eventsDb));
            }
            CHECK(eventsTbl.count() == 0);

            const uint32_t numberOfWeeks = 4;
            std::vector<bool> weekDayOptions;
            weekDayOptions = eventsTbl.parseOptions(customRepeatOption);

            testRow1.repeat    = customRepeatOption;
            testRow1.date_from = originalStartDate;
            testRow1.date_till = originalEndDate;
            CHECK(eventsTbl.addCustom(testRow1));
            CHECK(eventsTbl.count() == numberOfEvents);

            auto entries = eventsTbl.selectByDatePeriod(TimePointFromString("2018-12-10 14:30:00"),
                                                        TimePointFromString("2029-12-10 14:30:00"),
                                                        0,
                                                        numberOfEvents);
            CHECK(entries.size() == numberOfEvents);
            uint32_t orginalEventID = UINT32_MAX;
            for (auto entry : entries) {
                if (orginalEventID > entry.ID) {
                    orginalEventID = entry.ID;
                }
            }

            TimePoint expectedStartDate = TimePointFromString("2020-12-07 14:30:00"); // monday
            TimePoint expectedEndDate   = TimePointFromString("2020-12-07 15:30:00"); // monday

            uint32_t i = 0;
            for (uint32_t l = 0; l < numberOfWeeks; l++) {
                for (uint32_t j = 0; j < weekDayOptions.size(); j++) {
                    if (entries[i].ID == orginalEventID) {
                        CHECK(entries[i].title == testRow1.title);
                        CHECK(TimePointToString(entries[i].date_from) == TimePointToString(originalStartDate));
                        CHECK(TimePointToString(entries[i].date_till) == TimePointToString(originalEndDate));
                        CHECK(entries[i].reminder == testRow1.reminder);
                        CHECK(entries[i].repeat == testRow1.repeat);
                        CHECK(entries[i].reminder_fired == testRow1.reminder_fired);
                        CHECK(entries[i].provider_type == testRow1.provider_type);
                        CHECK(entries[i].provider_id == testRow1.provider_id);
                        CHECK(entries[i].provider_iCalUid == testRow1.provider_iCalUid);
                        CHECK(entries[i].isValid());
                        i++;
                    }
                    else if (weekDayOptions[j]) {
                        CHECK(entries[i].title == testRow1.title);
                        CHECK(TimePointToString(entries[i].date_from) == TimePointToString(expectedStartDate));
                        CHECK(TimePointToString(entries[i].date_till) == TimePointToString(expectedEndDate));
                        CHECK(entries[i].reminder == testRow1.reminder);
                        CHECK(entries[i].repeat == testRow1.repeat);
                        CHECK(entries[i].reminder_fired == testRow1.reminder_fired);
                        CHECK(entries[i].provider_type == testRow1.provider_type);
                        CHECK(entries[i].provider_id == testRow1.provider_id);
                        CHECK(entries[i].provider_iCalUid == testRow1.provider_iCalUid);
                        CHECK(entries[i].isValid());
                        i++;
                    }
                    expectedStartDate += 24h;
                    expectedEndDate += 24h;
                }
            }
        };

        SECTION("Repeat Mondays and Wednsdays (original thursday)")
        {
            uint32_t customRepeatOption =
                static_cast<uint32_t>(weekDayOption::monday) + static_cast<uint32_t>(weekDayOption::wednesday);
            uint32_t numberOfEvents     = 9;
            TimePoint originalStartDate = TimePointFromString("2020-12-10 14:30:00"); // thursday
            TimePoint originalEndDate   = TimePointFromString("2020-12-10 15:30:00"); // thursday

            check_custom_repeat(customRepeatOption, numberOfEvents, originalStartDate, originalEndDate);
        }

        SECTION("Repeat Mondays, Tuesdays, Wednesdays, Sundays (original thursday)")
        {
            uint32_t customRepeatOption =
                static_cast<uint32_t>(weekDayOption::monday) + static_cast<uint32_t>(weekDayOption::wednesday) +
                static_cast<uint32_t>(weekDayOption::tuesday) + static_cast<uint32_t>(weekDayOption::sunday);
            uint32_t numberOfEvents     = 17;
            TimePoint originalStartDate = TimePointFromString("2020-12-10 14:30:00"); // thursday
            TimePoint originalEndDate   = TimePointFromString("2020-12-10 15:30:00"); // thursday

            check_custom_repeat(customRepeatOption, numberOfEvents, originalStartDate, originalEndDate);
        }

        SECTION("Repeat Saturdays (original thursday)")
        {
            uint32_t customRepeatOption = static_cast<uint32_t>(weekDayOption::saturday);
            uint32_t numberOfEvents     = 5;
            TimePoint originalStartDate = TimePointFromString("2020-12-10 14:30:00"); // thursday
            TimePoint originalEndDate   = TimePointFromString("2020-12-10 15:30:00"); // thursday

            check_custom_repeat(customRepeatOption, numberOfEvents, originalStartDate, originalEndDate);
        }
    }

    SECTION("Check count from filter")
    {
        TimePoint from = TimePointFromString("2019-10-20 14:30:00");
        TimePoint till = TimePointFromString("2019-10-24 14:20:00");

        CHECK(eventsTbl.countFromFilter(from, till) == 4);
    }

    SECTION("Check get by UID")
    {
        /// get
        auto entry = eventsTbl.getByUID(testRow2.UID);
        check_tableRow(entry, testRow2);
    }

    SECTION("Check Add entry and Get entry by ID")
    {
        CHECK(eventsTbl.count() == 6);

        /// get
        auto entry = eventsTbl.getById(testRow2.ID);
        check_tableRow(entry, testRow2);

        auto entry2 = eventsTbl.getById(testRow4.ID);
        check_tableRow(entry2, testRow4);
    }

    SECTION("Remove entry by ID")
    {
        CHECK(eventsTbl.count() == 6);
        CHECK(eventsTbl.removeById(testRow2.ID));
        CHECK(eventsTbl.count() == 5);
        CHECK_NOTHROW(eventsTbl.getById(testRow2.ID));
    }

    SECTION("Remove entry by UID")
    {
        CHECK(eventsTbl.count() == 6);
        CHECK(eventsTbl.removeByUID(testRow2.UID));
        CHECK(eventsTbl.count() == 5);
        CHECK_NOTHROW(eventsTbl.getById(testRow2.ID));
    }

    SECTION("Update entry by ID")
    {
        CHECK(eventsTbl.count() == 6);

        std::string newTitle = "Updated Title", newProviderID = "PurePhoneUpdated";
        TimePoint newDateFrom    = TimePointFromString("2020-10-20 15:00:00"),
                  newDateTill    = TimePointFromString("2020-10-20 16:00:00");
        uint32_t newReminder     = static_cast<uint32_t>(Reminder::one_week_before);
        uint32_t newRepeatOption = static_cast<uint32_t>(Repeat::biweekly);

        /// check title and provider id update
        auto entryToUpdate        = eventsTbl.getById(testRow6.ID);
        entryToUpdate.title       = newTitle;
        entryToUpdate.provider_id = newProviderID;
        CHECK(eventsTbl.update(entryToUpdate));

        auto entry = eventsTbl.getById(entryToUpdate.ID);
        check_tableRow(entry, entryToUpdate);

        CHECK(eventsTbl.count() == 6);

        /// check date update
        entryToUpdate           = eventsTbl.getById(testRow5.ID);
        entryToUpdate.date_from = newDateFrom;
        entryToUpdate.date_till = newDateTill;
        CHECK(eventsTbl.update(entryToUpdate));

        entry = eventsTbl.getById(entryToUpdate.ID);
        CHECK(entry.ID == entryToUpdate.ID);
        CHECK(entry.UID == entryToUpdate.UID);
        CHECK(entry.title == entryToUpdate.title);
        CHECK(TimePointToString(entry.date_from) == TimePointToString(newDateFrom));
        CHECK(TimePointToString(entry.date_till) == TimePointToString(newDateTill));
        CHECK(entry.reminder == entryToUpdate.reminder);
        CHECK(entry.repeat == entryToUpdate.repeat);
        CHECK(entry.reminder_fired == entryToUpdate.reminder_fired);
        CHECK(entry.provider_type == entryToUpdate.provider_type);
        CHECK(entry.provider_id == entryToUpdate.provider_id);
        CHECK(entry.provider_iCalUid == entryToUpdate.provider_iCalUid);
        CHECK(entry.isValid());

        CHECK(eventsTbl.count() == 6);

        /// check options update
        entryToUpdate          = eventsTbl.getById(testRow5.ID);
        entryToUpdate.repeat   = newRepeatOption;
        entryToUpdate.reminder = newReminder;
        CHECK(eventsTbl.update(entryToUpdate));

        entry = eventsTbl.getById(entryToUpdate.ID);
        check_tableRow(entry, entryToUpdate);

        CHECK(eventsTbl.count() == 6);
    }

    SECTION("Update entry by ID")
    {
        CHECK(eventsTbl.count() == 6);

        std::string newTitle = "Updated Title", newProviderType = "PurePhoneUpdate", newProviderID = "newID",
                    newProvideriCalUid = "new iCalUid";
        TimePoint newDateFrom          = TimePointFromString("2020-10-20 15:00:00"),
                  newDateTill          = TimePointFromString("2020-10-20 16:00:00");
        uint32_t newReminder           = static_cast<uint32_t>(Reminder::one_week_before);
        uint32_t newRepeatOption       = static_cast<uint32_t>(Repeat::biweekly);

        /// check title and provider id update
        auto entryToUpdate             = eventsTbl.getById(testRow6.ID);
        entryToUpdate.title            = newTitle;
        entryToUpdate.provider_type    = newProviderType;
        entryToUpdate.provider_id      = newProviderID;
        entryToUpdate.provider_iCalUid = newProvideriCalUid;
        CHECK(eventsTbl.update(entryToUpdate));

        auto entry = eventsTbl.getById(entryToUpdate.ID);
        check_tableRow(entry, entryToUpdate);

        CHECK(eventsTbl.count() == 6);

        /// check date update
        entryToUpdate           = eventsTbl.getById(testRow5.ID);
        entryToUpdate.date_from = newDateFrom;
        entryToUpdate.date_till = newDateTill;
        CHECK(eventsTbl.update(entryToUpdate));

        entry = eventsTbl.getById(entryToUpdate.ID);
        CHECK(entry.ID == entryToUpdate.ID);
        CHECK(entry.UID == entryToUpdate.UID);
        CHECK(entry.title == entryToUpdate.title);
        CHECK(TimePointToString(entry.date_from) == TimePointToString(newDateFrom));
        CHECK(TimePointToString(entry.date_till) == TimePointToString(newDateTill));
        CHECK(entry.reminder == entryToUpdate.reminder);
        CHECK(entry.repeat == entryToUpdate.repeat);
        CHECK(entry.reminder_fired == entryToUpdate.reminder_fired);
        CHECK(entry.provider_type == entryToUpdate.provider_type);
        CHECK(entry.provider_id == entryToUpdate.provider_id);
        CHECK(entry.provider_iCalUid == entryToUpdate.provider_iCalUid);
        CHECK(entry.isValid());

        CHECK(eventsTbl.count() == 6);

        /// check options update
        entryToUpdate          = eventsTbl.getById(testRow5.ID);
        entryToUpdate.repeat   = newRepeatOption;
        entryToUpdate.reminder = newReminder;
        CHECK(eventsTbl.update(entryToUpdate));

        entry = eventsTbl.getById(entryToUpdate.ID);
        check_tableRow(entry, entryToUpdate);

        CHECK(eventsTbl.count() == 6);
    }

    SECTION("Update entry by UID")
    {
        CHECK(eventsTbl.count() == 6);

        std::string newTitle = "Updated Title", newProviderType = "PurePhoneUpdate", newProviderID = "newID",
                    newProvideriCalUid = "new iCalUid";
        TimePoint newDateFrom          = TimePointFromString("2020-10-20 15:00:00"),
                  newDateTill          = TimePointFromString("2020-10-20 16:00:00");
        uint32_t newReminder           = static_cast<uint32_t>(Reminder::one_week_before);
        uint32_t newRepeatOption       = static_cast<uint32_t>(Repeat::biweekly);

        /// check title and provider id update
        auto entryToUpdate             = eventsTbl.getByUID(testRow6.UID);
        entryToUpdate.title            = newTitle;
        entryToUpdate.provider_type    = newProviderType;
        entryToUpdate.provider_id      = newProviderID;
        entryToUpdate.provider_iCalUid = newProvideriCalUid;
        CHECK(eventsTbl.updateByUID(entryToUpdate));

        auto entry = eventsTbl.getById(entryToUpdate.ID);
        check_tableRow(entry, entryToUpdate);
        CHECK(entry.isValid());

        CHECK(eventsTbl.count() == 6);

        /// check date update
        entryToUpdate           = eventsTbl.getByUID(testRow5.UID);
        entryToUpdate.date_from = newDateFrom;
        entryToUpdate.date_till = newDateTill;
        CHECK(eventsTbl.updateByUID(entryToUpdate));

        entry = eventsTbl.getById(entryToUpdate.ID);
        CHECK(entry.ID == entryToUpdate.ID);
        CHECK(entry.UID == entryToUpdate.UID);
        CHECK(entry.title == entryToUpdate.title);
        CHECK(entry.date_from == newDateFrom);
        CHECK(TimePointToString(entry.date_from) == "2020-10-20 15:00:00");
        CHECK(entry.date_till == newDateTill);
        CHECK(TimePointToString(entry.date_till) == "2020-10-20 16:00:00");
        CHECK(entry.reminder == entryToUpdate.reminder);
        CHECK(entry.repeat == entryToUpdate.repeat);
        CHECK(entry.reminder_fired == entryToUpdate.reminder_fired);
        CHECK(entry.provider_type == entryToUpdate.provider_type);
        CHECK(entry.provider_id == entryToUpdate.provider_id);
        CHECK(entry.provider_iCalUid == entryToUpdate.provider_iCalUid);
        CHECK(entry.isValid());

        CHECK(eventsTbl.count() == 6);

        /// check options update
        entryToUpdate          = eventsTbl.getByUID(testRow5.UID);
        entryToUpdate.repeat   = newRepeatOption;
        entryToUpdate.reminder = newReminder;
        CHECK(eventsTbl.updateByUID(entryToUpdate));

        entry = eventsTbl.getById(entryToUpdate.ID);
        check_tableRow(entry, entryToUpdate);

        CHECK(eventsTbl.count() == 6);
    }

    SECTION("Select entry by date with limit")
    {
        auto entries = eventsTbl.selectByDatePeriod(testRow1.date_from, testRow6.date_from, 0, 3);
        REQUIRE(entries.size() == 3);
        check_tableRow(entries[0], testRow1);

        check_tableRow(entries[1], testRow2);

        check_tableRow(entries[2], testRow3);
    }

    SECTION("Select entry by date max")
    {
        auto entries = eventsTbl.selectByDatePeriod(
            TimePointFromString("2019-10-21 10:00:00"), TimePointFromString("2019-10-24 10:00:00"), 0, UINT32_MAX);
        CHECK(entries.size() == 3);

        check_tableRow(entries[0], testRow2);

        check_tableRow(entries[1], testRow3);

        check_tableRow(entries[2], testRow4);
    }

    SECTION("Select entry by date invalid")
    {
        auto entries = eventsTbl.selectByDatePeriod(
            TimePointFromString("2000-01-01 00:00:00"), TimePointFromString("2012-12-31 23:59:00"), 0, UINT32_MAX);
        CHECK(entries.size() == 0);
    }

    SECTION("Get all limited by date")
    {
        auto entries = eventsTbl.getLimitOffsetByDate(0, 6);
        CHECK(entries.size() == 6);
    }

    SECTION("Select first upcoming event")
    {
        for (uint32_t i = 1; i <= 10; ++i) {
            auto response = eventsTbl.removeById(i);
            REQUIRE(response);
        if (eventsTbl.count() > 0) {
            REQUIRE(remove_events(eventsDb));
        }
        REQUIRE(eventsTbl.count() == 0);
        CHECK(eventsTbl.count() == 0);

        const std::array<TimePoint, 6> paramDate{TimePointFromString("2018-10-20 15:24:00"),
                                                 TimePointFromString("2019-10-30 14:24:00"),
                                                 TimePointFromString("2020-10-20 14:24:00"),
                                                 TimePointFromString("2021-12-20 14:24:00"),
                                                 TimePointFromString("2022-10-20 14:24:00"),
                                                 TimePointFromString("2023-10-20 14:24:00")};
        TimePoint startDate1 = TimePointFromString("2018-10-20 14:24:00");
        TimePoint startDate2 = TimePointFromString("2020-10-20 14:24:00");

        TimePoint tillDate  = TimePointFromString("2030-10-20 15:24:00");
        TimePoint firedDate = TimePointFromString("2018-10-20 14:24:00");

        REQUIRE(eventsTbl.add({{.ID = 1},
                               .UID            = "test",
                               .title          = "Event1",
                               .date_from      = paramDate[0],
                               .date_till      = tillDate,
                               .reminder       = 120,
                               .repeat         = 0,
                               .reminder_fired = TIME_POINT_INVALID}));
        REQUIRE(eventsTbl.add({{.ID = 2},
                               .UID            = "test",
                               .title          = "Event2",
                               .date_from      = paramDate[1],
                               .date_till      = tillDate,
                               .reminder       = 120,
                               .repeat         = 0,
                               .reminder_fired = TIME_POINT_INVALID}));
        REQUIRE(eventsTbl.add({{.ID = 3},
                               .UID            = "test",
                               .title          = "Event3",
                               .date_from      = paramDate[2],
                               .date_till      = tillDate,
                               .reminder       = 120,
                               .repeat         = 0,
                               .reminder_fired = firedDate}));
        REQUIRE(eventsTbl.add({{.ID = 4},
                               .UID            = "test",
                               .title          = "Event4",
                               .date_from      = paramDate[3],
                               .date_till      = tillDate,
                               .reminder       = 120,
                               .repeat         = 0,
                               .reminder_fired = firedDate}));
        REQUIRE(eventsTbl.add({{.ID = 5},
                               .UID            = "test",
                               .title          = "Event5",
                               .date_from      = paramDate[4],
                               .date_till      = tillDate,
                               .reminder       = 120,
                               .repeat         = 0,
                               .reminder_fired = TIME_POINT_INVALID}));
        REQUIRE(eventsTbl.add({{.ID = 6},
                               .UID            = "test",
                               .title          = "Event6",
                               .date_from      = paramDate[5],
                               .date_till      = tillDate,
                               .reminder       = 120,
                               .repeat         = 0,
                               .reminder_fired = TIME_POINT_INVALID}));
        EventsTableRow testEvent1 = {{1},
                                     .UID              = "test1",
                                     .title            = "Event1",
                                     .date_from        = TimePointFromString("2017-10-19 14:24:00"),
                                     .date_till        = tillDate,
                                     .reminder         = 120,
                                     .repeat           = 0,
                                     .reminder_fired   = TIME_POINT_INVALID,
                                     .provider_type    = "PurePhone",
                                     .provider_id      = "testID",
                                     .provider_iCalUid = "test6"};

        EventsTableRow testEvent2 = {{2},
                                     .UID              = "test2",
                                     .title            = "Event2",
                                     .date_from        = TimePointFromString("2019-10-30 14:24:00"),
                                     .date_till        = tillDate,
                                     .reminder         = 120,
                                     .repeat           = 0,
                                     .reminder_fired   = TIME_POINT_INVALID,
                                     .provider_type    = "PurePhone",
                                     .provider_id      = "testID",
                                     .provider_iCalUid = "test6"};

        EventsTableRow testEvent3 = {{3},
                                     .UID              = "test3",
                                     .title            = "Event3",
                                     .date_from        = TimePointFromString("2020-10-20 14:24:00"),
                                     .date_till        = tillDate,
                                     .reminder         = 120,
                                     .repeat           = 0,
                                     .reminder_fired   = firedDate,
                                     .provider_type    = "PurePhone",
                                     .provider_id      = "testID",
                                     .provider_iCalUid = "test6"};

        EventsTableRow testEvent4 = {{4},
                                     .UID              = "test4",
                                     .title            = "Event4",
                                     .date_from        = TimePointFromString("2021-12-20 14:24:00"),
                                     .date_till        = tillDate,
                                     .reminder         = 120,
                                     .repeat           = 0,
                                     .reminder_fired   = firedDate,
                                     .provider_type    = "PurePhone",
                                     .provider_id      = "testID",
                                     .provider_iCalUid = "test6"};

        EventsTableRow testEvent5 = {{5},
                                     .UID              = "test5",
                                     .title            = "Event5",
                                     .date_from        = TimePointFromString("2022-10-20 14:24:00"),
                                     .date_till        = tillDate,
                                     .reminder         = 120,
                                     .repeat           = 0,
                                     .reminder_fired   = TIME_POINT_INVALID,
                                     .provider_type    = "PurePhone",
                                     .provider_id      = "testID",
                                     .provider_iCalUid = "test6"};

        EventsTableRow testEvent6 = {{6},
                                     .UID              = "test6",
                                     .title            = "Event6",
                                     .date_from        = TimePointFromString("2023-10-20 14:24:00"),
                                     .date_till        = tillDate,
                                     .reminder         = 120,
                                     .repeat           = 0,
                                     .reminder_fired   = TIME_POINT_INVALID,
                                     .provider_type    = "PurePhone",
                                     .provider_id      = "testID",
                                     .provider_iCalUid = "test6"};

        CHECK(eventsTbl.add(testEvent1));
        CHECK(eventsTbl.add(testEvent2));
        CHECK(eventsTbl.add(testEvent3));
        CHECK(eventsTbl.add(testEvent4));
        CHECK(eventsTbl.add(testEvent5));
        CHECK(eventsTbl.add(testEvent6));

        auto entries = eventsTbl.SelectFirstUpcoming(startDate1, tillDate);
        REQUIRE(entries.size() == 1);
        CHECK(entries.size() == 1);
        for (auto entry : entries) {
            REQUIRE(entry.title == "Event2");
            CHECK(entry.title == "Event2");
        }

        entries = eventsTbl.SelectFirstUpcoming(startDate2, tillDate);
        REQUIRE(entries.size() == 1);
        CHECK(entries.size() == 1);
        for (auto entry : entries) {
            REQUIRE(entry.title == "Event5");
            CHECK(entry.title == "Event5");
        }

        for (uint32_t i = 1; i <= 10; ++i) {
            auto response = eventsTbl.removeById(i);
            REQUIRE(response);
        }
        REQUIRE(eventsTbl.count() == 0);
    }

    eventsTbl.drop();
    Database::deinitialize();
}

M module-services/service-desktop/endpoints/calendarEvents/CalendarEventsHelper.cpp => module-services/service-desktop/endpoints/calendarEvents/CalendarEventsHelper.cpp +31 -13
@@ 39,12 39,21 @@ namespace parserFSM

    namespace json::calendar::events
    {
        constexpr inline auto UID    = "UID";
        constexpr inline auto data   = "data";
        constexpr inline auto offset = "offset";
        constexpr inline auto limit  = "limit";
        constexpr inline auto count  = "count";
    } // namespace json::calendar::events
        constexpr inline auto UID         = "UID";
        constexpr inline auto data        = "data";
        constexpr inline auto offset      = "offset";
        constexpr inline auto limit       = "limit";
        constexpr inline auto count       = "count";
        constexpr inline auto total_count = "total_count";

        constexpr inline auto providers = "providers";
        namespace provider
        {
            constexpr inline auto type    = "provider_type";
            constexpr inline auto id      = "provider_id";
            constexpr inline auto iCalUid = "provider_iCalUid";
        } // namespace provider
    }     // namespace json::calendar::events
} // namespace parserFSM
using namespace parserFSM;



@@ 153,16 162,18 @@ auto CalendarEventsHelper::requestDataFromDB(Context &context) -> sys::ReturnCod
    auto listener = std::make_unique<db::EndpointListener>(
        [&](db::QueryResult *result, Context context) {
            if (auto EventsResult = dynamic_cast<db::query::events::GetAllLimitedResult *>(result)) {
                auto records = EventsResult->getResult();
                assert(records != nullptr);
                auto parser = std::make_unique<ParserICS>();
                auto records        = EventsResult->getResult();
                uint32_t totalCount = EventsResult->getCountResult();
                auto parser         = std::make_unique<ParserICS>();
                std::vector<ICalEvent> icalEvents;
                for (auto rec : *records) {
                for (auto rec : records) {
                    icalEvents.push_back(icalEventFrom(rec));
                }
                parser->importEvents(icalEvents);
                auto jsonObj = json11::Json::object({{json::calendar::events::data, parser->getIcsData()},
                                                     {json::calendar::events::count, std::to_string(records->size())}});
                auto jsonObj =
                    json11::Json::object({{json::calendar::events::data, parser->getIcsData()},
                                          {json::calendar::events::count, std::to_string(records.size())},
                                          {json::calendar::events::total_count, std::to_string(totalCount)}});

                context.setResponseBody(jsonObj);
                MessageHandler::putToSendQueue(context.createSimpleResponse());


@@ 240,20 251,27 @@ auto CalendarEventsHelper::eventsRecordFrom(ICalEvent &icalEvent) const -> Event

auto CalendarEventsHelper::createDBEntry(Context &context) -> sys::ReturnCodes
{
    auto parser = std::make_unique<ParserICS>();
    auto parser = std::make_shared<ParserICS>();
    parser->loadData(context.getBody()[json::calendar::events::data].string_value());
    auto icalEvents = parser->exportEvents();

    bool ret = true;
    for (auto event : icalEvents) {
        auto UID    = event.event.getUID();
        auto record = eventsRecordFrom(event);
        auto query  = std::make_unique<db::query::events::Add>(record);

        auto listener = std::make_unique<db::EndpointListener>(
            [=](db::QueryResult *result, Context context) {
                if (auto EventResult = dynamic_cast<db::query::events::AddResult *>(result)) {

                    auto jsonObj = json11::Json::object(
                        {{json::calendar::events::data, parser->getIcsData()}, {json::calendar::events::UID, UID}});

                    context.setResponseBody(jsonObj);
                    context.setResponseStatus(EventResult->getResult() ? http::Code::OK
                                                                       : http::Code::InternalServerError);

                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }

M module-services/service-time/timeEvents/CalendarTimeEvents.cpp => module-services/service-time/timeEvents/CalendarTimeEvents.cpp +3 -3
@@ 56,12 56,12 @@ namespace stm
            return 0;
        }

        std::unique_ptr<std::vector<EventsRecord>> records = firstUpcomingQuery->getResult();
        if (records->size() == 0) {
        std::vector<EventsRecord> records = firstUpcomingQuery->getResult();
        if (records.size() == 0) {
            return 0;
        }

        eventRecord   = records->at(0);
        eventRecord   = records.at(0);
        startTP       = eventRecord.date_from - minutes{eventRecord.reminder};
        auto duration = eventRecord.date_from - std::chrono::minutes{eventRecord.reminder} - TimePointNow();
        if (duration.count() <= 0) {