commit 213aa6792980336111e535295f683b9453aa7c70
parent 154ae5ea322a87c48d5273c26b1aa2fa93fb3b36
Author: lash <dev@holbrook.no>
Date: Sat, 3 Dec 2022 09:18:35 +0000
Implement file attachment in messages, dynamic load renderer in show tool
Diffstat:
7 files changed, 75 insertions(+), 198 deletions(-)
diff --git a/piknik/render/base.py b/piknik/render/base.py
@@ -18,6 +18,9 @@ class Renderer:
self.b = basket
self.a = accumulator
self.w = wrapper
+ if self.w == None:
+ logg.info('no wrapper defined. no message parts will be output')
+ self.render_mode = 0
def add(self, v, accumulator=None):
@@ -86,7 +89,6 @@ class Renderer:
def message_callback(message, message_id, message_date):
envelope = None
if self.w == None:
- logg.warning('no wrapper defined. no message parts will be output')
return (envelope, message,)
(envelope, message) = self.w.process_message(message, message_id, message_date)
@@ -114,7 +116,7 @@ class Renderer:
def apply_state_pre(self, state, accumulator=None):
- pass
+ self.render_mode = 1
def apply_state_post(self, state, accumulator=None):
diff --git a/piknik/render/html.py b/piknik/render/html.py
@@ -22,7 +22,6 @@ class Accumulator:
self.issue = None
self.msg = None
self.w = w
- #self.message = None
def add(self, v, w=None):
@@ -59,14 +58,11 @@ class Accumulator:
class Renderer(BaseRenderer):
- def __init__(self, basket, accumulator=None, wrapper=None, outdir='/tmp'):
+ def __init__(self, basket, accumulator=None, wrapper=None, outdir=None):
if accumulator == None:
accumulator = Accumulator().add
super(Renderer, self).__init__(basket, accumulator=accumulator, wrapper=wrapper)
self.outdir = outdir
- self.last_message_id = None
- self.render_mode = 0
- self.msg_idx = 0
def apply_state(self, state, accumulator=None):
@@ -143,16 +139,15 @@ class Renderer(BaseRenderer):
r.add(s)
self.add(r)
-
- #return r
-
+
def apply_message_part(self, state, issue, tags, envelope, message, message_date, message_content, accumulator=None):
+ r = None
if message_content['filename'] != None:
s = 'data:{}/{};base64,{}'.format(
message_content['type'][0],
message_content['type'][1],
- message.get_payload(),
+ message_content['contents'],
)
r = None
if message_content['type'][0] == 'image':
@@ -161,11 +156,15 @@ class Renderer(BaseRenderer):
v = os.path.basename(message_content['filename'])
r = a(v, href=s)
- s = 'd_m_{}_{}'.format(
+ else:
+ r = message_content['contents']
+
+ m_id = 'd_m_{}_{}'.format(
message_content['id'],
message_content['idx'],
)
- return div(r, _id=s)
+
+ return div(r, _id=m_id)
def apply_begin(self, accumulator=None):
diff --git a/piknik/render/ini.py b/piknik/render/ini.py
@@ -1,28 +0,0 @@
-# local imports
-from .base import Renderer as BaseRenderer
-from .base import stream_accumulator
-
-
-class Renderer(BaseRenderer):
-
- def __init__(self, basket, accumulator=stream_accumulator, **kwargs):
- super(Renderer, self).__init__(basket, accumulator=accumulator, **kwargs)
-
-
- def apply_state(self, state, accumulator=None):
- s = '[' + state + ']\n'
- self.add(s, accumulator=accumulator)
- super(Renderer, self).apply_state(state, accumulator=accumulator)
-
-
- def apply_issue(self, state, issue, tags, accumulator=None):
- s = '{}\t{}\t{}\n'.format(
- issue.title,
- ','.join(tags),
- issue.id,
- )
- self.add(s, accumulator=accumulator)
-
-
- def apply_state_post(self, state, accumulator=None):
- self.add('\n', accumulator=accumulator)
diff --git a/piknik/render/plain.py b/piknik/render/plain.py
@@ -34,6 +34,19 @@ class Renderer(BaseRenderer):
def apply_issue(self, state, issue, tags, accumulator=None):
+ if self.render_mode == 0:
+ return self.apply_issue_standalone(state, issue, tags, accumulator=accumulator)
+
+ s = '{}\t{}\t{}\n'.format(
+ issue.title,
+ ','.join(tags),
+ issue.id,
+ )
+ self.add(s, accumulator=accumulator)
+ super(Renderer, self).apply_issue(state, issue, tags, accumulator=accumulator)
+
+
+ def apply_issue_standalone(self, state, issue, tags, accumulator=None):
s = """title: {}
tags: {}
id: {}
@@ -92,3 +105,13 @@ id: {}
s = '\n\t' + v + '\n'
return s
+
+
+ def apply_state(self, state, accumulator=None):
+ s = '[' + state + ']\n'
+ self.add(s, accumulator=accumulator)
+ super(Renderer, self).apply_state(state, accumulator=accumulator)
+
+
+ def apply_state_post(self, state, accumulator=None):
+ self.add('\n', accumulator=accumulator)
diff --git a/piknik/runnable/show.py b/piknik/runnable/show.py
@@ -7,6 +7,7 @@ import logging
import tempfile
from base64 import b64decode
from email.utils import parsedate_to_datetime
+import importlib
# local imports
from piknik import Basket
@@ -15,7 +16,7 @@ from piknik.store import FileStoreFactory
from piknik.crypto import PGPSigner
from piknik.render.plain import Renderer
-logging.basicConfig(level=logging.INFO)
+logging.basicConfig(level=logging.DEBUG)
logg = logging.getLogger()
argp = argparse.ArgumentParser()
@@ -32,172 +33,39 @@ arg = argp.parse_args(sys.argv[1:])
store_factory = FileStoreFactory(arg.d)
basket = Basket(store_factory)
-
-def to_suffixed_file(d, s, data):
- (v, ext) = os.path.splitext(s)
- r = tempfile.mkstemp(suffix=ext, dir=d)
-
- f = os.fdopen(r[0], 'wb')
- try:
- f.write(data)
- except TypeError:
- f.write(data.encode('utf-8'))
- f.close()
-
- return r[1]
-
-
-class PGPWrapper(PGPSigner):
-
- def __init__(self, state, issue, home_dir=None):
- super(PGPWrapper, self).__init__(home_dir=home_dir, skip_verify=True)
- self.message_date = None
- self.messages = []
- self.part = []
- self.message_id = None
- self.sender = None
- self.valid = False
- self.state = state
- self.issue = issue
-
-
- def envelope_callback(self, envelope, envelope_type):
- envelope = super(PGPWrapper, self).envelope_callback(envelope, envelope_type)
- envelope.valid = False
- return envelope
-
-
- def message_callback(self, envelope, message, message_id, message_date):
- (envelope, message) = super(PGPWrapper, self).message_callback(envelope, message, message_id, message_date)
-
- 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
-
- if message_id == None:
- return (envelope, message,)
-
- messages = []
- if message.get('X-Piknik-Msg-Id') == None:
- 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.sender, self.message_date, self.message_id,))
- self.part = []
- self.message_id = None
- self.message_date = None
- else:
- self.part.append(message)
- else:
- d = message.get('Date')
- self.message_date = parsedate_to_datetime(d)
- self.message_id = message_id
-
- return (envelope, message,)
-
-
- def post_callback(self, messages_id):
- dump_dir = None
- if arg.f:
- dump_dir = arg.files_dir
- rg = None
- if arg.reverse:
- rg = range(0, len(self.messages))
- else:
- 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], v[4], dump_dir=dump_dir)
-
-
gpg_home = os.environ.get('GPGHOME')
-def render(renderer, basket, issue, tags):
- renderer.apply_begin()
- render_issue(renderer, basket, issue, tags)
- renderer.apply_state_post(state)
- renderer.apply_end()
-
-
-def render_issue(renderer, basket, issue, tags):
- state = basket.get_state(issue.id)
- renderer.apply_issue(state, issue, tags)
- verifier = PGPWrapper(renderer, state, issue, home_dir=gpg_home)
- m = basket.get_msg(
- issue.id,
- envelope_callback=verifier.envelope_callback,
- message_callback=verifier.message_callback,
- post_callback=verifier.post_callback,
- )
- renderer.apply_issue_post(state, issue, tags)
-
-
-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
-
- renderer.apply_state(k)
-
- for issue_id in states[k]:
- if k != 'BLOCKED' and issue_id in states['BLOCKED']:
- continue
- issue = basket.get(issue_id)
- tags = basket.tags(issue_id)
- #renderer.apply_issue(k, issue, tags)
- #renderer.apply_issue_post(k, issue, tags)
- render_issue(renderer, basket, issue, tags)
-
- renderer.apply_state_post(k)
-
- renderer.apply_end()
-
-
-
-def process_states(renderer, basket):
- results = {}
- states = []
- for s in arg.state:
- states.append(s.upper())
-
- l = len(states)
- for s in basket.states():
-
- if results.get(s) == None:
- results[s] = []
+def main():
- if l == 0 or s in states:
- for v in basket.list(category=s):
- results[s].append(v)
+ renderer = arg.renderer
+ if renderer == 'default':
+ renderer = 'piknik.render.plain'
+ elif renderer == 'html':
+ renderer = 'piknik.render.html'
- render_states(renderer, basket, results)
+ m = None
+ try:
+ m = importlib.import_module(renderer)
+ except ModuleNotFoundError:
+ renderer = 'piknik.render.' + renderer
+ m = importlib.import_module(renderer)
-def main():
if arg.issue_id == None:
- #import piknik.render.html
- #renderer = piknik.render.html.Renderer()
- import piknik.render.ini
- renderer = piknik.render.ini.Renderer(basket)
- #return process_states(renderer, basket)
+ renderer = m.Renderer(basket)
renderer.apply()
return
issue = basket.get(arg.issue_id)
tags = basket.tags(arg.issue_id)
state = basket.get_state(arg.issue_id)
- verifier = PGPWrapper(state, issue, home_dir=gpg_home)
-
- import piknik.render.plain
- renderer = piknik.render.plain.Renderer(basket, wrapper=verifier) #envelope_callback=verifier.envelope_callback, message_callback=verifier.message_callback)
+ verifier = PGPSigner(home_dir=gpg_home, skip_verify=False)
+ renderer = m.Renderer(basket, wrapper=verifier)
+ renderer.apply_begin()
renderer.apply_issue(state, issue, tags)
+ renderer.apply_end()
if __name__ == '__main__':
diff --git a/piknik/wrap.py b/piknik/wrap.py
@@ -1,5 +1,6 @@
# standard imports
import logging
+from base64 import b64decode
# external imports
from mimeparse import parse_mime_type
@@ -45,11 +46,25 @@ class Wrapper:
if message.get('Content-Transfer-Encoding') == 'BASE64':
v = b64decode(v).decode()
else:
- if self.dump_dir != None:
- v = message.get_payload()
- if message.get('Content-Transfer-Encoding') == 'BASE64':
+ v = message.get_payload()
+ if message.get('Content-Transfer-Encoding') == 'BASE64':
+ try:
v = b64decode(v).decode()
- filename = to_suffixed_file(self.dump_dir, filename, v)
+ except UnicodeDecodeError:
+ pass
+
+ if self.dump_dir != None:
+ (void, ext) = os.path.splitext(filename)
+ (fp, fn) = tempfile.mkstemp(suffix=ext, dir=self.dump_dir)
+
+ f = os.fdopen(fp, 'wb')
+ try:
+ f.write(v)
+ except TypeError:
+ f.write(v.encode('utf-8'))
+ f.close()
+
+ filename = fn
sz = message.get('Content-Length')
if sz == None:
@@ -68,7 +83,6 @@ class Wrapper:
}
self.msg_idx += 1
- logg.info('buffered content {}'.format(o))
self.content_buffer.append(o)
diff --git a/tests/test_html.py b/tests/test_html.py
@@ -49,7 +49,6 @@ class TestMsg(unittest.TestCase):
def tearDown(self):
- #logg.debug('look in {}'.format(self.render_dir))
shutil.rmtree(self.render_dir)