Compare commits

...

No commits in common. "cbd99f25acd070b6c397f9c7d8a98eadb86341e1" and "ef4d268225ed9b8de115e3d283d4b6367db37e2c" have entirely different histories.

5 changed files with 181 additions and 386 deletions

1
.gitignore vendored
View File

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

328
main.py Executable file → Normal file
View File

@ -1,241 +1,157 @@
#!/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.QtGui import QPalette, QColor
from PySide2.QtWidgets import (
QApplication,
QDialog,
QFrame,
QGroupBox,
QHBoxLayout,
QLabel,
QLineEdit,
QProgressBar,
QPushButton,
QScrollArea,
QScroller,
QTabBar,
QVBoxLayout,
)
import doc_manager
import task_manager
APP_TITLE = "Org-mob"
DOCS_PATH = os.environ["ORG_PATH"]
MAX_SEARCH_NODES = 100
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):
## 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)
palette = self.palette()
palette.setColor(QPalette.Window, QColor(255, 255, 255))
self.setPalette(palette)
self.setAutoFillBackground(True)
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.addTab("History")
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()
elif tabIndex == 3:
self.loadHistory()
# 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.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.loading += 1
self.task_manager.get_task_list(self.on_task_list_ready)
self.update_tab()
## Rendering
def build_agenda_task_row(self, task):
row = Gtk.ListBoxRow()
hbox = Gtk.Box()
def longoperationcomplete(self, data):
logging.info(
"Loading complete in {:.3f}s".format(time.time() - self.loading_start_time)
)
self.endLoad()
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)
def loadAgenda(self):
agenda = self.manager.get_agenda()
old = self.results.layout()
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)
if old:
print("Deleting old")
old.deleteLater()
# 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)
layout = QVBoxLayout()
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_ready(self, agenda):
# TODO: Avoid reconstructing the whole list every time
i = 0
child = self.task_list.get_first_child()
while child is not None:
was = child
child = child.get_next_sibling()
i += 1
self.task_list.remove(was)
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())
self.task_list.append(self.build_agenda_task_row(item))
for item in agenda.no_hour:
layout.addWidget(self.build_agenda_task_widget(item))
self.task_list.append(self.build_agenda_task_row(item))
self.on_ready()
layout.addStretch()
## Reactions
def on_status_button_clicked(self, button):
print('Status button clicked: {}'.format(button))
frame = QFrame(self.results)
frame.setLayout(layout)
self.results.setWidget(frame)
def build_agenda_task_widget(self, item):
box = QHBoxLayout()
frame = QFrame()
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[:MAX_SEARCH_NODES]:
layout.addWidget(self.build_note_task_widget(note))
layout.addStretch()
frame = QFrame(self.results)
frame.setLayout(layout)
self.results.setWidget(frame)
def loadTasks(self):
logging.warning("loadTasks not yet implemented")
def loadHistory(self):
logging.warning("loadHistory not yet implemented")
def on_clock_button_clicked(self, button):
print('Clock button clicked: {}'.format(button))
# Create the Qt Application
if __name__ == "__main__":
class Application(Gtk.Application):
""" Main Aplication class """
def __init__(self):
super().__init__(application_id='com.codigoparallevar.gtk4-organizer',
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,
)
win.set_default_size(720, 1024) # PinePhone screen is 720x1440
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

20
style.css Normal file
View File

@ -0,0 +1,20 @@
#task-list {
border: 1px solid #cdc7c2;
padding: 1ex;
margin: 1ex;
}
#task-list .state-button,
#task-list .clock-button {
margin-right: 1ex;
}
window {
background-color: #d6d5d4;
}
#task-list .task-name {
border: none;
border-bottom: 1px solid #ccc;
border-radius: 0;
}

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,32 @@ 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, callback):
def aux():
if self.docs is None:
last_result = None
for doc in self.load():
result = self.get_agenda()
if ((last_result is None)
or (len(result.with_hour) != len(last_result.with_hour))
or (len(result.no_hour) != len(last_result.no_hour))):
print("Loaded:", doc._path)
GObject.idle_add(callback, result)
print("Load completed")
else:
result = self.get_agenda()
print("Result", result)
GObject.idle_add(callback, result)
thread = threading.Thread(target=aux)
thread.start()
self.threads.append(thread)
def get_agenda(self) -> Agenda:
headline_count = 0
items_in_agenda = []
@ -122,25 +153,3 @@ class DocumentManager:
with_hour=sorted(items_with_hour, key=lambda x: x.scheduled.time),
no_hour=other_items,
)
def get_notes(self, query) -> List[org_rw.Headline]:
headline_count = 0
t0 = datetime.now()
notes = []
query = [q.lower() for q in query]
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)
logging.info(
"Filtered {} to {} items in {:.3f}s".format(
headline_count, len(notes), (datetime.now() - t0).total_seconds()
)
)
return notes