commit adb2c7aeeddb2687483d1a24e1b498a1af0daf9e
parent 54bc3b091052fc769897e9cef636986c8a7c85e7
Author: lash <dev@holbrook.no>
Date: Sat, 19 Nov 2022 09:01:11 +0000
Add render module, stub html rendering of states, issue page
Diffstat:
8 files changed, 229 insertions(+), 66 deletions(-)
diff --git a/html_requirements.txt b/html_requirements.txt
@@ -0,0 +1 @@
+dominate~=2.7.0
diff --git a/piknik/basket.py b/piknik/basket.py
@@ -56,6 +56,11 @@ class Basket:
return o
+ def get_state(self, issue_id):
+ v = self.state.state(issue_id)
+ return self.state.name(v)
+
+
def list(self, category=None):
if category == None:
category = self.state.BACKLOG
diff --git a/piknik/render/base.py b/piknik/render/base.py
@@ -0,0 +1,35 @@
+import sys
+
+
+class Renderer:
+
+ def apply_begin(self, w=sys.stdout):
+ pass
+
+
+ def apply_state_pre(self, state, w=sys.stdout):
+ pass
+
+
+ def apply_state(self, state, w=sys.stdout):
+ pass
+
+
+ def apply_issue_pre(self, state, issue_id, issue, tags, w=sys.stdout):
+ pass
+
+
+ def apply_issue(self, state, issue_id, issue, tags, w=sys.stdout):
+ pass
+
+
+ def apply_issue_post(self, state, issue_id, issue, tags, w=sys.stdout):
+ pass
+
+
+ def apply_state_post(self, state, w=sys.stdout):
+ pass
+
+
+ def apply_end(self, state, w=sys.stdout):
+ pass
diff --git a/piknik/render/html.py b/piknik/render/html.py
@@ -0,0 +1,66 @@
+import sys
+import os
+
+import dominate
+from dominate.tags import div, p, a, meta, ul, li, h1, h2, link
+
+from .base import Renderer as BaseRenderer
+
+
+class Renderer(BaseRenderer):
+
+ def __init__(self, outdir='/home/lash/tmp'):
+ super(Renderer, self).__init__()
+ self.issue_buf = []
+ self.state_buf = []
+ self.outdir = outdir
+
+
+ def apply_state_post(self, state, w=sys.stdout):
+ r = div(_id='state_' + state)
+ r.add(h2(state))
+ r_l = ul(_class='state_listing')
+ while True:
+ try:
+ v = self.issue_buf.pop(0)
+ r_l.add(v)
+ except IndexError:
+ break
+ r.add(r_l)
+ self.state_buf.append(r)
+
+
+ def apply_issue(self, state, issue_id, issue, tags, w=sys.stdout):
+ v = li(a(issue.title, href=issue_id + '.html'))
+ self.issue_buf.append(v)
+
+
+ def apply_message(self, state, issue_id, issue, tags, message, w=None):
+
+
+
+ def apply_issue_post(self, state, issue_id, issue, tags, w=None):
+ close = False
+ if w == None:
+ fp = os.path.join(self.outdir, issue_id + '.html')
+ w = open(fp, 'w')
+ close = True
+ r = dominate.document(title='issue: {} ({})'.format(issue.title, issue_id))
+ r.add(h1(issue.title))
+ w.write(r.render())
+
+ if close:
+ w.close()
+
+
+ def apply_end(self, w=sys.stdout):
+ r = dominate.document(title='issues for ...')
+ r.head.add(meta(name='generator', content='piknik'))
+ r.head.add(link(rel='stylesheet', href='style.css'))
+ while True:
+ try:
+ v = self.state_buf.pop(0)
+ r.add(v)
+ except IndexError:
+ break
+ w.write(r.render())
diff --git a/piknik/render/ini.py b/piknik/render/ini.py
@@ -0,0 +1,17 @@
+import sys
+
+from .base import Renderer as BaseRenderer
+
+
+class Renderer(BaseRenderer):
+
+ def apply_state(self, state, w=sys.stdout):
+ w.write('[' + state + ']\n')
+
+
+ def apply_issue(self, state, issue_id, issue, tags, w=sys.stdout):
+ w.write('{}\t{}\t{}\n'.format(issue.title, ','.join(tags), issue_id))
+
+
+ def apply_state_post(self, state, w=sys.stdout):
+ w.write('\n')
diff --git a/piknik/render/plain.py b/piknik/render/plain.py
@@ -0,0 +1,69 @@
+# standard imports
+import sys
+
+# external imports
+from mimeparse import parse_mime_type
+
+# local imports
+from .base import Renderer as BaseRenderer
+
+
+class Renderer(BaseRenderer):
+
+ def apply_issue(self, state, issue_id, issue, tags, w=sys.stdout):
+ w.write("""id: {}
+title: {}
+tags: {}
+
+""".format(
+ issue.id,
+ issue.title,
+ ', '.join(tags),
+ )
+ )
+
+ assigned = issue.get_assigned()
+
+ if len(assigned) == 0:
+ w.write('(not assigned)\n')
+ return
+
+ w.write('assigned to:\n')
+ owner = issue.owner()
+ for v in assigned:
+ o = v[0]
+ s = o.id()
+ if o == owner:
+ s += ' (owner)'
+ w.write('\t' + str(s))
+
+
+ def apply_message(self, state, issue_id, issue, tags, message, w=sys.stdout):
+ pass
+
+
+ def apply_message_part(self, state, issue, envelope, message, message_date, message_id, dump_dir=None, w=sys.stdout):
+ m = parse_mime_type(message.get_content_type())
+ filename = message.get_filename()
+
+ v = ''
+ if filename == None:
+ if m[0] == 'text':
+ if m[1] == 'plain':
+ v = message.get_payload()
+ if message.get('Content-Transfer-Encoding') == 'BASE64':
+ v = b64decode(v).decode()
+ else:
+ v = '[rich text]'
+ else:
+ if dump_dir != None:
+ v = message.get_payload()
+ if message.get('Content-Transfer-Encoding') == 'BASE64':
+ v = b64decode(v).decode()
+ filename = to_suffixed_file(dump_dir, filename, v)
+ sz = message.get('Content-Length')
+ if sz == None:
+ sz = 'unknown'
+ v = '[file: {}, type {}/{}, size: {}]'.format(filename, m[0], m[1], sz)
+
+ w.write(v)
diff --git a/piknik/runnable/list.py b/piknik/runnable/list.py
@@ -17,20 +17,26 @@ store_factory = FileStoreFactory(arg.d)
basket = Basket(store_factory)
-def render_ini(b, r):
+def render(b, r, m):
+ m.apply_begin()
+
for k in basket.states():
if k == 'FINISHED' and not arg.show_finished:
continue
- print('[' + k + ']')
+
+ m.apply_state(k)
for v in r[k]:
if k != 'BLOCKED' and v in r['BLOCKED']:
continue
o = b.get(v)
t = b.tags(v)
- print('{}\t{}\t{}'.format(o.title, ','.join(t), v))
+ m.apply_issue(k, v, o, t)
+ m.apply_issue_post(k, v, o, t)
+
+ m.apply_state_post(k)
- print()
+ m.apply_end()
def main():
@@ -49,7 +55,10 @@ def main():
for v in basket.list(category=s):
results[s].append(v)
- globals()['render_' + arg.renderer](basket, results)
+ import piknik.render.html
+ m = piknik.render.html.Renderer()
+ #globals()['render_' + arg.renderer](basket, results, m)
+ render(basket, results, m)
if __name__ == '__main__':
diff --git a/piknik/runnable/show.py b/piknik/runnable/show.py
@@ -1,4 +1,5 @@
# standard imports
+import io
import os
import sys
import argparse
@@ -7,14 +8,12 @@ import tempfile
from base64 import b64decode
from email.utils import parsedate_to_datetime
-# external imports
-from mimeparse import parse_mime_type
-
# local imports
from piknik import Basket
from piknik import Issue
from piknik.store import FileStoreFactory
from piknik.crypto import PGPSigner
+from piknik.render.plain import Renderer
#logging.basicConfig(level=logging.DEBUG)
logg = logging.getLogger()
@@ -49,7 +48,7 @@ def to_suffixed_file(d, s, data):
# TODO can implement as email parser api instead?
class PGPWrapper(PGPSigner):
- def __init__(self, home_dir=None):
+ def __init__(self, renderer, state, issue, home_dir=None):
super(PGPWrapper, self).__init__(home_dir=home_dir)
self.message_date = None
self.messages = []
@@ -57,40 +56,22 @@ class PGPWrapper(PGPSigner):
self.message_id = None
self.sender = None
self.valid = False
+ self.renderer = renderer
+ self.state = state
+ self.issue = issue
def render_message(self, envelope, messages, message_date, message_id, dump_dir=None, w=sys.stdout):
r = ''
+ w.write('\n')
for message in messages:
- m = parse_mime_type(message.get_content_type())
- filename = message.get_filename()
-
- v = ''
- if filename == None:
- if m[0] == 'text':
- if m[1] == 'plain':
- v = message.get_payload()
- if message.get('Content-Transfer-Encoding') == 'BASE64':
- v = b64decode(v).decode()
- else:
- v = '[rich text]'
- else:
- #filename = message.get_filename()
- if dump_dir != None:
- v = message.get_payload()
- if message.get('Content-Transfer-Encoding') == 'BASE64':
- v = b64decode(v).decode()
- filename = to_suffixed_file(dump_dir, filename, v)
- sz = message.get('Content-Length')
- if sz == None:
- sz = 'unknown'
- #v = '[file: ' + filename + ', type: ' + m[0] + m[1] + ', size: ' + sz + ']'
- v = '[file: {}, type {}/{}, size: {}]'.format(filename, m[0], m[1], sz)
-
+ ww = io.StringIO()
+ self.renderer.apply_message_part(self.state, self.issue, envelope, message, message_date, message_id, dump_dir=dump_dir, w=ww)
valid = '[++]'
if not self.valid:
valid = '[!!]'
- r += '\n\t' + v + '\n'
+ ww.seek(0)
+ r += '\n\t' + ww.read() + '\n'
w.write('\nmessage {} from {} {} - {}\n\t{}\n'.format(message_date, self.sender, valid, message_id, r))
@@ -143,35 +124,11 @@ class PGPWrapper(PGPSigner):
gpg_home = os.environ.get('GPGHOME')
-verifier = PGPWrapper(home_dir=gpg_home)
-def render_default(b, o, t):
- print("""id: {}
-title: {}
-tags: {}
-""".format(
- o.id,
- o.title,
- ', '.join(t),
- )
- )
-
- assigned = o.get_assigned()
-
- if len(assigned) == 0:
- print('(not assigned)')
- return
-
- print('assigned to:')
- owner = o.owner()
- for v in assigned:
- o = v[0]
- s = o.id()
- if o == owner:
- s += ' (owner)'
- print('\t' + str(s))
-
+def render(renderer, basket, state, issue, tags):
+ renderer.apply_issue(state, arg.issue_id, issue, tags)
+ verifier = PGPWrapper(renderer, state, issue, home_dir=gpg_home)
m = basket.get_msg(
arg.issue_id,
envelope_callback=verifier.envelope_callback,
@@ -181,10 +138,14 @@ tags: {}
def main():
- o = basket.get(arg.issue_id)
- t = basket.tags(arg.issue_id)
-
- globals()['render_' + arg.renderer](basket, o, t)
+ issue = basket.get(arg.issue_id)
+ tags = basket.tags(arg.issue_id)
+ state = basket.get_state(arg.issue_id)
+
+ #globals()['render_' + arg.renderer](basket, state, issue, tags)
+ import piknik.render.plain
+ renderer = piknik.render.plain.Renderer()
+ render(renderer, basket, state, issue, tags)
if __name__ == '__main__':