text: # -*- coding: utf-8 -*-
  import pytz
  import json
  from datetime import datetime, timedelta, date, time
  import math
  import random
  import logging
  from prettytable import PrettyTable
  from beem.instance import shared_blockchain_instance
  from .exceptions import AccountDoesNotExistsException, OfflineHasNoRPCException
  from beemapi.exceptions import ApiNotSupported, MissingRequiredActiveAuthority
  from .blockchainobject import BlockchainObject
  from .blockchain import Blockchain
  from .utils import formatTimeString, formatTimedelta, remove_from_dict, reputation_to_score, addTzInfo
  from beem.amount import Amount
  from beembase import operations
  from beem.rc import RC
  from beemgraphenebase.account import PrivateKey, PublicKey, PasswordKey
  from beemgraphenebase.py23 import bytes_types, integer_types, string_types, text_type
  from beem.constants import STEEM_VOTE_REGENERATION_SECONDS, STEEM_1_PERCENT, STEEM_100_PERCENT, STEEM_VOTING_MANA_REGENERATION_SECONDS
  log = logging.getLogger(__name__)
  
  
  class Community(BlockchainObject):
      """ This class allows to easily access Community data
  
          :param str account: Name of the account
          :param Steem/Hive blockchain_instance: Hive or Steem
                 instance
          :param bool lazy: Use lazy loading
          :param bool full: Obtain all account data including orders, positions,
                 etc.
          :param Hive hive_instance: Hive instance
          :param Steem steem_instance: Steem instance
          :returns: Account data
          :rtype: dictionary
          :raises beem.exceptions.AccountDoesNotExistsException: if account
                  does not exist
  
          Instances of this class are dictionaries that come with additional
          methods (see below) that allow dealing with an community and its
          corresponding functions.
  
          .. code-block:: python
  
              >>> from beem.community import Community
              >>> from beem import Hive
              >>> from beem.nodelist import NodeList
              >>> nodelist = NodeList()
              >>> nodelist.update_nodes()
              >>> stm = Hive(node=nodelist.get_hive_nodes())
              >>> community = Community("hive-139531", blockchain_instance=stm)
              >>> print(community)
              <Community hive-139531>
              >>> print(community.balances) # doctest: +SKIP
  
          .. note:: This class comes with its own caching function to reduce the
                    load on the API server. Instances of this class can be
                    refreshed with ``Community.refresh()``. The cache can be
                    cleared with ``Community.clear_cache()``
  
      """
  
      type_id = 2
  
      def __init__(
          self,
          community,
          observer="",
          full=True,
          lazy=False,
          blockchain_instance=None,
          **kwargs
      ):
          """Initialize an community
  
          :param str community: Name of the community
          :param Hive/Steem blockchain_instance: Hive/Steem
                 instance
          :param bool lazy: Use lazy loading
          :param bool full: Obtain all community data including orders, positions,
                 etc.
          """
          self.full = full
          self.lazy = lazy
          self.observer = observer
          if blockchain_instance is None:
              if kwargs.get("steem_instance"):
                  blockchain_instance = kwargs["steem_instance"]
              elif kwargs.get("hive_instance"):
                  blockchain_instance = kwargs["hive_instance"]
          self.blockchain = blockchain_instance or shared_blockchain_instance()
          if isinstance(community, dict):
              community = self._parse_json_data(community)
          super(Community, self).__init__(
              community,
              lazy=lazy,
              full=full,
              id_item="name",
              blockchain_instance=blockchain_instance
          )
  
      def refresh(self):
          """ Refresh/Obtain an community's data from the API server
          """
          if not self.blockchain.is_connected():
              return
          self.blockchain.rpc.set_next_node_on_empty_reply(True)
          community = self.blockchain.rpc.get_community({'name': self.identifier, 'observer': self.observer}, api="bridge")
  
          if not community:
              raise AccountDoesNotExistsException(self.identifier)
          community = self._parse_json_data(community)
          self.identifier = community["name"]
          # self.blockchain.refresh_data()
  
          super(Community, self).__init__(community, id_item="name", lazy=self.lazy, full=self.full, blockchain_instance=self.blockchain)
  
      def _parse_json_data(self, community):
          parse_int = [
              "sum_pending", "subscribers", "num_pending", "num_authors",
          ]
          for p in parse_int:
              if p in community and isinstance(community.get(p), string_types):
                  community[p] = int(community.get(p, 0))
          parse_times = [
              "created_at"            
          ]
          for p in parse_times:
              if p in community and isinstance(community.get(p), string_types):
                  community[p] = addTzInfo(datetime.strptime(community.get(p, "1970-01-01 00:00:00"), "%Y-%m-%d %H:%M:%S"))
          return community
  
      def json(self):
          output = self.copy()
          parse_int = [
              "sum_pending", "subscribers", "num_pending", "num_authors",
          ]
          parse_int_without_zero = [
  
          ]
          for p in parse_int:
              if p in output and isinstance(output[p], integer_types):
                  output[p] = str(output[p])
          for p in parse_int_without_zero:
              if p in output and isinstance(output[p], integer_types) and output[p] != 0:
                  output[p] = str(output[p])
  
          parse_times = [
              "created_at",        
          ]
          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, time)):
                      output[p] = formatTimeString(p_date).replace("T", " ")
                  else:
                      output[p] = p_date
          return json.loads(str(json.dumps(output)))
  
      def get_community_roles(self):
          """ Lists community roles
          """
          community = self["name"]
          if not self.blockchain.is_connected():
              raise OfflineHasNoRPCException("No RPC available in offline mode!")
          self.blockchain.rpc.set_next_node_on_empty_reply(False)        
          return self.blockchain.rpc.list_community_roles({"community": community}, api="bridge")
  
      def get_subscribers(self):
          """  Returns subscribers
          """
          community = self["name"]
          if not self.blockchain.is_connected():
              raise OfflineHasNoRPCException("No RPC available in offline mode!")
          self.blockchain.rpc.set_next_node_on_empty_reply(False)        
          return self.blockchain.rpc.list_subscribers({"community": community}, api="bridge")
  
      def get_activities(self, limit=100, last_id=None):
          """ Returns community activity
          """
          community = self["name"]
          if not self.blockchain.is_connected():
              raise OfflineHasNoRPCException("No RPC available in offline mode!")
          self.blockchain.rpc.set_next_node_on_empty_reply(False)        
          return self.blockchain.rpc.account_notifications({"account": community, "limit": limit, "last_id": last_id}, api="bridge")
  
      def get_ranked_posts(self, observer=None, limit=100, start_author=None, start_permlink=None, sort="created"):
          """ Returns community post
          """
          community = self["name"]
          if not self.blockchain.is_connected():
              raise OfflineHasNoRPCException("No RPC available in offline mode!")
          self.blockchain.rpc.set_next_node_on_empty_reply(False)        
          return self.blockchain.rpc.get_ranked_posts({"tag": community, "observer": observer,
                                                       "limit": limit, "start_author": start_author,
                                                       "start_permlink": start_permlink, "sort":sort}, api="bridge")
  
      def set_role(self, account, role, mod_account):
          """ Set role for a given account
  
              :param str account: Set role of this account
              :param str role: Can be member, mod, admin, owner, guest
              :param str mod_account: Account who broadcast this, (mods or higher)
  
          """
          community = self["name"]
          if not self.blockchain.is_connected():
              raise OfflineHasNoRPCException("No RPC available in offline mode!")
          json_body = [
              'setRole', {
                  'community': community,
                  'account': account,
                  'role': role,
              }
          ]
          return self.blockchain.custom_json(
              "community", json_body, required_posting_auths=[mod_account])
  
      def set_user_title(self, account, title, mod_account):
          """ Set title for a given account
  
              :param str account: Set role of this account
              :param str title: Title
              :param str mod_account: Account who broadcast this, (mods or higher)
  
          """
          community = self["name"]
          if not self.blockchain.is_connected():
              raise OfflineHasNoRPCException("No RPC available in offline mode!")
          json_body = [
              'setUserTitle', {
                  'community': community,
                  'account': account,
                  'title': title,
              }
          ]
          return self.blockchain.custom_json(
              "community", json_body, required_posting_auths=[mod_account])
  
      def mute_post(self, account, permlink, notes, mod_account):
          """ Mutes a post
  
              :param str account: Set role of this account
              :param str permlink: permlink
              :param str notes: permlink
              :param str mod_account: Account who broadcast this, (mods or higher)
  
          """
          community = self["name"]
          if not self.blockchain.is_connected():
              raise OfflineHasNoRPCException("No RPC available in offline mode!")
          json_body = [
              'mutePost', {
                  'community': community,
                  'account': account,
                  'permlink': permlink,
                  "notes": notes
              }
          ]
          return self.blockchain.custom_json(
              "community", json_body, required_posting_auths=[mod_account])
  
  
      def unmute_post(self, account, permlink, notes, mod_account):
          """ Unmute a post
  
              :param str account: post author
              :param str permlink: permlink
              :param str notes: notes
              :param str mod_account: Account who broadcast this, (mods or higher)
  
          """
          community = self["name"]
          if not self.blockchain.is_connected():
              raise OfflineHasNoRPCException("No RPC available in offline mode!")
          json_body = [
              'unmutePost', {
                  'community': community,
                  'account': account,
                  'permlink': permlink,
                  "notes": notes
              }
          ]
          return self.blockchain.custom_json(
              "community", json_body, required_posting_auths=[mod_account])
  
      def update_props(self, title, about, is_nsfw, description, flag_text, admin_account):
          """ Updates the community properties
  
              :param str title: Community title
              :param str about: about
              :param bool is_nsfw: is_nsfw
              :param str description: description
              :param str flag_text: flag_text            
              :param str admin_account: Account who broadcast this, (admin or higher)
  
          """
          community = self["name"]
          if not self.blockchain.is_connected():
              raise OfflineHasNoRPCException("No RPC available in offline mode!")
          json_body = [
              'updateProps', {
                  'community': community,
                  'props': {
                      "title": title,
                      "about": about,
                      "is_nsfw": is_nsfw,
                      "description": description,
                      "flag_text": flag_text
                  }
              }
          ]
          return self.blockchain.custom_json(
              "community", json_body, required_posting_auths=[admin_account])
  
      def subscribe(self, account):
          """ subscribe to a community
  
              :param str account: account who suscribe to the community (is also broadcasting the custom_json)
  
          """
          community = self["name"]
          if not self.blockchain.is_connected():
              raise OfflineHasNoRPCException("No RPC available in offline mode!")
          json_body = [
              'subscribe', {
                  'community': community,
              }
          ]
          return self.blockchain.custom_json(
              "community", json_body, required_posting_auths=[account])
  
      def pin_post(self, account, permlink, mod_account):
          """ Stickes a post to the top of a community
  
              :param str account: post author
              :param str permlink: permlink
              :param str mod_account: Account who broadcast this, (mods or higher)
  
          """
          community = self["name"]
          if not self.blockchain.is_connected():
              raise OfflineHasNoRPCException("No RPC available in offline mode!")
          json_body = [
              'pinPost', {
                  'community': community,
                  'account': account,
                  'permlink': permlink,
              }
          ]
          return self.blockchain.custom_json(
              "community", json_body, required_posting_auths=[mod_account])
  
      def unsubscribe(self, account):
          """ unsubscribe a community
  
              :param str account: account who unsuscribe to the community (is also broadcasting the custom_json)
  
          """
          community = self["name"]
          if not self.blockchain.is_connected():
              raise OfflineHasNoRPCException("No RPC available in offline mode!")
          json_body = [
              'unsubscribe', {
                  'community': community,
              }
          ]
          return self.blockchain.custom_json(
              "community", json_body, required_posting_auths=[account])
  
      def unpin_post(self, account, permlink, mod_account):
          """ Removes a post from the top of a community
  
              :param str account: post author
              :param str permlink: permlink
              :param str mod_account: Account who broadcast this, (mods or higher)
  
          """
          community = self["name"]
          if not self.blockchain.is_connected():
              raise OfflineHasNoRPCException("No RPC available in offline mode!")
          json_body = [
              'unpinPost', {
                  'community': community,
                  'account': account,
                  'permlink': permlink,
              }
          ]
          return self.blockchain.custom_json(
              "community", json_body, required_posting_auths=[mod_account])    
  
      def flag_post(self, account, permlink, notes, reporter):
          """ Suggest a post for the review queue
  
              :param str account: post author
              :param str permlink: permlink
              :param str notes: notes
              :param str reporter: Account who broadcast this
          """
          community = self["name"]
          if not self.blockchain.is_connected():
              raise OfflineHasNoRPCException("No RPC available in offline mode!")
          json_body = [
              'flagPost', {
                  'community': community,
                  'account': account,
                  'permlink': permlink,
                  'notes': notes
              }
          ]
          return self.blockchain.custom_json(
              "community", json_body, required_posting_auths=[reporter])        
  
  
  class CommunityObject(list):
      def printAsTable(self):
          t = PrettyTable(["Nr.", "Name", "Title", "lang", "subscribers", "sum_pending", "num_pending", "num_authors"])
          t.align = "l"
          count = 0
          for community in self:
              count += 1
              t.add_row([str(count), community['name'], community["title"], community["lang"], community["subscribers"], community["sum_pending"],
                         community["num_pending"], community["num_authors"]])
          print(t)
  
  
  class Communities(CommunityObject):
      """ Obtain a list of communities
  
          :param list name_list: list of accounts to fetch
          :param int batch_limit: (optional) maximum number of accounts
              to fetch per call, defaults to 100
          :param Steem/Hive blockchain_instance: Steem() or Hive() instance to use when
              accessing a RPCcreator = Account(creator, blockchain_instance=self)
      """
      def __init__(self, sort="rank", observer=None, last=None, limit=100, lazy=False, full=True, blockchain_instance=None, **kwargs):
          
          if blockchain_instance is None:
              if kwargs.get("steem_instance"):
                  blockchain_instance = kwargs["steem_instance"]
              elif kwargs.get("hive_instance"):
                  blockchain_instance = kwargs["hive_instance"]
          self.blockchain = blockchain_instance or shared_blockchain_instance()
  
          if not self.blockchain.is_connected():
              return
          communities = []
          community_cnt = 0
          batch_limit = 100
          if batch_limit > limit:
              batch_limit = limit
  
          while community_cnt < limit:
              self.blockchain.rpc.set_next_node_on_empty_reply(False)
              communities += self.blockchain.rpc.list_communities({'sort': sort, 'observer': observer, "last": last, "limit": batch_limit}, api="bridge")
              community_cnt += batch_limit
              last = communities[-1]["name"]
  
          super(Communities, self).__init__(
              [
                  Community(x, lazy=lazy, full=full, blockchain_instance=self.blockchain)
                  for x in communities
              ]
          )
  
      def search_title(self, title):
          """ Returns all communites which have a title similar to title"""
          ret = CommunityObject()
          for community in self:
              if title.lower() in community["title"].lower():
                  ret.append(community)
          return ret