From 704eb080917c0bdedab4ed222511405db6b3c1f5 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Fri, 3 Nov 2023 13:28:25 -0400 Subject: [PATCH] gtk: Add a CalendarView class for selecting xfstests runs by date This combines a Calendar and RunidView to select runs on a given day. Signed-off-by: Anna Schumaker --- tests/gtk/test_sidebar.py | 82 +++++++++++++++++++++++++++++++++++++++ xfstestsdb/gtk/sidebar.py | 51 ++++++++++++++++++++++++ 2 files changed, 133 insertions(+) diff --git a/tests/gtk/test_sidebar.py b/tests/gtk/test_sidebar.py index 320bdeb..45abfd8 100644 --- a/tests/gtk/test_sidebar.py +++ b/tests/gtk/test_sidebar.py @@ -3,6 +3,7 @@ import datetime import unittest import xfstestsdb.gtk.sidebar +from gi.repository import GLib from gi.repository import Gtk @@ -84,3 +85,84 @@ class TestRunidView(unittest.TestCase): self.view._view.emit("activate", 0) self.assertTrue(self.view._selection[0].get_expanded()) self.assertEqual(self.view.runid, 2) + + +class TestCalendarView(unittest.TestCase): + """Test the CalendarView class.""" + + def setUp(self): + """Set up common variables.""" + self.xfstestsdb = xfstestsdb.Command() + with unittest.mock.patch("sys.stdout"): + self.xfstestsdb.run(["new", "/dev/vda1"]) + self.xfstestsdb.run(["new", "/dev/vda2"]) + self.xfstestsdb.run(["new", "/dev/vda3"]) + self.xfstestsdb.run(["new", "/dev/vda4"]) + + query = "UPDATE xfstests_runs SET timestamp=? WHERE runid=?" + dtime = datetime.datetime.now().replace(year=2023, month=1) + self.xfstestsdb.sql(query, dtime.replace(day=1), 1) + self.xfstestsdb.sql(query, dtime.replace(day=8), 2) + self.xfstestsdb.sql(query, dtime.replace(day=15), 3) + + self.sidebar = xfstestsdb.gtk.sidebar.CalendarView(self.xfstestsdb.sql) + + def test_init(self): + """Test that the calendar view was set up properly.""" + self.assertIsInstance(self.sidebar, Gtk.Box) + self.assertEqual(self.sidebar.props.spacing, 6) + self.assertEqual(self.sidebar.props.orientation, + Gtk.Orientation.VERTICAL) + self.assertEqual(self.sidebar.sql, self.xfstestsdb.sql) + + def test_calendar(self): + """Test the calendar widget.""" + self.assertIsInstance(self.sidebar._calendar, Gtk.Calendar) + self.assertEqual(self.sidebar.get_first_child(), + self.sidebar._calendar) + + today = datetime.date.today() + self.assertTrue(self.sidebar._calendar.get_day_is_marked(today.day)) + + def test_view(self): + """Test the runid view widget.""" + self.assertIsInstance(self.sidebar._view, + xfstestsdb.gtk.sidebar.RunidView) + self.assertIsInstance(self.sidebar._view.model, + xfstestsdb.gtk.tree.DateDeviceList) + self.assertEqual(self.sidebar._calendar.get_next_sibling(), + self.sidebar._view) + + self.assertEqual(self.sidebar._view.model.date, datetime.date.today()) + + def test_marked_days(self): + """Test marking days in the calendar.""" + gl_date = GLib.DateTime.new_local(2023, 1, 10, 0, 0, 0) + self.sidebar._calendar.select_day(gl_date) + + for signal in ["next-month", "next-year", "prev-month", "prev-year"]: + with self.subTest(signal=signal): + with unittest.mock.patch.object(self.sidebar._calendar, + "clear_marks") as mock_clear: + with unittest.mock.patch.object(self.sidebar._calendar, + "mark_day") as mock_mark: + self.sidebar._calendar.emit(signal) + mock_clear.assert_called() + mock_mark.assert_has_calls([unittest.mock.call(1), + unittest.mock.call(8), + unittest.mock.call(15)]) + + def test_select_day(self): + """Test selecting a day in the calendar.""" + gl_now = GLib.DateTime.new_now_local() + self.sidebar._calendar.select_day(gl_now.add_days(-1)) + + today = datetime.date.today() + self.assertEqual(self.sidebar._view.model.date, + today - datetime.timedelta(days=1)) + + def test_runid_property(self): + """Test the runid property.""" + self.assertEqual(self.sidebar.runid, 0) + self.sidebar._view.runid = 42 + self.assertEqual(self.sidebar.runid, 42) diff --git a/xfstestsdb/gtk/sidebar.py b/xfstestsdb/gtk/sidebar.py index 91efa30..daebff5 100644 --- a/xfstestsdb/gtk/sidebar.py +++ b/xfstestsdb/gtk/sidebar.py @@ -1,9 +1,11 @@ # Copyright 2023 (c) Anna Schumaker. """Our sidebar for selecting a specific xfstests run to view.""" +import datetime import typing from gi.repository import GObject from gi.repository import Gio from gi.repository import Gtk +from .. import sqlite from . import row from . import tree @@ -44,3 +46,52 @@ class RunidView(Gtk.ScrolledWindow): param: GObject.ParamSpec) -> None: model = None if self.model is None else self.model.treemodel self._selection.props.model = model + + +class CalendarView(Gtk.Box): + """Our calendar view for seleting an xfstests run by date.""" + + runid = GObject.Property(type=int) + sql = GObject.Property(type=GObject.TYPE_PYOBJECT) + + def __init__(self, sql: sqlite.Connection) -> None: + """Initialize a CalendarView instance.""" + super().__init__(sql=sql, spacing=6, + orientation=Gtk.Orientation.VERTICAL) + today = datetime.date.today() + self._calendar = Gtk.Calendar() + self._view = RunidView(model=tree.DateDeviceList(sql, today)) + + self._view.bind_property("runid", self, "runid") + self._calendar.connect("day-selected", self.__day_selected) + self._calendar.connect("next-month", self.__date_changed) + self._calendar.connect("next-year", self.__date_changed) + self._calendar.connect("prev-month", self.__date_changed) + self._calendar.connect("prev-year", self.__date_changed) + + self.__mark_days(today) + + self.append(self._calendar) + self.append(self._view) + + def __day_selected(self, calendar: Gtk.Calendar) -> None: + self.__select_day(datetime.date(*calendar.get_date().get_ymd())) + + def __date_changed(self, calendar: Gtk.Calendar) -> None: + date = datetime.date(*calendar.get_date().get_ymd()) + self.__mark_days(date) + self.__select_day(date) + + def __select_day(self, date: datetime.date) -> None: + self._view.model = tree.DateDeviceList(self.sql, date) + + def __mark_days(self, date: datetime.date) -> None: + min = datetime.datetime.combine(date.replace(day=1), datetime.time()) + max = (min + datetime.timedelta(days=40)).replace(day=1) + + self._calendar.clear_marks() + for stamp in self.sql("""SELECT DISTINCT timestamp FROM tagged_runs + WHERE timestamp >= ? AND timestamp < ?""", + min, max).fetchall(): + ts = datetime.datetime.fromisoformat(stamp['timestamp']) + self._calendar.mark_day(ts.day)