Compare commits

...

No commits in common. "pyside" and "gtk4" have entirely different histories.
pyside ... gtk4

6 changed files with 244 additions and 357 deletions

1
.gitignore vendored
View File

@ -1,2 +1 @@
__pycache__
.idea

8
emacs_client.py Normal file
View File

@ -0,0 +1,8 @@
import subprocess
def navigate_emacs_to_id(item_id):
item_id = item_id.replace('"', '\\"')
return subprocess.check_call([
'emacsclient', '-e', '(org-id-goto "{}")'.format(item_id)],
stdout=subprocess.DEVNULL,
)

342
main.py Executable file → Normal file
View File

@ -1,210 +1,184 @@
#!/usr/bin/env python3
import logging
import os
import sys
import time
import webbrowser
import os
import logging
import threading
from PySide2.QtCore import QObject, QThread, Signal, Slot
from PySide2.QtWidgets import (QApplication, QDialog, QFrame, QGroupBox,
QHBoxLayout, QLabel, QLineEdit, QProgressBar,
QPushButton, QScrollArea, QScroller, QTabBar,
QVBoxLayout)
import doc_manager
import task_manager
import emacs_client
APP_TITLE = "Org-Convergence"
DOCS_PATH = os.environ["ORG_PATH"]
STYLE_FILE_PATH = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"style.css")
MIN_TITLE_WIDTH_CHARS = 10
class LoadDoneSignal(QObject):
sig = Signal(doc_manager.DocumentManager)
import gi
gi.require_version("Gtk", "4.0")
gi.require_version('Polkit', '1.0')
gi.require_version(namespace='Adw', version='1')
class DocumentLoader(QThread):
def __init__(self, manager):
QThread.__init__(self, None)
self.manager = manager
self.signal = LoadDoneSignal()
from gi.repository import Gtk, Polkit, GObject, Gio, Adw, Gdk
def run(self):
self.manager.load()
self.signal.sig.emit(self.manager)
class MainWindow(Gtk.Window):
__gsignals__ = {
"open-in-emacs": (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (object, )),
}
## Setup
def __init__(self, *, title, application, task_manager, with_titlebar=True):
super().__init__(title=title, application=application)
self.application = application
self.task_manager = task_manager
self.loading = 0
class Dialog(QDialog):
def __init__(self):
super(Dialog, self).__init__()
if with_titlebar:
self.header_bar = Gtk.HeaderBar()
# self.header_bar.set_show_close_button(True)
# self.header_bar.props.title = APP_TITLE
self.set_titlebar(self.header_bar)
self.setWindowTitle("OrgEditor")
scrSize = self.screen().size()
self.resize(scrSize.width() / 1.5, scrSize.height() / 1.5)
self.loader = None
self.manager = doc_manager.DocumentManager(DOCS_PATH)
layout = QVBoxLayout()
# Edit box
self.progressBar = QProgressBar()
self.progressBar.setRange(0, 0) # Make undetermined
layout.addWidget(self.progressBar)
self.edit = QLineEdit("", placeholderText="Search for notes")
self.edit.textEdited.connect(self.on_text_edited)
layout.addWidget(self.edit)
layout.setSpacing(0)
self.results = QScrollArea(widgetResizable=True)
layout.addWidget(self.results)
QScroller.grabGesture(
self.results.viewport(),
QScroller.LeftMouseButtonGesture,
)
# Options
self.tabBar = QTabBar(shape=QTabBar.RoundedSouth)
self.tabBar.addTab("Agenda")
self.tabBar.addTab("Notes")
self.tabBar.addTab("Tasks")
self.tabBar.currentChanged.connect(self.update_tab)
layout.addWidget(self.tabBar)
self.setLayout(layout)
self.startLoad()
@Slot()
def on_text_edited(self):
if self.tabBar.currentIndex() != 1:
self.tabBar.setCurrentIndex(1)
self.progress_spinner = Gtk.Spinner()
self.progress_spinner.start()
self.header_bar.pack_end(self.progress_spinner)
else:
self.loadNotes()
self.header_bar = None
self.progress_spinner = None
@Slot()
def update_tab(self):
tabIndex = self.tabBar.currentIndex()
if tabIndex == 0:
self.loadAgenda()
elif tabIndex == 1:
self.loadNotes()
elif tabIndex == 2:
self.loadTasks()
# self.main_box = Gtk.Box(name='main-box', vexpand=True, hexpand=True)
self.scrollview = Gtk.ScrolledWindow(vexpand=True, hexpand=True)
def startLoad(self):
self.edit.setDisabled(True)
self.edit.setVisible(False)
self.tabBar.setDisabled(True)
self.progressBar.setVisible(True)
self.task_list = Gtk.ListBox(name='task-list')
self.scrollview.set_child(self.task_list)
self.item_rows = []
self.loader = DocumentLoader(self.manager)
self.loader.signal.sig.connect(self.longoperationcomplete)
self.loading_start_time = time.time()
self.loader.start()
# self.main_box.props.valign = Gtk.Align.CENTER
# self.main_box.props.halign = Gtk.Align.CENTER
# self.main_box.append(self.scrollview)
# self.set_child(self.main_box)
self.set_child(self.scrollview)
def endLoad(self):
self.edit.setDisabled(False)
self.edit.setVisible(True)
self.tabBar.setDisabled(False)
self.progressBar.setVisible(False)
self.update_tab()
def longoperationcomplete(self, data):
logging.info(
"Loading complete in {:.3f}s".format(time.time() - self.loading_start_time)
self.loading += 1
self.task_manager.get_task_list(
self.on_task_list_update,
self.on_task_list_ready,
)
self.endLoad()
def loadAgenda(self):
agenda = self.manager.get_agenda()
old = self.results.layout()
if old:
print("Deleting old")
old.deleteLater()
layout = QVBoxLayout()
for item in agenda.with_hour:
layout.addWidget(self.build_agenda_task_widget(item))
# if len(agenda.with_hour) > 0 and len(agenda.no_hour) > 0:
# layout.addWidget(QSplitter())
for item in agenda.no_hour:
layout.addWidget(self.build_agenda_task_widget(item))
frame = QFrame(self.results)
frame.setLayout(layout)
self.results.setWidget(frame)
def build_agenda_task_widget(self, item):
box = QHBoxLayout()
frame = QGroupBox()
frame.setLayout(box)
state_button = QPushButton(text=f"{item.state or '-'}", maximumWidth=60)
if item.is_done:
state_button.setFlat(True)
box.addWidget(state_button)
box.addWidget(QLabel(text=f"{item.scheduled.time}", maximumWidth=200))
box.addWidget(QLabel(text=f"{item.title}"))
def on_clicked():
state_button.setText("DONE")
# state_button.setFlat(True)
# item.state = 'DONE'
if not item.is_done:
state_button.clicked.connect(on_clicked)
return frame
def build_note_task_widget(self, item):
box = QHBoxLayout()
frame = QGroupBox()
frame.setLayout(box)
titleButton = QPushButton(text=f"{item.title}")
box.addWidget(titleButton)
def on_clicked():
webbrowser.open("org-protocol://org-id?id=" + item.id)
titleButton.clicked.connect(on_clicked)
return frame
def loadNotes(self):
query = self.edit.text()
notes = self.manager.get_notes(query.split())
old = self.results.layout()
if old:
print("Deleting old")
old.deleteLater()
layout = QVBoxLayout()
for note in notes:
layout.addWidget(self.build_note_task_widget(note))
frame = QFrame(self.results)
frame.setLayout(layout)
self.results.setWidget(frame)
def loadTasks(self):
logging.warning("loadTasks not yet implemented")
## Keyboard shortcuts
def open_in_emacs(self, *args):
row = self.task_list.get_selected_row()
if row is None:
return
item = self.item_rows[row.get_index()]
item_id = item.id
if item_id is None:
logging.warning("No ID found for item: {}".format(item))
return
emacs_client.navigate_emacs_to_id(item_id)
# Create the Qt Application
if __name__ == "__main__":
## Rendering
def build_agenda_task_row(self, task):
row = Gtk.ListBoxRow()
hbox = Gtk.Box()
state_button = Gtk.Button.new_with_label(task.state or '')
state_button.props.css_classes = ('state-button',)
state_button.connect("clicked", self.on_status_button_clicked)
hbox.append(state_button)
clock_button = Gtk.Button.new_with_label('C')
clock_button.props.css_classes = ('clock-button',)
clock_button.connect("clicked", self.on_clock_button_clicked)
hbox.append(clock_button)
# task_name_label = Gtk.Entry(text=task.title, width_chars=max(MIN_TITLE_WIDTH_CHARS, len(task.title)))
task_name_label = Gtk.Label()
task_name_label.set_text(task.title)
task_name_label.props.css_classes = ('task-name',)
hbox.append(task_name_label)
row.set_child(hbox)
return row
def on_ready(self):
self.loading -= 1
if self.loading < 0:
self.loading = 0
elif self.loading == 0:
if self.progress_spinner is not None:
self.progress_spinner.stop()
## Callbacks
def on_task_list_update(self, new_rows):
for item in new_rows.with_hour:
self.task_list.append(self.build_agenda_task_row(item))
self.item_rows.append(item)
for item in new_rows.no_hour:
self.task_list.append(self.build_agenda_task_row(item))
self.item_rows.append(item)
def on_task_list_ready(self, success):
self.on_ready()
## Reactions
def on_status_button_clicked(self, button):
print('Status button clicked: {}'.format(button))
def on_clock_button_clicked(self, button):
print('Clock button clicked: {}'.format(button))
class Application(Gtk.Application):
""" Main Aplication class """
def __init__(self):
super().__init__(application_id='com.codigoparallevar.org-convergence',
flags=Gio.ApplicationFlags.FLAGS_NONE)
self.task_manager = task_manager.TaskManager(DOCS_PATH)
def do_activate(self):
win = self.props.active_window
if not win:
if os.path.exists(STYLE_FILE_PATH):
style_provider = Gtk.CssProvider()
Gtk.StyleContext.add_provider_for_display(Gdk.Display.get_default(), style_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
style_provider.load_from_path(STYLE_FILE_PATH)
win = MainWindow(
title=APP_TITLE,
application=self,
task_manager=self.task_manager,
)
# PinePhone screen is 720x1440 (portrait) but, has 2x pixel density
win.set_default_size(360, 720)
## Load shortcuts
# Open in emacs
action = Gio.SimpleAction.new("open-in-emacs", None)
action.connect("activate", win.open_in_emacs)
self.add_action(action)
self.set_accels_for_action('app.open-in-emacs', ["<Ctrl>e"])
win.present()
def main():
""" Run the main application"""
# GObject.threads_init()
logging.basicConfig(level=logging.INFO, format="%(levelname)-8s %(message)s")
app = Application()
return app.run(sys.argv)
app = QApplication(sys.argv)
dialog = Dialog()
sys.exit(dialog.exec_())
if __name__ == '__main__':
main()

View File

@ -1,149 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="144.98"
height="160"
viewBox="-7.65 -13.389 144.98 160"
version="1.1"
id="svg28"
sodipodi:docname="org-mode.svg"
inkscape:version="1.0.2 (e86c870879, 2021-01-15)">
<metadata
id="metadata34">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs32" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1364"
inkscape:window-height="743"
id="namedview30"
showgrid="false"
inkscape:zoom="1.066"
inkscape:cx="88.23"
inkscape:cy="32.52"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-maximized="1"
inkscape:current-layer="layer2"
inkscape:document-rotation="0" />
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Backdrop"
style="display:inline">
<circle
style="opacity:1;fill:#1a1a1a;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.0067;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:1;paint-order:fill markers stroke"
id="path878"
cx="66.35"
cy="72.64"
r="68.86" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Base"
style="display:inline">
<path
fill="#77aa99"
stroke="#000000"
stroke-width="0.5"
d="m 6.448,104.3 c 0,0 10.022,36.1 46.552,24.6 25.19,-7.2 43.25,3 46.11,4.1 l 4.39,-2.5 2.9,-2.1 C 105.8,126.9 102.8,114.6 76.98,99.64 61.72,91.32 71.3,76.99 72.39,71.93 c 0,0 12.23,8.55 21.09,-4.52 8.02,2.06 13.32,-1.47 20.22,1.03 4.2,1.83 21.8,0.72 15.3,-9.11 4.1,-2.68 4.5,-1.81 6.6,-5.9 1.1,-4.95 -1.4,-6.01 -2.2,-7.36 -0.2,-3.15 -2.8,-4.37 -6,-2.13 -7.2,-1.29 -14.3,-0.68 -17.8,-0.98 -7.7,-1.07 -14.03,-5.13 -14.03,-5.13 0.93,-1.24 0.48,-3.91 -5.5,-4.1 -3.29,-1.08 -6.75,-3.13 -9.29,-3.16 -2.57,0 -2.91,-2.54 -2.91,-2.54 -1.61,-0.87 -3.93,-4.25 -3.91,-9.44 0,-3.27 -6.09,0.32 -6.09,6.79 0,6.46 -5.82,7.76 -5.82,7.76 0,0 -2.55,-2.28 -2.86,-5.96 -0.58,-3.79 -4.93,-9.56 -7.05,-2.43 -3.23,7.64 -3.49,9.42 -4.12,13.15 -1.31,7.71 -0.34,8.01 -0.34,8.01 L 14.85,69.34 Z"
id="path4"
transform="translate(0,0.001)"
sodipodi:nodetypes="ccccccccscccccccscsscccsccc" />
<path
fill="#314b49"
stroke="#314b49"
stroke-width="0.75"
stroke-linecap="round"
stroke-linejoin="round"
d="m 84.11,42.83 c 1.55,-0.56 0.9,-0.41 1.15,-0.58 -2.96,0.58 -9.63,0.62 -14.31,-1.13 0.39,0.23 2.56,0.96 2.84,1.13 0.22,0.71 0.1,1.43 2.93,2.71 2.56,0.79 5.85,0.46 6.84,-0.53 0.11,-1.69 0.12,-1.07 0.55,-1.6 z"
id="path6"
transform="translate(0,0.001)" />
<path
fill="#314b49"
stroke="#314b49"
stroke-width="0.5"
d="m 116.5,61.98 c -2.9,-2.09 -4.9,-0.27 -6.5,-0.41 1,-0.65 3.8,-2.07 8.3,-2.07 2.5,0 3.8,2.2 5.5,2.21 1.2,0 4.5,-1.71 5.2,-2.38 -1,0.84 0.4,1.76 -0.5,3.01 -0.3,0.36 -0.9,1.51 -2.7,2.05 -2,0.97 -5.4,1.76 -9.3,-2.41 z"
id="path8"
transform="translate(0,0.001)" />
<path
fill="#314b49"
d="m 54.93,24.03 c 0,0 -3.35,8 0.31,15.33 3.67,7.34 0.53,-6.83 4.69,-4.16 3.4,0.39 -2.38,-3.21 -2.03,-7.82 -0.18,-2.89 -1.77,-5.19 -2.97,-3.35 z m 64.37,26.39 c 0,1.12 -1.3,2.03 -3,2.03 3.6,-1.12 -0.2,-4.66 -3.1,-2.03 0,-1.12 1.4,-2.03 3.1,-2.03 1.7,0 3,0.91 3,2.03 z"
id="path10"
transform="translate(0,0.001)" />
<path
fill="#314b49"
d="m 114.2,47.83 c 3.7,-0.23 6.3,0.33 5.5,3.14 0.6,-1.12 1.3,-2.83 -1,-3.51 -1.8,-0.38 -3.3,-0.37 -4.5,0.37 z"
id="path12"
transform="translate(0,0.001)" />
<path
fill="#796958"
stroke="#000000"
stroke-width="0.5"
d="m 76.27,30.18 c 0,0 -0.25,7.47 6.72,2.6 3.61,0.1 2.01,-3.86 2.01,-3.86 2.44,-0.68 2.81,-3.85 1.85,-5.71 3.11,0.1 2.61,-4.72 2.18,-6.38 2.44,-0.93 2.77,-3.83 1.77,-6.13 2.93,-0.67 3.02,-4.116 2.77,-6.55 3.02,-0.168 2.6,-5.457 2.6,-6.549 2.6,-1.679 2.02,-3.946 2.42,-6.573 1.61,-3.248 -0.57,-4.178 -2.11,-0.71 -1.65,3.001 -3.77,4.311 -3.75,6.528 0.75,1.259 -5.63,3.106 -3.61,7.052 -1.43,1.763 -4.79,4.03 -3.59,6.732 -0.61,1.33 -4.89,4.43 -3.04,7.37 -4.03,2.69 -3.79,3.34 -2.94,5.79 -2.16,1.38 -4.41,4.14 -3.28,6.39 z"
id="path16"
transform="translate(0,0.001)" />
<path
fill="#ffffff"
d="m 94.09,-5.087 c 0,0 -0.73,1.324 -0.73,2.133 0,0.809 2.18,0.568 2.93,-0.227 -1.63,0.02 -2.97,0.289 -2.2,-1.906 z m -4.26,6.27 c 0,0 -0.81,1.068 -0.18,2.316 0.39,0.98 2.81,0.962 3.55,0.167 C 91.57,3.69 89.06,3.379 89.83,1.183 Z M 86.7,7.638 c 0,0 -1,1.346 -0.49,2.602 0,0.81 2.83,0.96 3.58,0.16 -1.63,0 -3.65,-0.488 -3.09,-2.762 z M 83.62,14.8 c 0,0 -1.4,1.54 -0.15,2.95 1.44,0.8 3.75,0 4.49,-0.75 -1.63,0 -4.9,0.1 -4.34,-2.2 z m -3,5.72 c 0,0 -1.58,1.42 0,3.31 1.43,0.81 4.57,0.2 5.31,-0.59 -1.63,0 -5.99,-1.35 -5.29,-2.72 z m -3.15,6.74 c 0,0 -1.4,1.54 -0.15,2.95 1.44,0.81 6.04,-0.19 6.78,-0.98 -1.63,0 -7.19,0.31 -6.63,-1.97 z"
id="path18"
transform="translate(0,0.001)" />
<path
opacity="0.26"
d="m 60.33,57.94 c 0,0 6.76,13.59 17.59,13.99 10.84,0.4 10.84,-2.73 10.84,-2.73 0,0 -15.53,3.04 -28.43,-11.26 z m 3.16,7.69 c 2.27,3.1 4.85,5.22 7.72,6.38 0,0 -7.37,11.11 -3.61,20.02 3.75,8.87 13.12,11.07 23.32,21.27 7.94,7.9 12.98,16.6 12.98,16.6 l -4.52,2.4 c 0,0 -9.14,-8.2 -20.73,-7.5 0,0 -2.68,-9.7 -10.58,-17.6 C 56.83,95.95 51.9,81.32 63.49,65.63 Z"
id="path22"
transform="translate(0,0.001)"
sodipodi:nodetypes="csccccsscccsc" />
<path
opacity="0.27"
fill="#ffffff"
d="m 71.28,19.39 c 0.99,-0.96 1.14,-0.65 0.85,0.28 -0.98,2.36 0.35,4.65 0.8,6.21 l -3.87,0.11 c 0.11,-2.27 0.25,-4.79 2.22,-6.6 z"
id="path26"
transform="translate(0,0.001)" />
</g>
<g
inkscape:groupmode="layer"
id="layer4"
inkscape:label="Simp"
style="display:inline">
<path
fill="#a04d32"
stroke="#000000"
stroke-width="0.5"
d="m 52.84,54.65 c -1.64,-2.19 -2.62,-4.33 -3.21,-6.27 -1.4,-3.3 -3.43,-8.02 -0.61,-17.24 0,0 0.62,-0.38 0,0 -2.62,1.63 -3.57,6.69 -13.08,9.89 -3.21,1.07 -9.87,7.03 -15.43,7.48 C 10.94,49.28 6.393,61.65 -2.091,77.2 l 0.693,6.03 1.4808,7.02 C 7.319,109.3 3.994,101 9.172,110.4 c 2.138,5.4 2.988,4.2 2.988,4.2 -5.641,-23.37 5.43,5.7 5.78,-16.85 1.74,3.65 36.48,-40.52 34.9,-43.1 z"
id="path14-3"
style="display:inline"
sodipodi:nodetypes="cccsssccccccc" />
<path
fill="#a04d32"
stroke="#000000"
stroke-width="0.5"
d="m 76.29,30.29 c -0.4,-0.45 -0.5,-0.9 -0.4,-1.72 0,0 0.4,-4.11 -16.29,-0.16 -1.07,0.74 2.4,4.73 2.4,4.73 0,0 0.42,0.21 1.05,-0.42 5.7,4.24 17.09,1.64 13.24,-2.43 z"
id="path20-6"
style="display:inline"
sodipodi:nodetypes="cccccc" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 7.4 KiB

13
style.css Normal file
View File

@ -0,0 +1,13 @@
#task-list {
}
#task-list .state-button,
#task-list .clock-button {
margin-right: 1ex;
}
window {
}
#task-list .task-name {
}

