From b15cddd562760c4bee28534a75a0523efdaad03e Mon Sep 17 00:00:00 2001 From: Thomas Lauf Date: Thu, 30 May 2019 14:49:39 +0200 Subject: [PATCH] Create tag database from existing interval database on startup - Closes #224 Signed-off-by: Thomas Lauf --- src/Database.cpp | 41 ++++++++++++++++++++++++++++++----------- test/cli.t | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/src/Database.cpp b/src/Database.cpp index 4d9b3333..689e0c11 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -375,22 +375,41 @@ void Database::initializeTagDatabase () if (!File::read (_location + "/tags.data", content)) { - return; - } + auto intervals = getAllInclusions (*this); + + if (intervals.empty ()) + { + return; + } - auto* json = (json::object*) json::parse (content); + std::cout << "Tag info database does not exist. Recreating from interval data..." << std::endl ; - for (auto& pair : json->_data) + for (auto& interval : intervals) + { + for (auto& tag : interval.tags ()) + { + _tagInfoDatabase.incrementTag (tag); + } + } + } + else { - auto key = pair.first; - auto* value = (json::object*) pair.second; - auto iter = value->_data.find ("count"); - if (iter == value->_data.end ()) + auto *json = (json::object *) json::parse (content); + + for (auto &pair : json->_data) { - throw format ("Failed to find \"count\" member for tag \"{1}\" in tags database. Database corrupted?", key); + auto key = pair.first; + auto *value = (json::object *) pair.second; + auto iter = value->_data.find ("count"); + + if (iter == value->_data.end ()) + { + throw format ("Failed to find \"count\" member for tag \"{1}\" in tags database. Database corrupted?", key); + } + + auto number = dynamic_cast (iter->second); + _tagInfoDatabase.add (key, TagInfo{(unsigned int) number->_dvalue}); } - auto number = dynamic_cast (iter->second); - _tagInfoDatabase.add (key, TagInfo {(unsigned int) number->_dvalue}); } } diff --git a/test/cli.t b/test/cli.t index 9036459f..8818f3c3 100755 --- a/test/cli.t +++ b/test/cli.t @@ -30,6 +30,9 @@ import os import shutil import sys import unittest +import json + +from datetime import datetime, timedelta # Ensure python finds the local simpletap module sys.path.append(os.path.dirname(os.path.abspath(__file__))) @@ -52,6 +55,37 @@ class TestCLI(TestCase): assert os.path.isdir(self.t.env["TIMEWARRIORDB"]) assert os.path.exists(self.t.env["TIMEWARRIORDB"]) + assert os.path.exists(os.path.join(self.t.env["TIMEWARRIORDB"], "data")) + assert os.path.exists(os.path.join(self.t.env["TIMEWARRIORDB"], "data", "tags.data")) + assert not os.path.exists(os.path.join(self.t.env["TIMEWARRIORDB"], "data", "undo.data")) + + def test_tag_database_is_recreated(self): + """Verify that calling 'timew' recreates tag database""" + now_utc = datetime.now().utcnow() + + four_hours_before_utc = now_utc - timedelta(hours=4) + three_hours_before_utc = now_utc - timedelta(hours=3) + two_hours_before_utc = now_utc - timedelta(hours=2) + one_hour_before_utc = now_utc - timedelta(hours=1) + + self.t("track {:%Y-%m-%dT%H:%M:%S} - {:%Y-%m-%dT%H:%M:%S} FOO".format(four_hours_before_utc, three_hours_before_utc)) + self.t("track {:%Y-%m-%dT%H:%M:%S} - {:%Y-%m-%dT%H:%M:%S} BAR".format(three_hours_before_utc, two_hours_before_utc)) + self.t("track {:%Y-%m-%dT%H:%M:%S} - {:%Y-%m-%dT%H:%M:%S} FOO".format(two_hours_before_utc, one_hour_before_utc)) + + os.remove(os.path.join(self.t.env["TIMEWARRIORDB"], "data", "tags.data")) + + assert not os.path.exists(os.path.join(self.t.env["TIMEWARRIORDB"], "data", "tags.data")) + + self.t.runError("") + + assert os.path.exists(os.path.join(self.t.env["TIMEWARRIORDB"], "data", "tags.data")) + + with open(os.path.join(self.t.env["TIMEWARRIORDB"], "data", "tags.data")) as f: + data = json.load(f) + self.assertIn("FOO", data) + self.assertEqual(data["FOO"]["count"], 2) + self.assertIn("BAR", data) + self.assertEqual(data["BAR"]["count"], 1) def test_TimeWarrior_without_command_without_active_time_tracking(self): """Call 'timew' without active time tracking"""