From af113d242b79f652000df85f275a34ae5a2bc331 Mon Sep 17 00:00:00 2001
From: Holger <holger@nahrstaedt.de>
Date: Fri, 15 Jun 2018 13:47:45 +0200
Subject: [PATCH] All dates, ints, and amounts are parsed in account, block,
 comment, vote and witness

Account
* _parse_json_data improved and all missing ints and dates added
* json() adapted to _parse_json_data()
Block
* _parse_json_data added, expiration and timestamp are now datetime objects
* property json_transactions and json_operations added, which return trx and ops with date strings
* BlockHeader improved and datetime parsing added
Blockchain
* adapted to new datetime objects
CLI
* adapted to code changes
Comment
* int and dates inside active_votes are parsed
* Missing dates and int are parsed
Market
* date parsing to datetime added to market_history
Vote
* Some improvements
Witness
* _parse_json_data added
* all date and ints are parsed
* Demo added to Witnesses, WitnessesVotedByAccount, WitnessesRankedByVote and ListWitnesses
beemgraphenebase/types
* datetime handling added to PointInTime
Unit tests
* test_account adapted
* test_export added to test_block
* test in test_comment fixed
* test_export added to test_witness
---
 README.rst                          |   8 +-
 beem/account.py                     |  65 +++++++---
 beem/block.py                       | 182 +++++++++++++++++++++++++++-
 beem/blockchain.py                  |   6 +-
 beem/cli.py                         |  11 +-
 beem/comment.py                     | 100 +++++++++------
 beem/market.py                      |  13 +-
 beem/vote.py                        |  37 +++---
 beem/witness.py                     |  89 ++++++++++++--
 beemgraphenebase/types.py           |   4 +-
 tests/beem/test_account.py          |   2 +-
 tests/beem/test_block.py            |  46 +++++++
 tests/beem/test_blockchain_batch.py |   1 +
 tests/beem/test_comment.py          |   5 +-
 tests/beem/test_witness.py          |  27 +++++
 15 files changed, 500 insertions(+), 96 deletions(-)

diff --git a/README.rst b/README.rst
index f7646693..dfed84c6 100644
--- a/README.rst
+++ b/README.rst
@@ -168,7 +168,13 @@ Changelog
 * poloniex removed and huobi and ubpit added to steem_btc_ticker()
 * Add timeout to websocket connections
 * Documentation improved by crokkon
-* time, reputation and rshares are parsed from string in all vote objects
+* "time", "reputation" and "rshares" are parsed from string in all vote objects and inside all active_votes from a comment object
+* lazy and full properly passed
+* "votes", "virtual_last_update", "virtual_position", "virtual_scheduled_time",
+    "created", "last_sbd_exchange_update", "hardfork_time_vote" are properly casted in all witness objects
+* "time" and "expiration" are parsed to a datetime object inside all block objects
+* The json() function returns the original not parsed json dict. It is available for Account, Block, BlockHeader, Comment, Vote and Witness
+* json_transactions and json_operations added to Block, for returning all dates as string
 
 0.19.37
 -------
diff --git a/beem/account.py b/beem/account.py
index 820af4c7..81ff3077 100644
--- a/beem/account.py
+++ b/beem/account.py
@@ -46,9 +46,9 @@ class Account(BlockchainObject):
         .. code-block:: python
 
             >>> from beem.account import Account
-            >>> account = Account("test")
+            >>> account = Account("gtg")
             >>> print(account)
-            <Account test>
+            <Account gtg>
             >>> print(account.balances) # doctest: +SKIP
 
         .. note:: This class comes with its own caching function to reduce the
@@ -78,6 +78,9 @@ class Account(BlockchainObject):
         """
         self.full = full
         self.lazy = lazy
+        self.steem = steem_instance or shared_steem_instance()
+        if isinstance(account, dict):
+            account = self._parse_json_data(account)
         super(Account, self).__init__(
             account,
             lazy=lazy,
@@ -85,7 +88,6 @@ class Account(BlockchainObject):
             id_item="name",
             steem_instance=steem_instance
         )
-        self._parse_json_data()
 
     def refresh(self):
         """ Refresh/Obtain an account's data from the API server
@@ -108,13 +110,27 @@ class Account(BlockchainObject):
             account = account[0]
         if not account:
             raise AccountDoesNotExistsException(self.identifier)
+        account = self._parse_json_data(account)
         self.identifier = account["name"]
         # self.steem.refresh_data()
 
         super(Account, self).__init__(account, id_item="name", lazy=self.lazy, full=self.full, steem_instance=self.steem)
-        self._parse_json_data()
 
-    def _parse_json_data(self):
+    def _parse_json_data(self, account):
+        parse_int = [
+            "sbd_seconds", "savings_sbd_seconds", "average_bandwidth", "lifetime_bandwidth", "lifetime_market_bandwidth", "reputation",
+        ]
+        for p in parse_int:
+            if p in account and isinstance(account.get(p), string_types):
+                account[p] = int(account.get(p, "0"))
+        if "proxied_vsf_votes" in account:
+            proxied_vsf_votes = []
+            for p_int in account["proxied_vsf_votes"]:
+                if isinstance(p_int, string_types):
+                    proxied_vsf_votes.append(int(p_int))
+                else:
+                    proxied_vsf_votes.append(p_int)
+            account["proxied_vsf_votes"] = proxied_vsf_votes
         parse_times = [
             "last_owner_update", "last_account_update", "created", "last_owner_proved", "last_active_proved",
             "last_account_recovery", "last_vote_time", "sbd_seconds_last_update", "sbd_last_interest_payment",
@@ -122,8 +138,8 @@ class Account(BlockchainObject):
             "last_market_bandwidth_update", "last_post", "last_root_post", "last_bandwidth_update"
         ]
         for p in parse_times:
