piknik

Unnamed repository; edit this file 'description' to name the repository.
Info | Log | Files | Refs | README | LICENSE

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:
Mpiknik/render/base.py | 6++++--
Mpiknik/render/html.py | 21++++++++++-----------
Dpiknik/render/ini.py | 28----------------------------
Mpiknik/render/plain.py | 23+++++++++++++++++++++++
Mpiknik/runnable/show.py | 170+++++++++----------------------------------------------------------------------
Mpiknik/wrap.py | 24+++++++++++++++++++-----
Mtests/test_html.py | 1-
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)