View File

@ -1,6 +1,7 @@
import logging
import os
import sys
import threading
from datetime import datetime
from typing import List
@ -9,6 +10,8 @@ from org_rw import OrgDoc, OrgTime
EXTENSIONS = (".org", ".org.txt")
from gi.repository import GObject
def is_today(ot: OrgTime):
now = datetime.now()
@ -39,17 +42,22 @@ class Agenda:
for item in self.no_hour:
print(item.scheduled.time, item.state, item.title)
class TaskManager:
docs: List[OrgDoc]
threads: List[threading.Thread]
class DocumentManager:
docs: list[OrgDoc]
def __init__(self, base_path: os.PathLike):
self.base_path = base_path
def __init__(self, docs_path: os.PathLike):
self.docs_path = docs_path
self.threads = []
self.docs = None
def load(self):
top = os.path.abspath(self.base_path)
t0 = datetime.now()
top = os.path.abspath(self.docs_path)
docs = []
if self.docs is None:
self.docs = docs
for root, dirs, files in os.walk(top):
# Prune dirs
@ -68,8 +76,9 @@ class DocumentManager:
path = os.path.join(root, name)
try:
doc = org_rw.load(open(path), extra_cautious=True)
doc = org_rw.load(open(path), extra_cautious=False)
docs.append(doc)
yield doc
except Exception as err:
import traceback
@ -77,10 +86,29 @@ class DocumentManager:
print(f"== On {path}")
sys.exit(1)
logging.info("Loaded {} files".format(len(docs)))
t1 = datetime.now()
logging.info("Loaded {} files in {}s".format(len(docs), t1 - t0))
self.docs = docs
def get_task_list(self, progress_callback, complete_callback):
def aux():
if self.docs is None:
last_result = None
# No docs read yet, load them iteratively
for doc in self.load():
result = self.get_agenda_from_doc(doc)
GObject.idle_add(progress_callback, result)
else:
result = self.get_agenda()
print("Result", result)
GObject.idle_add(progress_callback, result)
GObject.idle_add(complete_callback, True)
thread = threading.Thread(target=aux)
thread.start()
self.threads.append(thread)
def get_agenda(self) -> Agenda:
headline_count = 0
items_in_agenda = []
@ -123,24 +151,38 @@ class DocumentManager:
no_hour=other_items,
)
def get_notes(self, query) -> List[org_rw.Headline]:
def get_agenda_from_doc(self, doc: OrgDoc) -> Agenda:
headline_count = 0
t0 = datetime.now()
notes = []
query = [q.lower() for q in query]
items_in_agenda = []
now = datetime.now()
for doc in self.docs:
for hl in doc.getAllHeadlines():
headline_count += 1
data = "\n".join(hl.get_contents("raw")).lower()
if all([q in data for q in query]):
notes.append(hl)
if (
hl.scheduled
and isinstance(hl.scheduled, OrgTime)
and hl.scheduled.time.active
):
if is_today(hl.scheduled):
items_in_agenda.append(hl)
elif (hl.scheduled.time.to_datetime() < now) and hl.is_todo:
items_in_agenda.append(hl)
logging.info(
"Filtered {} to {} items in {:.3f}s".format(
headline_count, len(notes), (datetime.now() - t0).total_seconds()
)
items_with_hour = [
item
for item in items_in_agenda
if item.scheduled and is_today(item.scheduled) and item.scheduled.time.hour
]
other_items = [
item
for item in items_in_agenda
if not (
item.scheduled and is_today(item.scheduled) and item.scheduled.time.hour
)
]
return notes
return Agenda(
with_hour=sorted(items_with_hour, key=lambda x: x.scheduled.time),
no_hour=other_items,
)