-            if p in self and isinstance(self.get(p), string_types):
-                self[p] = formatTimeString(self.get(p, "1970-01-01T00:00:00"))
+            if p in account and isinstance(account.get(p), string_types):
+                account[p] = formatTimeString(account.get(p, "1970-01-01T00:00:00"))
         # Parse Amounts
         amounts = [
             "balance",
@@ -136,14 +152,31 @@ class Account(BlockchainObject):
             "reward_vesting_steem",
             "vesting_shares",
             "delegated_vesting_shares",
-            "received_vesting_shares"
+            "received_vesting_shares",
+            "vesting_withdraw_rate",
+            "vesting_balance",
         ]
         for p in amounts:
-            if p in self and isinstance(self.get(p), (string_types, list, dict)):
-                self[p] = Amount(self[p], steem_instance=self.steem)
+            if p in account and isinstance(account.get(p), (string_types, list, dict)):
+                account[p] = Amount(account[p], steem_instance=self.steem)
+        return account
 
     def json(self):
         output = self.copy()
+        parse_int = [
+            "sbd_seconds", "savings_sbd_seconds", "average_bandwidth", "lifetime_bandwidth", "lifetime_market_bandwidth", "reputation",
+        ]
+        for p in parse_int:
+            if p in output and isinstance(output[p], int) and output[p] != 0:
+                output[p] = str(output[p])
+        if "proxied_vsf_votes" in output:
+            proxied_vsf_votes = []
+            for p_int in output["proxied_vsf_votes"]:
+                if isinstance(p_int, int) and p_int != 0:
+                    proxied_vsf_votes.append(str(p_int))
+                else:
+                    proxied_vsf_votes.append(p_int)
+            output["proxied_vsf_votes"] = proxied_vsf_votes
         parse_times = [
             "last_owner_update", "last_account_update", "created", "last_owner_proved", "last_active_proved",
             "last_account_recovery", "last_vote_time", "sbd_seconds_last_update", "sbd_last_interest_payment",
@@ -152,11 +185,11 @@ class Account(BlockchainObject):
         ]
         for p in parse_times:
             if p in output:
-                date = output.get(p, datetime(1970, 1, 1, 0, 0))
-                if isinstance(date, (datetime, date, time)):
-                    output[p] = formatTimeString(date)
+                p_date = output.get(p, datetime(1970, 1, 1, 0, 0))
+                if isinstance(p_date, (datetime, date, time)):
+                    output[p] = formatTimeString(p_date)
                 else:
-                    output[p] = date
+                    output[p] = p_date
         amounts = [
             "balance",
             "savings_balance",
@@ -168,7 +201,9 @@ class Account(BlockchainObject):
             "reward_vesting_steem",
             "vesting_shares",
             "delegated_vesting_shares",
-            "received_vesting_shares"
+            "received_vesting_shares",
+            "vesting_withdraw_rate",
+            "vesting_balance"
         ]
         for p in amounts:
             if p in output:
diff --git a/beem/block.py b/beem/block.py
index c369d472..e8657c4b 100644
--- a/beem/block.py
+++ b/beem/block.py
@@ -3,10 +3,13 @@ from __future__ import absolute_import
 from __future__ import division
 from __future__ import print_function
 from __future__ import unicode_literals
+from datetime import datetime, timedelta, date
+import json
 from .exceptions import BlockDoesNotExistsException
-from .utils import parse_time
+from .utils import parse_time, formatTimeString
 from .blockchainobject import BlockchainObject
 from beemapi.exceptions import ApiNotSupported
+from beemgraphenebase.py23 import bytes_types, integer_types, string_types, text_type
 
 
 class Block(BlockchainObject):
@@ -62,6 +65,10 @@ class Block(BlockchainObject):
         self.lazy = lazy
         self.only_ops = only_ops
         self.only_virtual_ops = only_virtual_ops
+        if isinstance(block, float):
+            block = int(block)
+        elif isinstance(block, dict):
+            block = self._parse_json_data(block)
         super(Block, self).__init__(
             block,
             lazy=lazy,
@@ -69,6 +76,65 @@ class Block(BlockchainObject):
             steem_instance=steem_instance
         )
 
