changeset 2157:b4a515e36631

jp (blog): added blog/get command: - blog/get will retrieve and parse blog items on specified service/node and can request only one or more specific items according to ids of max value - it takes profit of outputs and the new extra_outputs: a default output display microblogs as key/value, a fancy one display it in a more confortable way for reading it the terminal - for default output, keys can be filtered using --key argument
author Goffi <goffi@goffi.org>
date Thu, 16 Feb 2017 01:02:33 +0100
parents 8f96c242fa89
children 970a348d3fe9
files frontends/src/jp/cmd_blog.py
diffstat 1 files changed, 160 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/frontends/src/jp/cmd_blog.py	Thu Feb 16 00:51:33 2017 +0100
+++ b/frontends/src/jp/cmd_blog.py	Thu Feb 16 01:02:33 2017 +0100
@@ -20,7 +20,8 @@
 
 import base
 from sat.core.i18n import _
-from sat.core.constants import Const as C
+from sat_frontends.jp.constants import Const as C
+from sat.tools.common.ansi import ANSI as A
 from sat.tools import config
 from ConfigParser import NoSectionError, NoOptionError
 import json
@@ -63,8 +64,25 @@
 
 URL_REDIRECT_PREFIX = 'url_redirect_'
 INOTIFY_INSTALL = '"pip install inotify"'
-SECURE_UNLINK_MAX = 10 * 2 # we double value has there are 2 files per draft (content and metadata)
+SECURE_UNLINK_MAX = 10 * 2 # we double value as there are 2 files per draft (content and metadata)
 SECURE_UNLINK_DIR = ".backup"
+HEADER_ANSI = A.BOLD + A.FG_YELLOW
+NS_MICROBLOG = u'urn:xmpp:microblog:0'
+MB_KEYS = (u"id",
+           u"atom_id",
+           u"updated",
+           u"published",
+           u"comments",  # this key is used for all comments* keys
+           u"tags",  # this key is used for all tag* keys
+           u"author",
+           u"author_jid",
+           u"author_email",
+           u"author_jid_verified",
+           u"content",
+           u"content_xhtml",
+           u"title",
+           u"title_xhtml")
+OUTPUT_OPT_NO_HEADER = u'no-header'
 
 
 class BlogCommon(object):
@@ -169,6 +187,145 @@
             return []
 
 
