text: # -*- coding: utf-8 -*-
  from __future__ import with_statement, print_function, absolute_import
  
  import datetime
  from operator import itemgetter
  
  import pytz
  from dateutil import parser as dateparser
  
  from trello import TrelloBase
  from trello.attachments import Attachments
  from trello.checklist import Checklist
  from trello.compat import force_str
  from trello.label import Label
  from trello.organization import Organization
  from trello.customfield import CustomField, CustomFieldText, CustomFieldCheckbox, CustomFieldNumber, CustomFieldDate, CustomFieldList
  
  
  class Card(TrelloBase):
      """
      Class representing a Trello card. Card attributes are stored on
      the object
  
      https://developers.trello.com/advanced-reference/card
      """
  
      @property
      def short_url(self):
          return self.shortUrl
  
      @property
      def member_id(self):
          return self.idMembers
  
      @property
      def short_id(self):
          return self.idShort
  
      @property
      def list_id(self):
          return self.idList
  
      @property
      def board_id(self):
          return self.idBoard
  
      @property
      def description(self):
          return self.desc
  
      @property
      def date_last_activity(self):
          return self.dateLastActivity
  
      @property
      def labels(self):
          return self._labels if self._labels else []
  
      @property
      def custom_fields(self):
          """
          Lazily loads and returns the custom fields
          """
          if self.customFields is None:
              self.customFields = self.fetch_custom_fields()
          return self.customFields
  
      @property
      def comments(self):
          """
          Lazily loads and returns the comments
          """
          if self._comments is None:
              self._comments = self.fetch_comments()
          return self._comments
  
      @property
      def checklists(self):
          """
          Lazily loads and returns the checklists
          """
          if self._checklists is None:
              self._checklists = self.fetch_checklists()
          return self._checklists
  
      @property
      def plugin_data(self):
          """
          Lazily loads and returns the plugin data
          """
          if self._plugin_data is None:
              self._plugin_data = self.fetch_plugin_data()
          return self._plugin_data
  
      @property
      def attachments(self):
          """
          Lazily loads and returns the attachments
          """
          if self._attachments is None:
              self._attachments = self.fetch_attachments()
          return self._attachments
  
      def __init__(self, parent, card_id, name=''):
          """
          :parent: reference to the parent trello list
          :card_id: ID for this card
          """
          super(Card, self).__init__()
          if isinstance(parent, List):
              self.trello_list = parent
              self.board = parent.board
          else:
              self.board = parent
  
          self.client = parent.client
          self.id = card_id
          self.name = name
  
          self.customFields = None
          self._checklists = None
          self._comments = None
          self._plugin_data = None
          self._attachments = None
          self._labels = None
          self._json_obj = None
  
      @classmethod
      def from_json(cls, parent, json_obj):
          """
          Deserialize the card json object to a Card object
  
          :parent: the list object that the card belongs to
          :json_obj: json object
  
          :rtype: Card
          """
          if 'id' not in json_obj:
              raise Exception("key 'id' is not in json_obj")
          card = cls(parent,
                     json_obj['id'],
                     name=json_obj['name'])
          card._json_obj = json_obj
          card.desc = json_obj.get('desc', '')
          card.due = json_obj.get('due', '')
          card.is_due_complete = json_obj['dueComplete']
          card.closed = json_obj['closed']
          card.url = json_obj['url']
          card.pos = json_obj['pos']
          card.shortUrl = json_obj['shortUrl']
          card.idMembers = json_obj['idMembers']
          card.member_ids = json_obj['idMembers']
          card.idLabels = json_obj['idLabels']
          card.idBoard = json_obj['idBoard']
          card.idList = json_obj['idList']
          card.idShort = json_obj['idShort']
          card.badges = json_obj['badges']
          card.customFields = card.fetch_custom_fields(json_obj=json_obj)
          card.countCheckItems = json_obj['badges']['checkItems']
          card.countCheckLists = len(json_obj['idChecklists'])
          card._labels = Label.from_json_list(card.board, json_obj['labels'])
          card.dateLastActivity = dateparser.parse(json_obj['dateLastActivity'])
          if "attachments" in json_obj:
              card._attachments = []
              for attachment_json in json_obj["attachments"]:
                  card._attachments.append(attachment_json)
          if 'actions' in json_obj:
              card.actions = json_obj['actions']
          return card
  
      def __repr__(self):
          return force_str(u'<Card %s>' % self.name)
  
      def fetch(self, eager=True):
          """
          Fetch all attributes for this card
  
          :param eager: If eager, comments, checklists and attachments will be fetched immediately, otherwise on demand
          """
          json_obj = self.client.fetch_json(
              '/cards/' + self.id,
              query_params={'badges': False, 'customFieldItems': 'true'})
          self.id = json_obj['id']
          self.name = json_obj['name']
          self.desc = json_obj.get('desc', '')
          self.closed = json_obj['closed']
          self.url = json_obj['url']
          self.shortUrl = json_obj['shortUrl']
          self.idMembers = json_obj['idMembers']
          self.idShort = json_obj['idShort']
          self.idList = json_obj['idList']
          self.idBoard = json_obj['idBoard']
          self.idLabels = json_obj['idLabels']
          self._labels = Label.from_json_list(self.board, json_obj['labels'])
          self.badges = json_obj['badges']
          self.pos = json_obj['pos']
          if json_obj.get('due', ''):
              self.due = json_obj.get('due', '')
          else:
              self.due = ''
          self.checked = json_obj['checkItemStates']
          self.dateLastActivity = dateparser.parse(json_obj['dateLastActivity'])
  
          self._customFields = self.fetch_custom_fields(json_obj=json_obj)
          self._plugin_data = self.fetch_plugin_data() if eager else None
          self._checklists = self.fetch_checklists() if eager else None
          self._comments = self.fetch_comments() if eager else None
          self._attachments = self.fetch_attachments() if eager else None
  
      def fetch_custom_fields(self, json_obj=None):
          """
          Fetch current set of custom fields from card or json_obj.
          """
          if json_obj is None:
              json_obj = self.client.fetch_json(
                  '/cards/' + self.id,
                  query_params={'badges': False, 'customFieldItems': 'true'})
          return CustomField.from_json_list(
              self, json_obj.get('customFieldItems', {}))
  
      def fetch_comments(self, force=False, limit=None):
          comments = []
  
          if (force is True) or (self.badges['comments'] > 0):
              query_params = {'filter': 'commentCard,copyCommentCard'}
              if limit is not None:
                  query_params['limit'] = limit
              comments = self.client.fetch_json(
                  '/cards/' + self.id + '/actions',
                  query_params=query_params)
              return sorted(comments, key=lambda comment: comment['date'])
          return comments
  
      def get_list(self):
          obj = self.client.fetch_json('/lists/' + self.idList)
          return List.from_json(board=self, json_obj=obj)
  
      def get_comments(self):
          """Alias for fetch_comments for backward compatibility.
          Always contact server
          """
          return self.fetch_comments(force=True)
  
      def fetch_checklists(self):
  
          if self.countCheckLists == 0:
              return []
  
          if not hasattr(self, "checked") or self.checked is None:
              self.fetch(eager=False)
  
          checklists = []
          json_obj = self.client.fetch_json(
              '/cards/' + self.id + '/checklists', )
          # Thanks https://github.com/HuffAndPuff for noticing checklist
          # were not sorted
          json_obj = sorted(json_obj, key=lambda checklist: checklist['pos'])
          for cl in json_obj:
              checklists.append(Checklist(self.client, self.checked, cl,
                                          trello_card=self.id))
          return checklists
  
      def fetch_plugin_data(self):
          items = self.client.fetch_json(
              '/cards/' + self.id + '/pluginData')
          return items
  
      def fetch_attachments(self, force=False):
          if (force is True) or (self.badges['attachments'] > 0):
              items = self.client.fetch_json(
                  '/cards/' + self.id + '/attachments',
                  query_params={'filter':'false'})
              return items
          return []
  
      def get_attachments(self):
          return [Attachments.from_json(attachments_json) for attachments_json in self.fetch_attachments(force=True)]
  
      def fetch_actions(self, action_filter='createCard', since=None, before=None, action_limit=50):
          """
          Fetch actions for this card can give more argv to action_filter,
          split for ',' json_obj is list
          """
          query_params={'filter': action_filter, 'limit': action_limit}
          if since:
              query_params["since"] = since
  
          if before:
              query_params["before"] = before
  
          json_obj = self.client.fetch_json(
              '/cards/' + self.id + '/actions',
              query_params=query_params)
  
          self.actions = json_obj
          return self.actions
  
      def attriExp(self, multiple):
          """
              Provides the option to explore what comes from trello
              :multiple is one of the attributes of GET /1/cards/[card id or shortlink]/actions
          """
          self.fetch_actions(multiple)
          return self.actions
  
      @staticmethod
      def _movement_as_triplet(source_list, destination_list, movement_datetime):
          return [source_list["name"], destination_list["name"], movement_datetime]
  
      @staticmethod
      def _movement_as_dict(source_list, destination_list, movement_datetime):
          _movement = {
              "source": source_list,
              "destination": destination_list,
              "datetime": movement_datetime,
          }
          return _movement
  
      def _list_movements(self, movement_function, filter_by_date_interval=None):
          """
          Returns the list of movements of this card.
          The list of movements is in descending date and time order. First movement is the closest one to now.
          Its structure is a list of dicts where the lists are "source" and "destination" and both are also dicts.
          Date and time of the movement is in key "datetime" as a datetime object.
          :param movement_function: function that returns a representation of the movement.
          :param filter_by_date_interval: Date interval used to filter card movements to return. Optional
          :return: list with the movements.
          """
  
          action_since = None if not filter_by_date_interval else filter_by_date_interval[0]
          action_before = None if not filter_by_date_interval else filter_by_date_interval[1]
          if not hasattr(self, "actions") or self.actions is None:
              self.fetch_actions('updateCard:idList,', action_since, action_before)
  
          movements = []
  
          for idx in self.actions:
              date_str = idx['date']
              movement_datetime = dateparser.parse(date_str)
              source_list = idx['data']['listBefore']
              destination_list = idx['data']['listAfter']
              movement = movement_function(source_list, destination_list, movement_datetime)
              movements.append(movement)
  
          return movements
  
      def listCardMove_date(self):
          """Will return the history of transitions of a card from one list to
          another. The lower the index the more recent the historical item.
  
          It returns a list of lists. The sublists are triplets of
          starting list, ending list and when the transition occurred.
          """
          return self._list_movements(movement_function=Card._movement_as_triplet)
  
      def list_movements(self, list_cmp=None, filter_by_date_interval=None):
          """Will return the history of transitions of a card from one list to
          another. The lower the index the more recent the historical item.
  
          It returns a list of dicts in date and time descending order (the
          first movement is the most recent).
          Dicts are of the form source:
          <listobj> destination: <listobj> datetime: <datetimeobj>
  
          :param: list_cmp Comparison function between lists. For list_cmp(a, b) returns -1 if list a is greater that list b. Returns 1 otherwise.
          :param: filter_by_date_interval: pair of two dates (two strings in YYYY-MM-DD format) to filter card movements by date.
          """
          movement_as_dict_function = Card._movement_as_dict
          if list_cmp:
              def movement_as_dict_function(_source_list, _destination_list, _movement_datetime):
                  _movement = Card._movement_as_dict(_source_list, _destination_list, _movement_datetime)
                  _source_list_id = _source_list["id"]
                  _destination_list_id = _destination_list["id"]
                  _movement["moving_forward"] = list_cmp(_source_list_id, _destination_list_id) > 0
                  return _movement
  
          return self._list_movements(movement_function=movement_as_dict_function, filter_by_date_interval=filter_by_date_interval)
  
      def get_stats_by_list(self, lists, list_cmp=None, done_list=None, time_unit="seconds", card_movements_filter=None):
          """Gets several stats about the card by each list of the board:
          - time: The time that the card has been in each column in seconds (minutes or hours).
          - forward_moves: How many times this card has been the source of a forward movement.
          - backward_moves: How many times this card has been the source of a backward movement.
  
          Returns a dict where the key is list id and value is a dict with keys
          time, forward_moves and backward_moves.
  
          :param lists: list of board lists.
          :param list_cmp: function that compares two lists a,b given id_a, id_b. If b is in a forward position returns 1 else -1.
          :param time_unit: default to seconds. Allow specifying time in "minutes" or "hours".
          :param done_list: Column that implies that the task is done. If present, time measurement will be stopped if is current task list.
          :param card_movements_filter: Pair of two dates (two strings in YYYY-MM-DD format) that will filter the movements of the card. Optional.
          :return: dict of the form {list_id: {time:<time card was in that list>, forward_moves: <number>, backward_moves: <number> }}
          """
          tz = pytz.timezone(Organization.TIMEZONE)
  
          # Conversion of units
          seconds_to_time_unit = lambda time: time
          if time_unit == "minutes":
              seconds_to_time_unit = lambda time: time / 60.0
          elif time_unit == "hours":
              seconds_to_time_unit = lambda time: time / 3660.0
  
          # Creation datetime of the card
          creation_datetime = self.created_date
  
          #  Time in seconds stores the seconds that our card lives in a column
          stats_by_list = {list_.id: {"time":0, "forward_moves":0, "backward_moves":0} for list_ in lists}
  
          #  Last action date, used to compute the time the card spends between changes
          # of columns
          last_action_datetime = creation_datetime
  
          # Changes of columns of our card
          # Using list comparison function (if present) to check list position and, hence,
          # if the card movement was forward or backwards
          changes = self.list_movements(list_cmp, card_movements_filter)
  
          #  If there are no changes in the card, all its life has been in its creation list
          if len(changes) == 0:
              card_life_time = seconds_to_time_unit((datetime.datetime.now(tz) - last_action_datetime).total_seconds())
              stats_by_list[self.idList]["time"] += card_life_time
  
          else:
              # Changes in card are ordered to get the dates in order
              last_list = None
  
              ordered_changes = sorted(changes, key=itemgetter("datetime"))
              # For each arrival to a list, its datetime will be used to compute
              # the time this card is in that destination list
              for change in ordered_changes:
                  source_list = change["source"]
                  destination_list = change["destination"]
                  change_datetime = change["datetime"]
  
                  # For each column the total number of seconds this card is computed
                  source_list_id = source_list["id"]
  
                  time_from_last_list_change = seconds_to_time_unit((change_datetime - last_action_datetime).total_seconds())
  
                  # In case the source or destination list is not a list of this board, ignore them
                  if source_list_id not in stats_by_list or not destination_list["id"] not in stats_by_list:
                      continue
  
                  stats_by_list[source_list_id]["time"] += time_from_last_list_change
  
                  # Count if the change is to move forward or backwards
                  if "moving_forward" in change:
                      if change["moving_forward"]:
                          stats_by_list[source_list_id]["forward_moves"] += 1
                      else:
                          stats_by_list[source_list_id]["backward_moves"] += 1
  
                  # Our last action has been this change
                  last_action_datetime = change_datetime
  
                  # Store the last list
                  last_list = destination_list
  
              # Adding the number of seconds the card has been in its last column (until now)
              # only if the last column is not "Done" column
                  if done_list and last_list and last_list["id"] and last_list["id"] in stats_by_list and\
                      last_list["id"] != done_list.id:
                      time_card_has_spent_in_list_until_now = seconds_to_time_unit((datetime.datetime.now(tz) - last_action_datetime).total_seconds())
                      stats_by_list[last_list["id"]]["time"] += time_card_has_spent_in_list_until_now
  
          return stats_by_list
  
      @property
      def latestCardMove_date(self):
          """Returns the date of the last card transition"""
          self.fetch_actions('updateCard:idList')
          if self.actions is None or len(self.actions) == 0:
              return None
          date_str = self.actions[0]['date']
          return dateparser.parse(date_str)
  
      @property
      def created_date(self):
          """Will return the creation date of the card.
  
          WARNING: if the card was create via convertion of a checklist item
                  it fails. attriExp('convertToCardFromCheckItem') allows to
                  test for the condition.
          """
          if not hasattr(self, "creation_date"):
              localtz = pytz.timezone(Organization.TIMEZONE)
              self.creation_date = localtz.localize(datetime.datetime.fromtimestamp(int(self.id[0: 8], 16)))
          return self.creation_date
  
      @property
      def card_created_date(self):
          """Will return the creation date of the card.
  
          NOTE: This will return the date the card was created, even if it
          was created on another board. The created_date() above actually just
          returns the first activity and has the issue described in the warning.
  
          The first 8 characters of the card id is a hexadecimal number.
          Converted to a decimal from hexadecimal, the timestamp is an Unix
          timestamp (the number of seconds that have elapsed since January 1,
          1970 midnight UTC. See
          http://help.trello.com/article/759-getting-the-time-a-card-or-board-was-created
          """
          unix_time = int(self.id[:8], 16)
  
          return datetime.datetime.fromtimestamp(unix_time)
  
      @property
      def due_date(self):
          return dateparser.parse(self.due) if self.due else ''
  
      def set_name(self, new_name):
          """Update the name on the card to :new_name:
  
          :new_name: str
          """
          self._set_remote_attribute('name', new_name)
          self.name = new_name
  
      def set_description(self, description):
          self._set_remote_attribute('desc', description)
          self.desc = description
  
      def set_due(self, due):
          """Set the due time for the card
  
          :due: a datetime object
          """
          datestr = due.isoformat()
          self._set_remote_attribute('due', datestr)
          self.due = datestr
  
      def set_start(self, start):
          """Set the start time for the card
  
          :start: a datetime object
          """
          datestr = start.isoformat()
          self._set_remote_attribute('start', datestr)
          self.start = datestr
          
      def set_reminder(self, reminder):
          """Set a reminder time for the card
  
          :reminder: total number of minutes before the due date as an int
          """
          # datestr = reminder.isoformat()
          if isinstance(reminder,(float,str)):
              reminder = int(float(reminder))
          self._set_remote_attribute('dueReminder', reminder)
          self.reminder = reminder
  
      def set_due_complete(self):
          """Set due complete
  
          :return: None
          """
          self._set_due_complete(True)
  
      def remove_due_complete(self):
          """Remove due complete
  
          :return: None
          """
          self._set_due_complete(False)
  
  
      def remove_due(self):
          """
          Remove the due datetime of this card.
          """
          self._set_remote_attribute('due', None)
          self.due = ''
  
      def set_pos(self, pos):
          """
          Update card position in list
  
          :pos: 'top', 'bottom' or int
          """
          self._set_remote_attribute('pos', pos)
          self.pos = pos
  
      def set_custom_field(self, value, custom_field):
          """Update card custom field
  
          Arguments:
              value {[str, int, date, bool]} -- Value depending on the type of custom_field
              custom_field {custom field object} -- Custom Field Object (board.get_custom_field_definitions()[0])
  
          """
          if custom_field.field_type in ['text', 'number', 'date', 'checked']:
              if value == "":
                  post_args = {'value': ""}
              else:
                  post_args = {'value': {str(custom_field.field_type): value}}
          else:
              if value == "":
                  list_field_id = ""
              else:
                  list_field_id = [
                      x for x, y in custom_field.list_options.items() if y == value][0]
              post_args = {'idValue': list_field_id}
  
          self.client.fetch_json(
              '/card/' + self.id + '/customField/' + custom_field.id + '/item',
              http_method='PUT',
              post_args=post_args)
  
      def set_closed(self, closed):
          self._set_remote_attribute('closed', closed)
          self.closed = closed
  
      def delete_comment(self, comment):
          # Delete this comment permanently
          self.client.fetch_json(
              '/cards/' + self.id + '/actions/' + comment['id'] + '/comments',
              http_method='DELETE')
  
      def delete(self):
          # Delete this card permanently
          self.client.fetch_json(
              '/cards/' + self.id,
              http_method='DELETE')
  
      def assign(self, member_id):
          self.client.fetch_json(
              '/cards/' + self.id + '/members',
              http_method='POST',
              post_args={'value': member_id})
  
      def unassign(self, member_id):
          self.client.fetch_json(
              '/cards/' + self.id + '/idMembers/' + member_id,
              http_method='DELETE')
  
      def subscribe(self):
          self.client.fetch_json(
              '/cards/' + self.id + '/subscribed',
              http_method='PUT',
              post_args={'value': True})
  
      def comment(self, comment_text):
          """Add a comment to a card.
  
          :comment_text: str
          """
          comment_data = self.client.fetch_json(
              '/cards/' + self.id + '/actions/comments',
              http_method='POST',
              post_args={'text': comment_text})
          return comment_data
  
      def update_comment(self, comment_id, comment_text):
          """Update a comment."""
          comment_data = self.client.fetch_json(
              '/actions/' + comment_id,
              http_method='PUT',
              post_args={'text': comment_text}
          )
          return comment_data
  
      def add_label(self, label):
          self.client.fetch_json(
              '/cards/' + self.id + '/idLabels',
              http_method='POST',
              post_args={'value': label.id})
  
      def create_label(self, name, color):
          self.client.fetch_json(
              "/cards/" + self.id + "/labels",
              http_method='POST',
              post_args={"name": name, "color": color})
  
      def remove_label(self, label):
          self.client.fetch_json(
              '/cards/' + self.id + '/idLabels/' + label.id,
              http_method='DELETE')
  
      def add_member(self, member):
          self.client.fetch_json(
              '/cards/' + self.id + '/idMembers',
              http_method='POST',
              post_args={'value': member.id})
  
      def remove_member(self, member):
          self.client.fetch_json(
              '/cards/' + self.id + '/idMembers/' + member.id,
              http_method='DELETE')
  
      def attach(self, name=None, mimeType=None, file=None, url=None, setCover=None):
          """
          Add an attachment to the card. The attachment can be either a
          file or a url. Setting the name and/or mime type is optional.
          :param name: The name of the attachment
          :param mimeType: mime type for the attachement
          :param file: a file-like, binary object that supports read()
          :param url: a URL pointing to the resource to be attached
          :param cover: boolean
          """
          if (file and url) or (not file and not url):
              raise Exception('Please provide either a file or url, and not both!')
  
          kwargs = {}
          if file:
              kwargs['files'] = dict(file=(name, file, mimeType))
          else:
              kwargs['name'] = name
              kwargs['mimeType'] = mimeType
              kwargs['url'] = url
              kwargs['setCover'] = setCover
  
          return self._post_remote_data('attachments', **kwargs)
  
      def remove_attachment(self, attachment_id):
          """
          Remove attachment from card
          :param attachment_id: Attachment id
          :return: None
          """
          self.client.fetch_json(
              '/cards/' + self.id + '/attachments/' + attachment_id,
              http_method='DELETE')
  
      def change_pos(self, position):
          self.client.fetch_json(
              '/cards/' + self.id + '/pos',
              http_method='PUT',
              post_args={'value': position})
  
      def change_list(self, list_id):
          self.client.fetch_json(
              '/cards/' + self.id + '/idList',
              http_method='PUT',
              post_args={'value': list_id})
  
      def change_board(self, board_id, list_id=None):
          args = {'value': board_id}
          if list_id is not None:
              args['idList'] = list_id
          self.client.fetch_json(
              '/cards/' + self.id + '/idBoard',
              http_method='PUT',
              post_args=args)
  
      def add_checklist(self, title, items, itemstates=None):
          """Add a checklist to this card
  
          :title: title of the checklist
          :items: a list of the item names
          :itemstates: a list of the state (True/False) of each item
          :return: the checklist
          """
          if itemstates is None:
              itemstates = []
  
          json_obj = self.client.fetch_json(
              '/cards/' + self.id + '/checklists',
              http_method='POST',
              post_args={'name': title}, )
  
          cl = Checklist(self.client, [], json_obj, trello_card=self.id)
          for i, name in enumerate(items):
              try:
                  checked = itemstates[i]
              except IndexError:
                  checked = False
              cl.add_checklist_item(name, checked)
  
          self.fetch()
          return cl
  
      def _set_due_complete(self, is_complete):
          """Set due is complete or not complete
  
          https://developers.trello.com/advanced-reference/card#put-1-cards-card-id-or-shortlink-dueComplete
          :param is_complete: boolean
          :return: None
          """
          self.client.fetch_json('/cards/' + self.id + '/dueComplete',
                                 http_method='PUT',
                                 post_args={'value': is_complete})
  
      def _set_remote_attribute(self, attribute, value):
          self.client.fetch_json(
              '/cards/' + self.id + '/' + attribute,
              http_method='PUT',
              post_args={'value': value}, )
  
      def _post_remote_data(self, attribute, files=None, **kwargs):
          return self.client.fetch_json(
              '/cards/' + self.id + '/' + attribute,
              http_method='POST',
              files=files,
              post_args=kwargs)
  
      def get_custom_field_by_name(self, cf_name):
          """
          Returns existing custom field by name or creates a new one.
          """
          for cf in self.customFields:
              if cf.name == cf_name:
                  return cf
  
          cf_class = None
          cf_def_id = None
          for definition in self.board.get_custom_field_definitions():
              if definition.name == cf_name:
                  cf_def_id = definition.id
                  cf_class = {
                      'checkbox': CustomFieldCheckbox,
                      'date': CustomFieldDate,
                      'list': CustomFieldList,
                      'number': CustomFieldNumber,
                      'text': CustomFieldText,
                  }.get(definition.field_type)
          if cf_class is None:
              raise ValueError('Unknown custom field name specified ({})'.format(cf_name))
          return cf_class(self, 'unknown', cf_def_id, '')
  
  from trello.trellolist import List
  