# 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 class RunidView(Gtk.ScrolledWindow): """Our RunidView class.""" runid = GObject.Property(type=int) model = GObject.Property(type=Gio.ListModel) def __init__(self, model: tree.DateDeviceList) -> None: """Initialize a RunidView instance.""" super().__init__(model=model, vexpand=True) self._selection = Gtk.SingleSelection(model=model.treemodel) self._view = Gtk.ListView(model=self._selection, factory=row.SidebarFactory(), single_click_activate=True) self._view.connect("activate", self.__activate) self.connect("notify::model", self.__notify_model) self._view.add_css_class("navigation-sidebar") self._view.add_css_class("background") self.set_child(self._view) def __activate(self, view: Gtk.ListView, position: int) -> None: treeitem = self._selection[position] item = treeitem.get_item() if isinstance(item, tree.XfstestsRun): self.runid = item.runid elif treeitem.props.expanded: treeitem.props.expanded = False else: treeitem.props.expanded = True self.runid = item.get_earliest_run().runid def __notify_model(self, sidebar: typing.Self, 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) class Sidebar(Gtk.Box): """Our sidebar for seleting an xfstests run.""" runid = GObject.Property(type=int) sql = GObject.Property(type=GObject.TYPE_PYOBJECT) def __init__(self, sql: sqlite.Connection) -> None: """Initialize a CalendarView instance.""" animation = Gtk.StackTransitionType.OVER_LEFT_RIGHT super().__init__(sql=sql, orientation=Gtk.Orientation.VERTICAL) self._stack = Gtk.Stack(transition_type=animation) self._switcher = Gtk.StackSwitcher(stack=self._stack, margin_top=6, margin_bottom=6, margin_start=80, margin_end=80) self._calendar = CalendarView(sql) self._tags = RunidView(model=tree.TagList(sql)) self._calendar.bind_property("runid", self, "runid") self._tags.bind_property("runid", self, "runid") self.__add_page(self._calendar, "Calendar", "month-symbolic") self.__add_page(self._tags, "Tags", "tag-symbolic") self._switcher.add_css_class("large-icons") self.append(self._switcher) self.append(Gtk.Separator()) self.append(self._stack) def __add_page(self, page: Gtk.Widget, title: str, icon: str) -> None: pg = self._stack.add_titled(page, title.lower(), title) pg.set_icon_name(icon)