+class Get(base.CommandBase):
+
+    def __init__(self, host):
+        extra_outputs = {'default': self.default_output,
+                         'fancy': self.fancy_output}
+        base.CommandBase.__init__(self, host, 'get', use_verbose=True, use_output='complex', extra_outputs=extra_outputs, help=_(u'get blog item(s)'))
+        self.need_loop=True
+
+    def add_parser_options(self):
+        self.parser.add_argument("-n", "--node", type=base.unicode_decoder, default=NS_MICROBLOG, help=_(u"PubSub node to request"))
+        self.parser.add_argument("-i", "--item", type=base.unicode_decoder, action='append', default=[], dest='items',
+                                 help=_(u"item(s) id(s) to get (default: request all items)"))
+        self.parser.add_argument("-m", "--max", type=int, default=10, help=_(u"maximum number of items to get ({} to get all items)".format(C.NO_LIMIT)))
+        # TODO: a key(s) argument to select keys to display
+        self.parser.add_argument("-k", "--key", type=base.unicode_decoder, action='append', dest='keys',
+                                 help=_(u"microblog data key(s) to display (default: depend of verbosity)"))
+        self.parser.add_argument("service", type=base.unicode_decoder, nargs='?', default=u'',
+                                 help=_(u"JID of the PubSub service (default: request profile own blog)"))
+        # TODO: add MAM filters
+
+    def format_comments(self, item, keys):
+        comments_data = data_format.dict2iterdict(u'comments', item, (u'node', u'service'), pop=True)
+        lines = []
+        for data in comments_data:
+            lines.append(data[u'comments'])
+            for k in (u'node', u'service'):
+                if OUTPUT_OPT_NO_HEADER in self.args.output_opts:
+                    header = u''
+                else:
+                    header = HEADER_ANSI + k + u': ' + A.RESET
+                lines.append(header + data[k])
+        return u'\n'.join(lines)
+
+    def format_tags(self, item, keys):
+        tags = data_format.dict2iter('tag', item, pop=True)
+        return u', '.join(tags)
+
+    def get_keys(self):
+        """return keys to display according to verbosity or explicit key request"""
+        verbosity = self.args.verbose
+        if self.args.keys:
+            if not set(MB_KEYS).issuperset(self.args.keys):
+                self.disp(u"following keys are invalid: {invalid}.\n"
+                          u"Valid keys are: {valid}.".format(
+                          invalid = u', '.join(set(self.args.keys).difference(MB_KEYS)),
+                          valid = u', '.join(sorted(MB_KEYS))),
+                          error=True)
+                self.host.quit(C.EXIT_BAD_ARG)
+            return self.args.keys
+        else:
+            if verbosity == 0:
+                return (u'title', u'content')
+            elif verbosity == 1:
+                return (u"title", u"tags", u"author", u"author_jid", u"author_email", u"author_jid_verified", u"content")
+            else:
+                return MB_KEYS
+
+    def default_output(self, data):
+        """simple key/value output"""
+        items, metadata = data
+        keys = self.get_keys()
+
+        # k_cb use format_[key] methods for complex formattings
+        k_cb = {}
+        for k in keys:
+            try:
+                callback = getattr(self, "format_" + k)
+            except AttributeError:
+                pass
+            else:
+                k_cb[k] = callback
+        for idx, item in enumerate(items):
+            for k in keys:
+                if k not in item and k not in k_cb:
+                    continue
+                if OUTPUT_OPT_NO_HEADER in self.args.output_opts:
+                    header = ''
+                else:
+                    header = u"{k_fmt}{key}:{k_fmt_e} {sep}".format(
+                    k_fmt = HEADER_ANSI,
+                    key = k,
+                    k_fmt_e = A.RESET,
+                    sep = u'\n' if 'content' in k else u'')
+                value = k_cb[k](item, keys) if k in k_cb else item[k]
+                print header + value
+            # we want a separation line after each item but the last one
+            if idx < len(items)-1:
+                print(u'')
+
+    def fancy_output(self, data):
+        """display blog is a nice to read way
+
+        this output doesn't use keys filter
+        """
+        # thanks to http://stackoverflow.com/a/943921
+        rows, columns = map(int, os.popen('stty size', 'r').read().split())
+        items, metadata = data
+        sep = A.color(A.FG_BLUE, columns * u'▬')
+        if items:
+            print(u'\n' + sep  + '\n')
+
+        for idx, item in enumerate(items):
+            title = item.get(u'title')
+            tags = list(data_format.dict2iter('tag', item, pop=True))
+            content = item.get(u'content')
+
+            if title:
+                print(A.color(A.BOLD, A.FG_CYAN, item[u'title']))
+            if tags:
+                print(A.color(A.FG_YELLOW, u', '.join(tags)))
+            if (title or tags) and content:
+                print("")
+            if content:
+                print content
+
+            print(u'\n' + sep  + '\n')
+
+
+    def mbGetCb(self, mb_result):
+        self.output(mb_result)
+        self.host.quit(C.EXIT_OK)
+
+    def mbGetEb(self, failure_):
+        self.disp(u"can't get blog items: {reason}".format(
+            reason=failure_), error=True)
+        self.host.quit(C.EXIT_BRIDGE_ERRBACK)
+
+    def start(self):
+        self.host.bridge.mbGet(
+            self.args.service,
+            self.args.node,
+            self.args.max,
+            self.args.items,
+            {},
+            self.profile,
+            callback=self.mbGetCb,
+            errback=self.mbGetEb)
+
+
 class Edit(base.CommandBase, BlogCommon):
 
     def __init__(self, host):
@@ -654,7 +811,7 @@
 
 
 class Blog(base.CommandBase):
-    subcommands = (Edit, Preview, Import)
+    subcommands = (Get, Edit, Preview, Import)
 
     def __init__(self, host):
         super(Blog, self).__init__(host, 'blog', use_profile=False, help=_('blog/microblog management'))