commit bd3966f45276140ebaca933ac669c7eaaa8f0a7d
parent 6f2ccfcab6bba6fd242dca819d532fad240e58d8
Author: lash <dev@holbrook.no>
Date: Thu, 1 Dec 2022 15:12:04 +0000
Move renderer driver to base class
Diffstat:
9 files changed, 224 insertions(+), 38 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
@@ -1,3 +1,6 @@
+- 0.2.1
+ * Render browsable HTML version of board, issues and issue logs
+ * Add sender identity in email on signature
- 0.2.0
* GPG signing of issue messages.
* Use visitor pattern for messages verifier
diff --git a/ROADMAP b/ROADMAP
@@ -9,5 +9,3 @@
- target hash above
- git tag
- arbitrary string identifier
-- 0.2.1
- * Render browsable HTML version of board, issues and issue logs
diff --git a/piknik/crypto.py b/piknik/crypto.py
@@ -27,6 +27,17 @@ class PGPSigner:
self.__skip_verify = skip_verify
+ def set_from(self, msg, passphrase=None):
+ r = None
+ for v in self.gpg.list_keys(True):
+ if self.default_key == None or v['fingerprint'].upper() == self.default_key.upper():
+ r = v['uids'][0]
+ break
+ if r == None:
+ raise UnknownIdentityError('no signing keys found')
+ msg.add_header('From', r)
+
+
def sign(self, msg, passphrase=None): # msg = IssueMessage object
m = Message()
v = msg.as_string()
@@ -37,6 +48,7 @@ class PGPSigner:
fn = '{}.asc'.format(msg.get('X-Piknik-Msg-Id'))
ms.add_header('Content-Disposition', 'attachment', filename=fn)
+ self.set_from(msg, passphrase=passphrase)
sig = self.gpg.sign(v, keyid=self.default_key, detach=True, passphrase=self.passphrase)
ms.set_payload(str(sig))
@@ -58,6 +70,9 @@ class PGPSigner:
def message_callback(self, envelope, msg, message_id):
+ if msg.get('From') != None:
+ envelope.sender = msg.get('From')
+
if self.__envelope_state == 0:
self.__envelope_state = 1
self.__envelope = msg
@@ -87,7 +102,7 @@ class PGPSigner:
else:
logg.debug('signature ok from {}'.format(r.fingerprint))
envelope.valid = True
- envelope.sender = r.fingerprint
+ #envelope.sender = r.fingerprint
self.__envelope_state = 2
return (envelope, msg,)
diff --git a/piknik/render/base.py b/piknik/render/base.py
@@ -1,35 +1,118 @@
-import sys
+#import sys
+import logging
+
+logg = logging.getLogger(__name__)
class Renderer:
- def apply_begin(self, w=sys.stdout):
+ def __init__(self, basket, accumulator=None):
+ self.b = basket
+ self.e = None
+ self.a = accumulator
+
+
+ def __add(self, v):
+ if self.a != None:
+ if v != None:
+ self.a(v)
+
+
+ def apply_envelope_pre(self, state, issue, tags, envelope, accumulator=None):
+ #logg.debug('entering envelope {}'.format(envelope))
+ self.e = envelope
+
+
+ def apply_envelope_post(self, state, issue, tags, envelope, accumulator=None):
+ #logg.debug('leaving envelope {}'.format(self.e))
+ self.e = None
+
+
+ def apply_envelope(self, state, issue, tags, envelope, accumulator=None):
pass
- def apply_state_pre(self, state, w=sys.stdout):
+ def apply_message_pre(self, state, issue, tags, envelope, message, accumulator=None):
pass
- def apply_state(self, state, w=sys.stdout):
+ def apply_message_post(self, state, issue, tags, envelope, message, accumulator=None):
pass
+
+ def apply_message(self, state, issue, tags, envelope, message, accumulator=None):
+ pass
- def apply_issue_pre(self, state, issue, tags, w=sys.stdout):
+
+ def apply_issue_pre(self, state, issue, tags, accumulator=None):
pass
- def apply_issue(self, state, issue, tags, w=sys.stdout):
+ def apply_issue_post(self, state, issue, tags, accumulator=None):
pass
-
- def apply_issue_post(self, state, issue, tags, w=sys.stdout):
+
+ def apply_issue(self, state, issue, tags, accumulator=None):
+
+ def envelope_callback(envelope, envelope_type):
+ r = self.apply_envelope_pre(state, issue, tags, envelope, accumulator=accumulator)
+ self.__add(r)
+ r = self.apply_envelope(state, issue, tags, envelope, accumulator=accumulator)
+ self.__add(r)
+ r = self.apply_envelope_post(state, issue, tags, envelope, accumulator=accumulator)
+ self.__add(r)
+
+ def message_callback(envelope, message, message_id):
+ r = self.apply_message_pre(state, issue, tags, envelope, message, accumulator=accumulator)
+ self.__add(r)
+ r = self.apply_message(state, issue, tags, envelope, message, accumulator=accumulator)
+ self.__add(r)
+ r = self.apply_message_post(state, issue, tags, envelope, message, accumulator=accumulator)
+ self.__add(r)
+
+ #for msg in self.b.get_msg(issue.id, envelope_callback=envelope_callback, message_callback=message_callback):
+ self.b.get_msg(issue.id, envelope_callback=envelope_callback, message_callback=message_callback)
+
+
+ def apply_state_pre(self, state, accumulator=None):
+ pass
+
+
+ def apply_state_post(self, state, accumulator=None):
pass
- def apply_state_post(self, state, w=sys.stdout):
+ def apply_state(self, state, accumulator=None):
+ for issue_id in self.b.list(category=state):
+ issue = self.b.get(issue_id)
+ tags = self.b.tags(issue_id=issue_id)
+ r = self.apply_issue_pre(state, issue, tags)
+ self.__add(r)
+ r = self.apply_issue(state, issue, tags)
+ self.__add(r)
+ r = self.apply_issue_post(state, issue, tags)
+ self.__add(r)
+
+
+ def apply_begin(self, accumulator=None):
pass
- def apply_end(self, w=sys.stdout):
+ def apply_end(self, accumulator=None):
pass
+
+
+ def apply(self, accumulator=None):
+ r = self.apply_begin()
+ self.__add(r)
+
+ for state in self.b.states():
+ r = self.apply_state_pre(state)
+ self.__add(r)
+ r = self.apply_state(state)
+ self.__add(r)
+ r = self.apply_state_post(state)
+ self.__add(r)
+
+ r = self.apply_end()
+ self.__add(r)
diff --git a/piknik/render/html.py b/piknik/render/html.py
@@ -1,6 +1,7 @@
# standard imports
import sys
import os
+import logging
# external imports
import dominate
@@ -10,6 +11,8 @@ from mimeparse import parse_mime_type
# local imports
from .base import Renderer as BaseRenderer
+logg = logging.getLogger(__name__)
+
class Renderer(BaseRenderer):
@@ -20,6 +23,7 @@ class Renderer(BaseRenderer):
self.message_buf = []
self.outdir = outdir
self.last_message_id = None
+ self.msg_idx = 0
def apply_state_post(self, state, w=sys.stdout):
@@ -42,6 +46,7 @@ class Renderer(BaseRenderer):
while True:
try:
v = self.message_buf.pop(0)
+ logg.debug('msgd {} {}'.format(issue.id, str(v)))
r_l.add(v)
except IndexError:
break
@@ -100,8 +105,7 @@ class Renderer(BaseRenderer):
def apply_message_post(self, state, issue, tags, message, message_from, message_date, message_id, message_valid, w=sys.stdout):
#r = ol()
#w.write(self.message_buf.render())
-
-
+ self.msg_idx = 0
pass
@@ -110,30 +114,17 @@ class Renderer(BaseRenderer):
filename = message.get_filename()
if message_id != self.last_message_id:
- self.message_buf.append(div('--- ' + str(message_date), _id=issue.id))
+ s = '--- {} @ {}'.format(message_from, message_date)
+ self.message_buf.append(div(s, _id=issue.id))
self.last_message_id = message_id
- r = div(_id=issue.id + '.' + message_id)
+ r = div(_id=issue.id + '.' + message_id + '.' + str(self.msg_idx))
+ self.msg_idx += 1
if filename == None:
v = message.get_payload()
if message.get('Content-Transfer-Encoding') == 'BASE64':
v = b64decode(v).decode()
r.add(p(v))
-
- else:
- v = message.get_payload()
- if m[0] == 'image':
- img_src = 'data:{}/{};base64,'.format(m[0], m[1])
- img_src += v
- r.add(p(img(src=img_src)))
- else:
- data_src = 'data:application/octet-stream;base64,' + v
- r.add(a(filename, download=filename, href=data_src))
-
- self.message_buf.append(r)
-
- #for i, v in enumerate(self.message_buf):
- # r.add(p(v))
#w.write(r.render())
diff --git a/piknik/render/ini.py b/piknik/render/ini.py
@@ -15,3 +15,11 @@ class Renderer(BaseRenderer):
def apply_state_post(self, state, w=sys.stdout):
w.write('\n')
+
+
+ def apply_message_part(self, state, issue, envelope, message, message_from, message_date, message_id, message_valid, dump_dir=None, w=sys.stdout):
+ pass
+
+
+ def apply_message_post(self, state, issue, tags, message, message_from, message_date, message_id, message_valid, w=sys.stdout):
+ pass
diff --git a/piknik/runnable/show.py b/piknik/runnable/show.py
@@ -64,9 +64,9 @@ class PGPWrapper(PGPSigner):
self.issue = issue
- def render_message(self, envelope, messages, message_date, message_id, dump_dir=None, w=sys.stdout):
+ def render_message(self, envelope, messages, message_sender, message_date, message_id, dump_dir=None, w=sys.stdout):
for message in messages:
- self.renderer.apply_message_part(self.state, self.issue, envelope, message, self.sender, message_date, message_id, self.valid, dump_dir=dump_dir, w=w)
+ self.renderer.apply_message_part(self.state, self.issue, envelope, message, message_sender, message_date, message_id, self.valid, dump_dir=dump_dir, w=w)
#valid = '[++]'
#if not self.valid:
# valid = '[!!]'
@@ -83,6 +83,7 @@ class PGPWrapper(PGPSigner):
(envelope, message) = super(PGPWrapper, self).message_callback(envelope, message, message_id)
if envelope != None and not envelope.resolved:
+ logg.debug('have sender {}'.format(self.sender))
self.sender = envelope.sender
self.valid = envelope.valid
self.resolved = True
@@ -95,7 +96,7 @@ class PGPWrapper(PGPSigner):
if message.get('Content-Type') == 'application/pgp-signature':
#self.render_message(envelope, self.message, self.message_id, dump_dir=dump_dir)
- self.messages.append((envelope, self.part, self.message_date, self.message_id,))
+ self.messages.append((envelope, self.part, self.sender, self.message_date, self.message_id,))
self.part = []
self.message_id = None
self.message_date = None
@@ -118,7 +119,7 @@ class PGPWrapper(PGPSigner):
rg = range(len(self.messages)-1, -1, -1)
for i in rg:
v = self.messages[i]
- self.render_message(v[0], v[1], v[2], v[3], dump_dir=dump_dir)
+ self.render_message(v[0], v[1], v[2], v[3], v[4], dump_dir=dump_dir)
gpg_home = os.environ.get('GPGHOME')
@@ -148,6 +149,7 @@ def render_states(renderer, basket, states):
renderer.apply_begin()
for k in basket.states():
+ logg.debug('processing state {}'.format(k))
if k == 'FINISHED' and not arg.show_finished:
continue
@@ -167,6 +169,7 @@ def render_states(renderer, basket, states):
renderer.apply_end()
+
def process_states(renderer, basket):
results = {}
states = []
@@ -188,12 +191,16 @@ def process_states(renderer, basket):
def main():
if arg.issue_id == None:
- import piknik.render.html
- renderer = piknik.render.html.Renderer()
+ #import piknik.render.html
+ #renderer = piknik.render.html.Renderer()
+ import piknik.render.ini
+ renderer = piknik.render.ini.Renderer()
return process_states(renderer, basket)
import piknik.render.html
renderer = piknik.render.html.Renderer()
+ #import piknik.render.ini
+ #renderer = piknik.render.ini.Renderer()
issue = basket.get(arg.issue_id)
tags = basket.tags(arg.issue_id)
diff --git a/tests/test_msg.py b/tests/test_msg.py
@@ -96,5 +96,6 @@ class TestMsg(unittest.TestCase):
m = b.get_msg(v, envelope_callback=render_envelope, message_callback=render_message)
+
if __name__ == '__main__':
unittest.main()
diff --git a/tests/test_render.py b/tests/test_render.py
@@ -0,0 +1,80 @@
+# standard imports
+import unittest
+import logging
+import json
+import shutil
+import io
+import tempfile
+from email.message import Message
+from email.utils import localtime as email_localtime
+
+# external imports
+from mimeparse import parse_mime_type
+
+# local imports
+from piknik import (
+ Basket,
+ Issue,
+ )
+from piknik.msg import IssueMessage
+from piknik.render.base import Renderer
+
+# test imports
+from tests.common import TestStates
+from tests.common import TestMsgStore
+from tests.common import pgp_setup
+
+logging.basicConfig(level=logging.DEBUG)
+logg = logging.getLogger()
+
+
+def test_wrapper(p):
+ m = Message()
+ m.add_header('Foo', 'bar')
+ m.set_type('multipart/relative')
+ m.set_payload(p)
+ return m
+
+
+def test_unwrapper(msg, message_callback=None, part_callback=None):
+ for v in msg.walk():
+ if message_callback != None:
+ message_callback(v)
+
+
+
+
+class TestMsg(unittest.TestCase):
+
+ def setUp(self):
+ self.acc = []
+ def accumulate(v):
+ self.acc.append(v)
+
+ (self.crypto, self.gpg, self.gpg_dir) = pgp_setup()
+ self.store = TestStates()
+ self.b = Basket(self.store, message_wrapper=self.crypto.sign)
+ self.render_dir = tempfile.mkdtemp()
+ self.renderer = Renderer(self.b, accumulator=accumulate) #outdir=self.render_dir)
+
+
+ def tearDown(self):
+ #logg.debug('look in {}'.format(self.render_dir))
+ shutil.rmtree(self.render_dir)
+
+
+ def test_idlepass(self):
+ issue_one = Issue('foo')
+ self.b.add(issue_one)
+
+ issue_two = Issue('bar')
+ v = self.b.add(issue_two)
+
+ m = self.b.msg(v, 's:foo')
+
+ self.renderer.apply()
+ print(self.acc)
+
+
+if __name__ == '__main__':
+ unittest.main()