+    def _parse_json_data(self, block):
+        parse_times = [
+            "timestamp",
+        ]
+        for p in parse_times:
+            if p in block and isinstance(block.get(p), string_types):
+                block[p] = formatTimeString(block.get(p, "1970-01-01T00:00:00"))
+        if "transactions" in block:
+            new_transactions = []
+            for trx in block["transactions"]:
+                if 'expiration' in trx and isinstance(trx.get('expiration'), string_types):
+                    trx['expiration'] = formatTimeString(trx.get('expiration', "1970-01-01T00:00:00"))
+                new_transactions.append(trx)
+            block["transactions"] = new_transactions
+        elif "operations" in block:
+            new_operations = []
+            for trx in block["operations"]:
+                if 'timestamp' in trx and isinstance(trx.get('timestamp'), string_types):
+                    trx['timestamp'] = formatTimeString(trx.get('timestamp', "1970-01-01T00:00:00"))
+                new_operations.append(trx)
+            block["operations"] = new_operations
+        return block
+
+    def json(self):
+        output = self.copy()
+        parse_times = [
+            "timestamp",
+        ]
+        for p in parse_times:
+            if p in output:
+                p_date = output.get(p, datetime(1970, 1, 1, 0, 0))
+                if isinstance(p_date, (datetime, date)):
+                    output[p] = formatTimeString(p_date)
+                else:
+                    output[p] = p_date
+        if "transactions" in output:
+            new_transactions = []
+            for trx in output["transactions"]:
+                if 'expiration' in trx:
+                    p_date = trx.get('expiration', datetime(1970, 1, 1, 0, 0))
+                    if isinstance(p_date, (datetime, date)):
+                        trx['expiration'] = formatTimeString(p_date)
+                    else:
+                        trx['expiration'] = p_date
+                new_transactions.append(trx)
+            output["transactions"] = new_transactions
+        elif "operations" in output:
+            new_operations = []
+            for trx in output["operations"]:
+                if 'timestamp' in trx:
+                    p_date = trx.get('timestamp', datetime(1970, 1, 1, 0, 0))
+                    if isinstance(p_date, (datetime, date)):
+                        trx['timestamp'] = formatTimeString(p_date)
+                    else:
+                        trx['timestamp'] = p_date
+                new_operations.append(trx)
+            output["operations"] = new_operations
+        return json.loads(str(json.dumps(output)))
+
     def refresh(self):
         """ Even though blocks never change, you freshly obtain its contents
             from an API with this method
@@ -103,6 +169,7 @@ class Block(BlockchainObject):
                 block = self.steem.rpc.get_block(self.identifier)
         if not block:
             raise BlockDoesNotExistsException(str(self.identifier))
+        block = self._parse_json_data(block)
         super(Block, self).__init__(block, lazy=self.lazy, full=self.full, steem_instance=self.steem)
 
     @property
@@ -112,7 +179,7 @@ class Block(BlockchainObject):
 
     def time(self):
         """Return a datatime instance for the timestamp of this block"""
-        return parse_time(self['timestamp'])
+        return self['timestamp']
 
     @property
     def transactions(self):
@@ -142,6 +209,46 @@ class Block(BlockchainObject):
                 ops.append(op)
         return ops
 
+    @property
+    def json_transactions(self):
+        """ Returns all transactions as list, all dates are strings."""
+        if self.only_ops or self.only_virtual_ops:
+            return list()
+        trxs = self["transactions"]
+        for i in range(len(trxs)):
+            trx = trxs[i]
+            trx['transaction_id'] = self['transaction_ids'][i]
+            trx['block_num'] = self.block_num
+            trx['transaction_num'] = i
+            if 'expiration' in trx:
+                p_date = trx.get('expiration', datetime(1970, 1, 1, 0, 0))
+                if isinstance(p_date, (datetime, date)):
+                    trx['expiration'] = formatTimeString(p_date)
+                else:
+                    trx['expiration'] = p_date
+            trxs[i] = trx
+        return trxs
+
+    @property
+    def json_operations(self):
+        """Returns all block operations as list, all dates are strings."""
+        if self.only_ops or self.only_virtual_ops:
+            return self["operations"]
+        ops = []
+        trxs = self["transactions"]
+        for tx in trxs:
+            for op in tx["operations"]:
+                # Replace opid by op name
+                # op[0] = getOperationNameForId(op[0])
+                if 'timestamp' in op:
+                    p_date = op.get('timestamp', datetime(1970, 1, 1, 0, 0))
+                    if isinstance(p_date, (datetime, date)):
+                        op['timestamp'] = formatTimeString(p_date)
+                    else:
+                        op['timestamp'] = p_date
+                ops.append(op)
+        return ops
+
     def ops_statistics(self, add_to_ops_stat=None):
         """Returns a statistic with the occurrence of the different operation types"""
         if add_to_ops_stat is None:
@@ -166,6 +273,49 @@ class Block(BlockchainObject):
 
 
 class BlockHeader(BlockchainObject):
+    """ Read a single block header from the chain
+
+        :param int block: block number
+        :param beem.steem.Steem steem_instance: Steem
+            instance
+        :param bool lazy: Use lazy loading
+
+        In addition to the block data, the block number is stored as self["id"] or self.identifier.
+
+        .. code-block:: python
+
+            >>> from beem.block import BlockHeader
+            >>> block = BlockHeader(1)
+            >>> print(block)
+            <BlockHeader 1>
+
+    """
+    def __init__(
+        self,
+        block,
+        full=True,
+        lazy=False,
+        steem_instance=None
+    ):
+        """ Initilize a block
+
+            :param int block: block number
+            :param beem.steem.Steem steem_instance: Steem
+                instance
+            :param bool lazy: Use lazy loading
+
+        """
+        self.full = full
+        self.lazy = lazy
+        if isinstance(block, float):
+            block = int(block)
+        super(BlockHeader, self).__init__(
+            block,
+            lazy=lazy,
+            full=full,
+            steem_instance=steem_instance
+        )
+
     def refresh(self):
         """ Even though blocks never change, you freshly obtain its contents
             from an API with this method
@@ -181,17 +331,41 @@ class BlockHeader(BlockchainObject):
             block = self.steem.rpc.get_block_header(self.identifier)
         if not block:
             raise BlockDoesNotExistsException(str(self.identifier))
+        block = self._parse_json_data(block)
         super(BlockHeader, self).__init__(
-            block,
+            block, lazy=self.lazy, full=self.full,
             steem_instance=self.steem
         )
 
     def time(self):
         """ Return a datatime instance for the timestamp of this block
         """
-        return parse_time(self['timestamp'])
+        return self['timestamp']
 
     @property
     def block_num(self):
         """Returns the block number"""
         return self.identifier
+
+    def _parse_json_data(self, block):
+        parse_times = [
+            "timestamp",
+        ]
+        for p in parse_times:
+            if p in block and isinstance(block.get(p), string_types):
+                block[p] = formatTimeString(block.get(p, "1970-01-01T00:00:00"))
+        return block
+
+    def json(self):
+        output = self.copy()
+        parse_times = [
+            "timestamp",
+        ]
+        for p in parse_times:
+            if p in output:
+                p_date = output.get(p, datetime(1970, 1, 1, 0, 0))
+                if isinstance(p_date, (datetime, date)):
+                    output[p] = formatTimeString(p_date)
+                else:
+                    output[p] = p_date
+        return json.loads(str(json.dumps(output)))
diff --git a/beem/blockchain.py b/beem/blockchain.py
index 94f02be2..a57c667f 100644
--- a/beem/blockchain.py
+++ b/beem/blockchain.py
@@ -481,7 +481,7 @@ class Blockchain(object):
                         trx_id = block["transaction_ids"][trx_nr]
                         block_num = block.get("id")
                         _id = self.hash_op(event)
-                        timestamp = formatTimeString(block.get("timestamp"))
+                        timestamp = block.get("timestamp")
                     elif isinstance(event, dict) and "type" in event and "value" in event:
                         op_type = event["type"]
                         if len(op_type) > 10 and op_type[len(op_type) - 10:] == "_operation":
