From 030fa1d90e8331035dbfcb39b75d573976b495fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=B6rnquist?= Date: Thu, 1 Dec 2022 05:24:26 +0100 Subject: Move maildir stuff to own directory. --- mu4web/maildir.py | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ mu4web/main.py | 80 +------------------------------------------------- mu4web/util.py | 10 +++++++ 3 files changed, 99 insertions(+), 79 deletions(-) create mode 100644 mu4web/maildir.py create mode 100644 mu4web/util.py diff --git a/mu4web/maildir.py b/mu4web/maildir.py new file mode 100644 index 0000000..831f23d --- /dev/null +++ b/mu4web/maildir.py @@ -0,0 +1,88 @@ +from dataclasses import dataclass +import os.path + +from html_render import HTML +from util import find + +from urllib.parse import urlencode + +from typing import ( + Union +) + +try: + from natsort import natsorted +except ModuleNotFoundError: + natsorted = sorted + +@dataclass +class MaildirEntry: + """A single maildir, used by find_maildirs.""" + name: str + + +@dataclass +class MaildirGroup: + """A group of maildir, which isn't a maildir in itself.""" + name: str + children: list[Union[MaildirEntry, 'MaildirGroup']] + + +def _build_tree(items: list[list[str]]) -> MaildirGroup: + groups: dict[str, list[list[str]]] = {} + direct: list[MaildirEntry] = [] + for key, *rest in items: + if rest: + groups.setdefault(key, []).append(rest) + else: + direct.append(MaildirEntry(key)) + + node = MaildirGroup('root', []) + for key, values in groups.items(): + next = _build_tree(values) + next.name = key + node.children.append(next) + node.children.extend(direct) + return node + + + +def find_maildirs(basedir) -> MaildirGroup: + """ + Find all maildirs located under basedir. + + A maildir is defined as any directory which contains a `cur` + directory. + + Returns a MaildirGroup of the root, whose children will be all the + maildirs under basedir. Note that if a directory is both a maildir + and a group of maildirs then it will have two separate entries. + """ + basedir = basedir.rstrip('/') + files = find(basedir, type='d', name='cur') + # + 1 removes leading slash + # - 4 removes '/cur' + dirs = [entry[len(basedir)+1:-4].decode('UTF-8').split(os.path.sep) + for entry in files] + + return _build_tree(dirs) + + +def serialize_maildir(maildir: MaildirGroup, path: list[str] = []) -> HTML: + """Build a (recursive) list from a maildir node.""" + entries: list[HTML] = [] + for node in natsorted(maildir.children, key=lambda n: n.name): + if isinstance(node, MaildirEntry): + parts = '/'.join(path + [node.name]) + url = 'search?' + urlencode({'q': f'maildir:"/{parts}"'}) + entry = ('li', ('a', {'href': url}, + node.name or ('i', 'root'))) + else: + entry = ('li', + ('details', + ('summary', node.name), + serialize_maildir(node, path + [node.name]))) + entries.append(entry) + + return ('ul', *entries) + diff --git a/mu4web/main.py b/mu4web/main.py index 4d39c1a..d57cc64 100644 --- a/mu4web/main.py +++ b/mu4web/main.py @@ -23,9 +23,8 @@ import mu from html_render import HTML, render_document from user.local import LocalUser from user.pam import PamUser +from maildir import find_maildirs, serialize_maildir -import subprocess -from dataclasses import dataclass import flask from flask import ( @@ -38,10 +37,6 @@ from flask import ( get_flashed_messages ) -try: - from natsort import natsorted -except ModuleNotFoundError: - natsorted = sorted # # A few operations depend on the index of attachements. These index @@ -333,79 +328,6 @@ def search_page(q: str, by: Optional[mu.Sortfield], body=main_body)) -@dataclass -class Leaf: - name: str - -@dataclass -class Node: - name: str - children: list[Union[Leaf, 'Node']] - - -def build_tree(items: list[list[str]]) -> Node: - groups: dict[str, list[list[str]]] = {} - direct: list[Leaf] = [] - for key, *rest in items: - if rest: - groups.setdefault(key, []).append(rest) - else: - direct.append(Leaf(key)) - - node = Node('root', []) - for key, values in groups.items(): - next = build_tree(values) - next.name = key - node.children.append(next) - node.children.extend(direct) - return node - - -def find(basedir, **flags) -> list[bytes]: - cmdline = ['find', basedir] - for key, value in flags.items(): - cmdline += [f'-{key}', value] - cmdline.append('-print0') - - cmd = subprocess.run(cmdline, capture_output=True) - return cmd.stdout.split(b'\0')[:-1] - - -def find_maildirs(basedir) -> Node: - """ - Find all maildirs located under basedir. - - A maildir is defined as any directory which contains a `cur` - directory. - """ - basedir = basedir.rstrip('/') - files = find(basedir, type='d', name='cur') - # + 1 removes leading slash - # - 4 removes '/cur' - dirs = [entry[len(basedir)+1:-4].decode('UTF-8').split(os.path.sep) - for entry in files] - - return build_tree(dirs) - - -def serialize_maildir(maildir: Node, path: list[str] = []) -> HTML: - """Build a (recursive) list from a maildir node.""" - entries: list[HTML] = [] - for node in natsorted(maildir.children, key=lambda n: n.name): - if isinstance(node, Leaf): - parts = '/'.join(path + [node.name]) - url = 'search?' + urlencode({'q': f'maildir:"/{parts}"'}) - entry = ('li', ('a', {'href': url}, - node.name or ('i', 'root'))) - else: - entry = ('li', - ('details', - ('summary', node.name), - serialize_maildir(node, path + [node.name]))) - entries.append(entry) - - return ('ul', *entries) - def index_page(): data = mu.info() maildirs = find_maildirs(data['maildir'] + '/') diff --git a/mu4web/util.py b/mu4web/util.py new file mode 100644 index 0000000..c742cd6 --- /dev/null +++ b/mu4web/util.py @@ -0,0 +1,10 @@ +import subprocess + +def find(basedir, **flags) -> list[bytes]: + cmdline = ['find', basedir] + for key, value in flags.items(): + cmdline += [f'-{key}', value] + cmdline.append('-print0') + + cmd = subprocess.run(cmdline, capture_output=True) + return cmd.stdout.split(b'\0')[:-1] -- cgit v1.2.3