@@ -490,13 +490,13 @@ class Blockchain(object):
                         trx_id = block["transaction_ids"][trx_nr]
                         block_num = block.get("id")
                         _id = self.hash_op(event)
-                        timestamp = formatTimeString(block.get("timestamp"))
+                        timestamp = block.get("timestamp")
                     else:
                         op_type, op = event["op"]
                         trx_id = event.get("trx_id")
                         block_num = event.get("block")
                         _id = self.hash_op(event["op"])
-                        timestamp = formatTimeString(event.get("timestamp"))
+                        timestamp = event.get("timestamp")
                     if not opNames or op_type in opNames:
                         if raw_ops:
                             yield {"block_num": block_num,
diff --git a/beem/cli.py b/beem/cli.py
index 707729b7..0d2e2738 100644
--- a/beem/cli.py
+++ b/beem/cli.py
@@ -2221,7 +2221,7 @@ def curation(authorperm, account, limit, min_vote, max_vote, min_performance, ma
             else:
                 performance = 0
                 penalty = 0
-            vote_befor_min = ((formatTimeString(vote["time"]) - comment["created"]).total_seconds() / 60)
+            vote_befor_min = (((vote["time"]) - comment["created"]).total_seconds() / 60)
             sum_curation[0] += vote_SBD
             sum_curation[1] += penalty
             sum_curation[2] += float(curation_SP)
@@ -2413,7 +2413,7 @@ def rewards(accounts, only_sum, post, comment, curation, length, author, permlin
                             permlink_row = c.permlink
                     rows.append([c["author"],
                                  permlink_row,
-                                 ((now - formatTimeString(v["timestamp"])).total_seconds() / 60 / 60 / 24),
+                                 ((now - (v["timestamp"])).total_seconds() / 60 / 60 / 24),
                                  (payout_SBD),
                                  (payout_STEEM),
                                  (payout_SP),
@@ -2433,7 +2433,7 @@ def rewards(accounts, only_sum, post, comment, curation, length, author, permlin
                         permlink_row = v["comment_permlink"]
                     rows.append([v["comment_author"],
                                  permlink_row,
-                                 ((now - formatTimeString(v["timestamp"])).total_seconds() / 60 / 60 / 24),
+                                 ((now - (v["timestamp"])).total_seconds() / 60 / 60 / 24),
                                  0.000,
                                  0.000,
                                  payout_SP,
@@ -2865,8 +2865,9 @@ def info(objects):
             if block:
                 t = PrettyTable(["Key", "Value"])
                 t.align = "l"
-                for key in sorted(block):
-                    value = block[key]
+                block_json = block.json()
+                for key in sorted(block_json):
+                    value = block_json[key]
                     if key == "transactions" and not bool(tran_nr):
                         t.add_row(["Nr. of transactions", len(value)])
                     elif key == "transactions" and bool(tran_nr):
diff --git a/beem/comment.py b/beem/comment.py
index c8ab3448..b6fdfbd7 100644
--- a/beem/comment.py
+++ b/beem/comment.py
@@ -53,13 +53,16 @@ class Comment(BlockchainObject):
     ):
         self.full = full
         self.lazy = lazy
+        self.steem = steem_instance or shared_steem_instance()
         if isinstance(authorperm, string_types) and authorperm != "":
             [author, permlink] = resolve_authorperm(authorperm)
             self["id"] = 0
             self["author"] = author
             self["permlink"] = permlink
+            self["authorperm"] = authorperm
         elif isinstance(authorperm, dict) and "author" in authorperm and "permlink" in authorperm:
             authorperm["authorperm"] = construct_authorperm(authorperm["author"], authorperm["permlink"])
+            authorperm = self._parse_json_data(authorperm)
         super(Comment, self).__init__(
             authorperm,
             id_item="authorperm",
@@ -67,18 +70,15 @@ class Comment(BlockchainObject):
             full=full,
             steem_instance=steem_instance
         )
-        if "author" in self and "permlink" in self:
-            self.identifier = construct_authorperm(self["author"], self["permlink"])
-        self._parse_json_data()
 
-    def _parse_json_data(self):
+    def _parse_json_data(self, comment):
         parse_times = [
             "active", "cashout_time", "created", "last_payout", "last_update",
             "max_cashout_time"
         ]
         for p in parse_times:
-            if p in self and isinstance(self.get(p), string_types):
-                self[p] = formatTimeString(self.get(p, "1970-01-01T00:00:00"))
+            if p in comment and isinstance(comment.get(p), string_types):
+                comment[p] = formatTimeString(comment.get(p, "1970-01-01T00:00:00"))
         # Parse Amounts
         sbd_amounts = [
             "total_payout_value",
@@ -89,36 +89,48 @@ class Comment(BlockchainObject):
             "promoted",
         ]
         for p in sbd_amounts:
-            if p in self and isinstance(self.get(p), (string_types, list, dict)):
-                self[p] = Amount(self.get(p, "0.000 SBD"), steem_instance=self.steem)
+            if p in comment and isinstance(comment.get(p), (string_types, list, dict)):
+                comment[p] = Amount(comment.get(p, "0.000 SBD"), steem_instance=self.steem)
 
         # turn json_metadata into python dict
-        self._metadata_to_dict()
-        self["tags"] = []
-        self['community'] = ''
-        if isinstance(self['json_metadata'], dict):
-            if "tags" in self['json_metadata']:
-                self["tags"] = self['json_metadata']["tags"]
-            if 'community' in self['json_metadata']:
-                self['community'] = self['json_metadata']['community']
+        meta_str = comment.get("json_metadata", "{}")
+        if meta_str == "{}":
+            comment['json_metadata'] = meta_str
+        if isinstance(meta_str, (string_types, bytes_types, bytearray)):
+            try:
+                comment['json_metadata'] = json.loads(meta_str)
+            except:
+                comment['json_metadata'] = {}
+
+        comment["tags"] = []
+        comment['community'] = ''
+        if isinstance(comment['json_metadata'], dict):
+            if "tags" in comment['json_metadata']:
+                comment["tags"] = comment['json_metadata']["tags"]
+            if 'community' in comment['json_metadata']:
+                comment['community'] = comment['json_metadata']['community']
 
         parse_int = [
             "author_reputation",
         ]
         for p in parse_int:
-            if p in self and isinstance(self.get(p), string_types):
-                self[p] = int(self.get(p, "0"))
-
-    def _metadata_to_dict(self):
-        """turn json_metadata into python dict"""
-        meta_str = self.get("json_metadata", "{}")
-        if meta_str == "{}":
-            self['json_metadata'] = meta_str
-        if isinstance(meta_str, (string_types, bytes_types, bytearray)):
-            try:
-                self['json_metadata'] = json.loads(meta_str)
-            except:
-                self['json_metadata'] = {}
+            if p in comment and isinstance(comment.get(p), string_types):
+                comment[p] = int(comment.get(p, "0"))
+
+        if "active_votes" in comment:
+            new_active_votes = []
+            for vote in comment["active_votes"]:
+                if 'time' in vote and isinstance(vote.get('time'), string_types):
+                    vote['time'] = formatTimeString(vote.get('time', "1970-01-01T00:00:00"))
+                parse_int = [
+                    "rshares", "reputation",
+                ]
+                for p in parse_int:
+                    if p in vote and isinstance(vote.get(p), string_types):
+                        vote[p] = int(vote.get(p, "0"))
+                new_active_votes.append(vote)
+            comment["active_votes"] = new_active_votes
+        return comment
 
     def refresh(self):
         if self.identifier == "":
@@ -133,10 +145,9 @@ class Comment(BlockchainObject):
             content = self.steem.rpc.get_content(author, permlink)
         if not content or not content['author'] or not content['permlink']:
             raise ContentDoesNotExistsException(self.identifier)
+        content = self._parse_json_data(content)
+        content["authorperm"] = construct_authorperm(content['author'], content['permlink'])
         super(Comment, self).__init__(content, id_item="authorperm", lazy=self.lazy, full=self.full, steem_instance=self.steem)
-        self["authorperm"] = construct_authorperm(self["author"], self["permlink"])
-        self.identifier = self["authorperm"]
-        self._parse_json_data()
 
     def json(self):
         output = self.copy()
@@ -154,11 +165,11 @@ class Comment(BlockchainObject):
         ]
         for p in parse_times:
             if p in output:
-                date = output.get(p, datetime(1970, 1, 1, 0, 0))
-                if isinstance(date, (datetime, date)):
-                    output[p] = formatTimeString(date)
+                p_date = output.get(p, datetime(1970, 1, 1, 0, 0))
+                if isinstance(p_date, (datetime, date)):
+                    output[p] = formatTimeString(p_date)
                 else:
-                    output[p] = date
+                    output[p] = p_date
         sbd_amounts = [
             "total_payout_value",
             "max_accepted_payout",
@@ -176,6 +187,23 @@ class Comment(BlockchainObject):
         for p in parse_int:
             if p in output and isinstance(output[p], int):
                 output[p] = str(output[p])
+        if "active_votes" in output:
+            new_active_votes = []
+            for vote in output["active_votes"]:
+                if 'time' in vote:
+                    p_date = vote.get('time', datetime(1970, 1, 1, 0, 0))
+                    if isinstance(p_date, (datetime, date)):
+                        vote['time'] = formatTimeString(p_date)
+                    else:
+                        vote['time'] = p_date
+                parse_int = [
+                    "rshares", "reputation",
+                ]
+                for p in parse_int:
+                    if p in vote and isinstance(vote[p], int):
+                        vote[p] = str(vote[p])
+                new_active_votes.append(vote)
+            output["active_votes"] = new_active_votes
         return json.loads(str(json.dumps(output)))
 
     @property
diff --git a/beem/market.py b/beem/market.py
index 7afde7f3..03773804 100644
--- a/beem/market.py
+++ b/beem/market.py
@@ -16,6 +16,7 @@ from .amount import Amount
 from .price import Price, Order, FilledOrder
 from .account import Account
 from beembase import operations
+from beemgraphenebase.py23 import bytes_types, integer_types, string_types, text_type
 REQUEST_MODULE = None
 if not REQUEST_MODULE:
     try:
@@ -404,7 +405,7 @@ class Market(dict):
         else:
             return ret
 
-    def market_history(self, bucket_seconds=300, start_age=3600, end_age=0):
+    def market_history(self, bucket_seconds=300, start_age=3600, end_age=0, raw_data=False):
         """ Return the market history (filled orders).
 
             :param int bucket_seconds: Bucket size in seconds (see
@@ -413,6 +414,7 @@ class Market(dict):
                 window (default: 1h/3600)
             :param int end_age: Age (in seconds) of the end of the window
                 (default: now/0)
+            :param bool raw_data: (optional) returns raw data if set True
 
             Example:
                 .. code-block:: js
@@ -451,7 +453,14 @@ class Market(dict):
                 formatTimeFromNow(-start_age - end_age),
                 formatTimeFromNow(-end_age),
                 api="market_history")
-        return history
+        if raw_data:
+            return history
+        new_history = []
+        for h in history:
+            if 'open' in h and isinstance(h.get('open'), string_types):
+                h['open'] = formatTimeString(h.get('open', "1970-01-01T00:00:00"))
+            new_history.append(h)
+        return new_history
 
     def accountopenorders(self, account=None, raw_data=False):
         """ Returns open Orders
diff --git a/beem/vote.py b/beem/vote.py
index b6f91816..ee1d3c3d 100644
--- a/beem/vote.py
+++ b/beem/vote.py
@@ -9,7 +9,7 @@ import math
 import pytz
 import logging
 from prettytable import PrettyTable
-from datetime import datetime
+from datetime import datetime, date
 from beemgraphenebase.py23 import integer_types, string_types, text_type
 from .instance import shared_steem_instance
 from .account import Account
@@ -46,6 +46,7 @@ class Vote(BlockchainObject):
     ):
         self.full = full
         self.lazy = lazy
+        self.steem = steem_instance or shared_steem_instance()
         if isinstance(voter, string_types) and authorperm is not None:
             [author, permlink] = resolve_authorperm(authorperm)
             self["voter"] = voter
@@ -56,6 +57,7 @@ class Vote(BlockchainObject):
         elif isinstance(voter, dict) and "author" in voter and "permlink" in voter and "voter" in voter:
             authorpermvoter = voter
             authorpermvoter["authorpermvoter"] = construct_authorpermvoter(voter["author"], voter["permlink"], voter["voter"])
+            authorpermvoter = self._parse_json_data(authorpermvoter)
         elif isinstance(voter, dict) and "authorperm" in voter and authorperm is not None:
             [author, permlink] = resolve_authorperm(voter["authorperm"])
             authorpermvoter = voter
@@ -63,12 +65,14 @@ class Vote(BlockchainObject):
             authorpermvoter["author"] = author
             authorpermvoter["permlink"] = permlink
             authorpermvoter["authorpermvoter"] = construct_authorpermvoter(author, permlink, authorperm)
+            authorpermvoter = self._parse_json_data(authorpermvoter)
         elif isinstance(voter, dict) and "voter" in voter and authorperm is not None:
             [author, permlink] = resolve_authorperm(authorperm)
             authorpermvoter = voter
             authorpermvoter["author"] = author
             authorpermvoter["permlink"] = permlink
             authorpermvoter["authorpermvoter"] = construct_authorpermvoter(author, permlink, voter["voter"])
+            authorpermvoter = self._parse_json_data(authorpermvoter)
         else:
             authorpermvoter = voter
             [author, permlink, voter] = resolve_authorpermvoter(authorpermvoter)
@@ -82,7 +86,6 @@ class Vote(BlockchainObject):
             full=full,
             steem_instance=steem_instance
         )
-        self._parse_json_data()
 
     def refresh(self):
         if self.identifier is None:
@@ -105,25 +108,25 @@ class Vote(BlockchainObject):
                 vote = x
         if not vote:
             raise VoteDoesNotExistsException(self.identifier)
+        vote = self._parse_json_data(vote)
+        vote["authorpermvoter"] = construct_authorpermvoter(author, permlink, voter)
         super(Vote, self).__init__(vote, id_item="authorpermvoter", lazy=self.lazy, full=self.full, steem_instance=self.steem)
 
-        self.identifier = construct_authorpermvoter(author, permlink, voter)
-        self._parse_json_data()
-
-    def _parse_json_data(self):
+    def _parse_json_data(self, vote):
         parse_int = [
             "rshares", "reputation",
         ]
         for p in parse_int:
-            if p in self and isinstance(self.get(p), string_types):
-                self[p] = int(self.get(p, "0"))
+            if p in vote and isinstance(vote.get(p), string_types):
+                vote[p] = int(vote.get(p, "0"))
 
-        if "time" in self and isinstance(self.get("time"), string_types) and self.get("time") != '':
-            self["time"] = formatTimeString(self.get("time", "1970-01-01T00:00:00"))
-        elif "timestamp" in self and isinstance(self.get("timestamp"), string_types) and self.get("timestamp") != '':
-            self["time"] = formatTimeString(self.get("timestamp", "1970-01-01T00:00:00"))
+        if "time" in vote and isinstance(vote.get("time"), string_types) and vote.get("time") != '':
+            vote["time"] = formatTimeString(vote.get("time", "1970-01-01T00:00:00"))
+        elif "timestamp" in vote and isinstance(vote.get("timestamp"), string_types) and vote.get("timestamp") != '':
+            vote["time"] = formatTimeString(vote.get("timestamp", "1970-01-01T00:00:00"))
         else:
-            self["time"] = formatTimeString("1970-01-01T00:00:00")
+            vote["time"] = formatTimeString("1970-01-01T00:00:00")
+        return vote
 
     def json(self):
         output = self.copy()
@@ -136,11 +139,11 @@ class Vote(BlockchainObject):
         ]
         for p in parse_times:
             if p in output:
-                date = output.get(p, datetime(1970, 1, 1, 0, 0))
-                if isinstance(date, (datetime, date)):
-                    output[p] = formatTimeString(date)
+                p_date = output.get(p, datetime(1970, 1, 1, 0, 0))
+                if isinstance(p_date, (datetime, date)):
+                    output[p] = formatTimeString(p_date)
                 else:
-                    output[p] = date
+                    output[p] = p_date
         parse_int = [
             "rshares", "reputation",
         ]
diff --git a/beem/witness.py b/beem/witness.py
index 24e7c188..820afd93 100644
--- a/beem/witness.py
+++ b/beem/witness.py
@@ -4,6 +4,7 @@ from __future__ import division
 from __future__ import print_function
 from __future__ import unicode_literals
 from builtins import str
+import json
 from beem.instance import shared_steem_instance
 from beemgraphenebase.py23 import bytes_types, integer_types, string_types, text_type
 from .account import Account
@@ -11,7 +12,7 @@ from .amount import Amount
 from .exceptions import WitnessDoesNotExistsException
 from .blockchainobject import BlockchainObject
 from .utils import formatTimeString
-from datetime import datetime, timedelta
+from datetime import datetime, timedelta, date
 from beembase import transactions, operations
 from beemgraphenebase.account import PrivateKey, PublicKey
 import pytz
@@ -43,6 +44,9 @@ class Witness(BlockchainObject):
     ):
         self.full = full
         self.lazy = lazy
+        self.steem = steem_instance or shared_steem_instance()
+        if isinstance(owner, dict):
+            owner = self._parse_json_data(owner)
         super(Witness, self).__init__(
             owner,
             lazy=lazy,
@@ -65,8 +69,43 @@ class Witness(BlockchainObject):
             witness = self.steem.rpc.get_witness_by_account(self.identifier)
         if not witness:
             raise WitnessDoesNotExistsException(self.identifier)
+        witness = self._parse_json_data(witness)
         super(Witness, self).__init__(witness, id_item="owner", lazy=self.lazy, full=self.full, steem_instance=self.steem)
-        self.identifier = self["owner"]
+
+    def _parse_json_data(self, witness):
+        parse_times = [
+            "created", "last_sbd_exchange_update", "hardfork_time_vote",
+        ]
+        for p in parse_times:
+            if p in witness and isinstance(witness.get(p), string_types):
+                witness[p] = formatTimeString(witness.get(p, "1970-01-01T00:00:00"))
+        parse_int = [
+            "votes", "virtual_last_update", "virtual_position", "virtual_scheduled_time",
+        ]
+        for p in parse_int:
+            if p in witness and isinstance(witness.get(p), string_types):
+                witness[p] = int(witness.get(p, "0"))
+        return witness
+
+    def json(self):
+        output = self.copy()
+        parse_times = [
+            "created", "last_sbd_exchange_update", "hardfork_time_vote",
+        ]
+        for p in parse_times:
+            if p in output:
+                p_date = output.get(p, datetime(1970, 1, 1, 0, 0))
+                if isinstance(p_date, (datetime, date)):
+                    output[p] = formatTimeString(p_date)
+                else:
+                    output[p] = p_date
+        parse_int = [
+            "votes", "virtual_last_update", "virtual_position", "virtual_scheduled_time",
+        ]
+        for p in parse_int:
+            if p in output and isinstance(output[p], int):
+                output[p] = str(output[p])
+        return json.loads(str(json.dumps(output)))
 
     @property
     def account(self):
@@ -156,7 +195,7 @@ class WitnessesObject(list):
         elif sort_key == 'quote':
             sortedList = sorted(self, key=lambda self: self['sbd_exchange_rate']['quote'], reverse=reverse)
         elif sort_key == 'last_sbd_exchange_update':
-            sortedList = sorted(self, key=lambda self: (utc.localize(datetime.utcnow()) - formatTimeString(self['last_sbd_exchange_update'])).total_seconds(), reverse=reverse)
+            sortedList = sorted(self, key=lambda self: (utc.localize(datetime.utcnow()) - self['last_sbd_exchange_update']).total_seconds(), reverse=reverse)
         elif sort_key == 'account_creation_fee':
             sortedList = sorted(self, key=lambda self: self['props']['account_creation_fee'], reverse=reverse)
         elif sort_key == 'sbd_interest_rate':
@@ -168,7 +207,7 @@ class WitnessesObject(list):
         else:
             sortedList = sorted(self, key=lambda self: self[sort_key], reverse=reverse)
         for witness in sortedList:
-            td = utc.localize(datetime.utcnow()) - formatTimeString(witness['last_sbd_exchange_update'])
+            td = utc.localize(datetime.utcnow()) - witness['last_sbd_exchange_update']
             disabled = ""
             if not witness.is_active:
                 disabled = "yes"
@@ -219,8 +258,15 @@ class Witnesses(WitnessesObject):
 
         :param steem steem_instance: Steem() instance to use when
             accesing a RPC
+
+        .. code-block:: python
+
+           >>> from beem.witness import Witnesses
+           >>> Witnesses()
+           <Witnesses >
+
     """
-    def __init__(self, lazy=False, full=False, steem_instance=None):
+    def __init__(self, lazy=False, full=True, steem_instance=None):
         self.steem = steem_instance or shared_steem_instance()
         self.steem.rpc.set_next_node_on_empty_reply(False)
         if self.steem.rpc.get_use_appbase():
@@ -245,8 +291,15 @@ class WitnessesVotedByAccount(WitnessesObject):
         :param str account: Account name
         :param steem steem_instance: Steem() instance to use when
             accesing a RPC
+
+        .. code-block:: python
+
+           >>> from beem.witness import WitnessesVotedByAccount
+           >>> WitnessesVotedByAccount("gtg")
+           <WitnessesVotedByAccount gtg>
+
     """
-    def __init__(self, account, lazy=False, full=False, steem_instance=None):
+    def __init__(self, account, lazy=False, full=True, steem_instance=None):
         self.steem = steem_instance or shared_steem_instance()
         self.account = Account(account, full=True, steem_instance=self.steem)
         account_name = self.account["name"]
@@ -276,9 +329,17 @@ class WitnessesVotedByAccount(WitnessesObject):
 class WitnessesRankedByVote(WitnessesObject):
     """ Obtain a list of witnesses ranked by Vote
 
-        :param str from_account: Witness name
+        :param str from_account: Witness name from which the lists starts (default = "")
+        :param int limit: Limits the number of shown witnesses (default = 100)
         :param steem steem_instance: Steem() instance to use when
             accesing a RPC
+
+        .. code-block:: python
+
+           >>> from beem.witness import WitnessesRankedByVote
+           >>> WitnessesRankedByVote(limit=100)
+           <WitnessesRankedByVote >
+
     """
     def __init__(self, from_account="", limit=100, lazy=False, full=False, steem_instance=None):
         self.steem = steem_instance or shared_steem_instance()
@@ -321,13 +382,21 @@ class WitnessesRankedByVote(WitnessesObject):
 
 
 class ListWitnesses(WitnessesObject):
-    """ Obtain a list of witnesses which have been voted by an account
+    """ List witnesses ranked by name
 
-        :param str from_account: Account name
+        :param str from_account: Witness name from which the lists starts (default = "")
+        :param int limit: Limits the number of shown witnesses (default = 100)
         :param steem steem_instance: Steem() instance to use when
             accesing a RPC
+
+        .. code-block:: python
+
+           >>> from beem.witness import ListWitnesses
+           >>> ListWitnesses(from_account="gtg", limit=100)
+           <ListWitnesses gtg>
+
     """
-    def __init__(self, from_account, limit, lazy=False, full=False, steem_instance=None):
+    def __init__(self, from_account="", limit=100, lazy=False, full=False, steem_instance=None):
         self.steem = steem_instance or shared_steem_instance()
         self.identifier = from_account
         self.steem.rpc.set_next_node_on_empty_reply(False)
diff --git a/beemgraphenebase/types.py b/beemgraphenebase/types.py
index b95af895..8fc72b75 100644
--- a/beemgraphenebase/types.py
+++ b/beemgraphenebase/types.py
@@ -264,7 +264,9 @@ class PointInTime(object):
 
     def __bytes__(self):
         """Returns bytes representation."""
-        if sys.version > '3':
+        if isinstance(self.data, datetime):
+            unixtime = timegm(self.data.timetuple())
+        elif sys.version > '3':
             unixtime = timegm(time.strptime((self.data + "UTC"), timeformat))
         else:
             unixtime = timegm(time.strptime((self.data + "UTC"), timeformat.encode("utf-8")))
diff --git a/tests/beem/test_account.py b/tests/beem/test_account.py
index 7ffc289b..d8f58f0e 100644
--- a/tests/beem/test_account.py
+++ b/tests/beem/test_account.py
@@ -476,7 +476,7 @@ class Testcases(unittest.TestCase):
         json_content = account.json()
 
         for k in keys:
-            if k not in "json_metadata" and k != 'reputation' and k != 'active_votes':
+            if k not in "json_metadata" and k != 'reputation' and k != 'active_votes' and k != 'savings_sbd_seconds':
                 if isinstance(content[k], dict) and isinstance(json_content[k], list):
                     self.assertEqual(list(content[k].values()), json_content[k])
                 else:
diff --git a/tests/beem/test_block.py b/tests/beem/test_block.py
index 6b354c29..4a2c8c1d 100644
--- a/tests/beem/test_block.py
+++ b/tests/beem/test_block.py
@@ -107,3 +107,49 @@ class Testcases(unittest.TestCase):
             exceptions.BlockDoesNotExistsException
         ):
             BlockHeader(0, steem_instance=bts)
+
+    @parameterized.expand([
+        ("non_appbase"),
+        ("appbase"),
+    ])
+    def test_export(self, node_param):
+        if node_param == "non_appbase":
+            bts = self.bts
+        else:
+            bts = self.appbase
+        block_num = 2000000
+        if bts.rpc.get_use_appbase():
+            block = bts.rpc.get_block({"block_num": block_num}, api="block")
+            if block and "block" in block:
+                block = block["block"]
+        else:
+            block = bts.rpc.get_block(block_num)
+
+        b = Block(block_num, steem_instance=bts)
+        keys = list(block.keys())
+        json_content = b.json()
+
+        for k in keys:
+            if k not in "json_metadata":
+                if isinstance(block[k], dict) and isinstance(json_content[k], list):
+                    self.assertEqual(list(block[k].values()), json_content[k])
+                else:
+                    self.assertEqual(block[k], json_content[k])
+
+        if bts.rpc.get_use_appbase():
+            block = bts.rpc.get_block_header({"block_num": block_num}, api="block")
+            if "header" in block:
+                block = block["header"]
+        else:
+            block = bts.rpc.get_block_header(block_num)
+
+        b = BlockHeader(block_num, steem_instance=bts)
+        keys = list(block.keys())
+        json_content = b.json()
+
+        for k in keys:
+            if k not in "json_metadata":
+                if isinstance(block[k], dict) and isinstance(json_content[k], list):
+                    self.assertEqual(list(block[k].values()), json_content[k])
+                else:
+                    self.assertEqual(block[k], json_content[k])
diff --git a/tests/beem/test_blockchain_batch.py b/tests/beem/test_blockchain_batch.py
index 579afdd7..2171e29a 100644
--- a/tests/beem/test_blockchain_batch.py
+++ b/tests/beem/test_blockchain_batch.py
@@ -28,6 +28,7 @@ class Testcases(unittest.TestCase):
             node=nodelist.get_nodes(normal=False, appbase=True, dev=True),
             nobroadcast=True,
             num_retries=10,
+            timeout=30,
             use_condenser=False,
             keys={"active": wif},
         )
diff --git a/tests/beem/test_comment.py b/tests/beem/test_comment.py
index ebb94dff..c9236204 100644
--- a/tests/beem/test_comment.py
+++ b/tests/beem/test_comment.py
@@ -165,7 +165,10 @@ class Testcases(unittest.TestCase):
 
         for k in keys:
             if k not in "json_metadata" and k != 'reputation' and k != 'active_votes':
-                self.assertEqual(content[k], json_content[k])
+                if isinstance(content[k], dict) and isinstance(json_content[k], list):
+                    self.assertEqual(list(content[k].values()), json_content[k])
+                else:
+                    self.assertEqual(content[k], json_content[k])
 
     def test_resteem(self):
         bts = self.bts
diff --git a/tests/beem/test_witness.py b/tests/beem/test_witness.py
index 5eb482a7..e4ffd1d2 100644
--- a/tests/beem/test_witness.py
+++ b/tests/beem/test_witness.py
@@ -121,3 +121,30 @@ class Testcases(unittest.TestCase):
         w.printAsTable()
         self.assertTrue(len(w) > 0)
         self.assertTrue(isinstance(w[0], Witness))
+
+    @parameterized.expand([
+        ("non_appbase"),
+        ("appbase"),
+    ])
+    def test_export(self, node_param):
+        if node_param == "non_appbase":
+            bts = self.bts
+        else:
+            bts = self.appbase
+        owner = "gtg"
+        if bts.rpc.get_use_appbase():
+            witness = bts.rpc.find_witnesses({'owners': [owner]}, api="database")['witnesses']
+            if len(witness) > 0:
+                witness = witness[0]
+        else:
+            witness = bts.rpc.get_witness_by_account(owner)
+
+        w = Witness(owner, steem_instance=bts)
+        keys = list(witness.keys())
+        json_witness = w.json()
+
+        for k in keys:
+            if isinstance(witness[k], dict) and isinstance(json_witness[k], list):
+                self.assertEqual(list(witness[k].values()), json_witness[k])
+            else:
+                self.assertEqual(witness[k], json_witness[k])
-- 
GitLab