예제 #1
0
class TapetoTrello():
    """
    The main class for the API between Tape to Trello functions.
    """

    def __init__(self, api_key, api_secret):
        self.client = TrelloClient(api_key=api_key, api_secret=api_secret)

    def export_txt_to_trello(self, filepath='', board_name="TapeBoard"+str(randint(1, 25**4))):
        """
        Given the path to the .txt tape savefile and a name of the new board
        it creates a new board on trello
        """
        with open(filepath) as tape_file:
            tape_str = tape_file.read()

        json_tape = json.loads(tape_str)

        new_board = self.client.add_board(board_name)

        for k in json_tape:
            # creates lists of the new board
            new_list = new_board.add_list(json_tape[k]["name"])

            # creates the cards of the new list
            list_cards = json_tape[k]["items"]

            for json_card in list_cards:
                new_list.add_card(
                    json_card["name"], desc="State: {}".format(json_card["state"].capitalize()))
    def handle(self, *args, **options):
        trello = TrelloClient(settings.TRELLO_API_KEY, settings.TRELLO_TOKEN)
        boards = trello.list_boards()

        #######################################################################
        # INSTALL THE MASTER BOARD ############################################
        #######################################################################

        # check if the board exists
        m = self.__search_board_by_name(settings.TRELLO_MASTER, boards)

        # if not create it
        if m is None:
            m = trello.add_board(settings.TRELLO_MASTER, default_lists=False)

        try:
            master = Board.objects.get(remoteid=m.id)
        except Board.DoesNotExist:
            master = Board(remoteid=m.id)

        master.name = m.name
        master.closed = m.closed
        master.last_activity = None
        master.save()

        self.__install_lists(m, master)
        self.__install_labels(m, master)
        self.__install_members(m)
        #######################################################################

        self.__install_projects_boards(trello, master, boards)

        self.__install_members_boards(trello, master, boards)
예제 #3
0
def create_trello_board(board_name):
    """
    Create Trello board and returns it

    :board_name: the board name
    """
    trello_client = TrelloClient(api_key=trello_api_key, api_secret=trello_api_secret, token=trello_token, token_secret=trello_token_secret)
    return trello_client.add_board(board_name)
예제 #4
0
class TrelloClass:
    def __init__(self, api_key, token):
        self.client = TrelloClient(api_key=api_key, token=token)

    def get_client(self):
        return self.client

    def get_open_boards(self):
        return self.client.list_boards(board_filter="open")

    def get_board(self, board_name):
        return next(self.get_board_gen(board_name))

    def get_board_gen(self, board_name):
        for board in self.client.list_boards():
            if board.name == board_name:
                yield board

    def create_board(self, board_name):
        return self.client.add_board(board_name)

    def close_board(self, board_name):
        self.get_board(board_name).close()

    def get_open_lists(self, board_name):
        return self.get_board(board_name).open_lists()

    def create_list(self, board_name, list_name):
        return self.get_board(board_name).add_list(list_name)

    def get_list(self, board_name, list_name):
        return next(self.get_list_gen(board_name, list_name))

    def get_list_gen(self, board_name, list_name):
        for trello_list in self.get_open_lists(board_name):
            if trello_list.name == list_name:
                yield trello_list

    def close_list(self, board_name, list_name):
        self.get_list(board_name, list_name).close()

    def get_cards_on_board(self, board_name):
        return self.get_board(board_name).open_cards()

    def get_cards_on_list(self, board_name, list_name):
        return self.get_list(board_name, list_name).list_cards()

    def add_card(self, board_name, list_name, card_name):
        return self.get_list(board_name, list_name).add_card(card_name)

    def get_card(self, card_name, board_name):
        return next(self.get_card_gen(card_name, board_name))

    def get_card_gen(self, card_name, board_name):
        for card in self.get_board(board_name).open_cards():
            if card.name == card_name:
                yield card
예제 #5
0
def create_trello_board(board_name):
    """
    Create Trello board and returns it

    :board_name: the board name
    """
    trello_client = TrelloClient(api_key=trello_api_key,
                                 api_secret=trello_api_secret,
                                 token=trello_token,
                                 token_secret=trello_token_secret)
    return trello_client.add_board(board_name)
예제 #6
0
def configure(key, token, board_name):
    config_store()
    client = TrelloClient(api_key=key, token=token)
    new_board = client.add_board(board_name)
    for i in new_board.all_lists():
        i.close()
    links_label = new_board.add_label(name='Links', color='sky')
    desc_label = new_board.add_label(name='Description', color='purple')
    checklist_label = new_board.add_label(name='To Do', color='red')
    config = dict(board_id=new_board.id,
        link_l=links_label.id,
        desc_l=desc_label.id,
        check_l=checklist_label.id,
        key=key,
        token=token)
    with open(os.path.expanduser("~/.trellogd/config.json"), 'w') as c:
        json.dump(config, c)
        print('\nSuccessfully Created: config.json')
        print('Successfully Created: {}'.format(board_name))
        print('View board at: https://trello.com/b/{}\n'.format(config['board_id']))
예제 #7
0
def fetch():

    # Create the client connection
    client = TrelloClient(api_key=os.getenv("TRELLO_API_KEY"),
                          api_secret=os.getenv("TRELLO_API_TOKEN"))

    # Find / create board
    targetBoard = next(
        filter(lambda b: b.name == os.getenv("BOARD_NAME"),
               client.list_boards()), None)
    if targetBoard is None:
        targetBoard = client.add_board(os.getenv("BOARD_NAME"))

    # Find / create list
    targetList = next(
        filter(lambda l: l.name == os.getenv("LIST_NAME"),
               targetBoard.list_lists()), None)
    if targetList is None:
        targetList = targetBoard.add_list(os.getenv("LIST_NAME"))

    return targetList.list_cards()
예제 #8
0
    def make_trello_board(self):
        client = TrelloClient(
            api_key=self.auth["trello"]["api_key"],
            api_secret=self.auth["trello"]["api_secret"],
            token=self.auth["trello"]["token"],
            token_secret=self.auth["trello"]["token_secret"],
        )

        boards = client.list_boards()
        template = None

        for board in boards:
            if board.name == self.trello_template:
                template = board

        new_board = client.add_board(
            "DP: " + self.params["title"],
            source_board=template,
            permission_level="private"
        )
        self.params["trello_url"] = new_board.url
        print("Created Trello board - " + new_board.url)
class TrelloProject(object):

    def __init__(self, board_name, backlog_list_name, lists_names=[]):
        self.board_name   = board_name
        self.backlog_name = backlog_list_name
        self.lists_names  = lists_names + [backlog_list_name]

        self.repos = []

        self._trello    = TrelloClient(conf.TRELLO_API_KEY, conf.TRELLO_TOKEN)
        self._bitbucket = Bitbucket(conf.BITBUCKET_USER, conf.BITBUCKET_PASSWORD)

    def add_bitbucket_repo(self, owner, repo_slug):
        self.repos.append( ('bitbucket', owner, repo_slug) )

    def __find_board(self, board_name):
        for board in self._trello.list_boards():
            if board.name==board_name:
                return board
        return None

    def __find_card(self, lists, label_name):
        for lst in lists:
            for card in lst.cards:
                if card.name==label_name:
                    return card
                    break
        return None

    def __sync_bitbucket(self, board, owner, repo_slug, backlog_list, board_lists, board_labels):
        
        success, data = self._bitbucket.issue.all(repo_slug=repo_slug, owner=owner)
        
        for issue in data['issues']:

            title    = issue.get('title',None)
            desc     = issue.get('content',None)
            priority = issue.get('priority',None)
            kind     = issue.get('metadata').get('kind')
            status   = issue.get('status',None)

            card = self.__find_card(board_lists, title)

            if card is None:
                card = backlog_list.add_card(
                    name=title,
                    desc=desc
                )
            

            priority_label = board_labels.get(priority, None)
            kind_label     = board_labels.get(kind, None)
            status_label   = board_labels.get(status, None)

            if priority_label is None:
                priority_label = board.add_label(priority, 'red')
                board_labels[priority] = priority_label

            if kind_label is None:
                kind_label = board.add_label(kind, 'green')
                board_labels[kind] = kind_label

            if status_label is None:
                status_label = board.add_label(status, 'sky')
                board_labels[status] = status_label

            card_labels = (card.labels if card.labels else [])
            
            if priority_label not in card_labels:
                card.add_label(priority_label)
            if kind_label not in card_labels:
                card.add_label(kind_label)
            if status_label not in card_labels:
                card.add_label(status_label)





    def sync(self):
        board = self.__find_board(self.board_name)
        
        # if the board does not exists create it
        if board is None:
            board = self._trello.add_board(self.board_name, default_lists=False)

        # store all the board labels.
        board_labels = dict([(l.name, l) for l in board.get_labels()])

        board_lists  = []
        lower_names  = [x.lower() for x in self.lists_names]
        for lst in board.list_lists():
            list_name = lst.name.lower()

            if list_name in lower_names:
                board_lists.append(lst)
                i = lower_names.index(list_name)
                self.lists_names.pop(i)
                lower_names.pop(i)
                lst.cards = lst.list_cards(card_filter='all')    
                
                

        for lst_name in self.lists_names:
            lst = board.add_list(lst_name)
            lst.cards = []
            board_lists.append(lst)
        
        backlog_list = None
        for lst in board_lists:
            if lst.name.lower()==self.backlog_name.lower(): 
                backlog_list = lst
                break
                


        for platform, owner, repo_slug in self.repos:

            if platform=='bitbucket':
                self.__sync_bitbucket(board, owner, repo_slug, backlog_list, board_lists, board_labels)
예제 #10
0
class TrelloCmd(Command):
    "Update trello board from launchpad filters"

    log = logging.getLogger(__name__)

    def get_parser(self, prog_name):
        parser = super(TrelloCmd, self).get_parser(prog_name)
        parser.add_argument(
            '--filter',
            type=str,
            action='append',
            required=True,
            help="List of params for searchTasks",
        )
        parser.add_argument('--project',
                            type=str,
                            action='append',
                            required=True,
                            help="Project")
        parser.add_argument('--board',
                            type=str,
                            required=True,
                            help="Trello board name")
        parser.add_argument(
            '--trello-key',
            type=str,
            required=False,
            help="You can get one at https://trello.com/app-key")
        parser.add_argument(
            '--trello-secret',
            type=str,
            required=False,
            help="You can get one at https://trello.com/app-key")
        parser.add_argument(
            '--trello-token',
            type=str,
            required=False,
            help="You can get one at https://trello.com/1/connect?" +
            "key=YOUR_TRELLO_KEY&name=bugfix-app&response_type=token&" +
            "scope=read,write&expiration=never")
        parser.add_argument(
            '--trello-token-secret',
            type=str,
            required=False,
        )
        parser.add_argument('--create-board',
                            action='store_true',
                            help='Create Trello board if not exists')
        parser.add_argument(
            '--use-labels',
            nargs='+',
            help='Labels for cards',
            default=['tricky', 'low-hanging-fruit', 'tech-debt'])
        return parser

    def take_action(self, parsed_args):
        err_count = 0
        logging.getLogger("requests").setLevel(logging.WARNING)
        self.log.info('Connecting to Launchpad')
        self.lp = Launchpad.login_with('lp-report-bot',
                                       'production',
                                       version='devel')
        self.tr = TrelloClient(api_key=parsed_args.trello_key,
                               api_secret=parsed_args.trello_secret,
                               token=parsed_args.trello_token,
                               token_secret=parsed_args.trello_token_secret)
        try:
            self.board = [
                board for board in self.tr.list_boards()
                if board.name == parsed_args.board
            ][0]
        except IndexError:
            if parsed_args.create_board:
                self.board = self.tr.add_board(parsed_args.board)
                # for label in self.board.all_lists():
                #    #label.delete()
                #    #                    self.client.fetch_json(
                #    #            '/cards/' + self.id,
                #    #            http_method='DELETE')
                for list in self.board.open_lists():
                    list.close()
            else:
                raise Exception(
                    "Board {0} doesn't exist. Use --create-board argument" +
                    " in order to create it".format(parsed_args.board))
        self.log.info("Working with board {0}".format(self.board))
        self.tag_labels = parsed_args.use_labels
        self.cards = dict()
        self.untouched_cards = dict()
        for card in self.board.open_cards():
            groups = re.search('(\d+)', card.name)
            if not (groups is None):
                bug_id = groups.group(0)
                if bug_id not in self.cards:
                    self.untouched_cards[bug_id] = card
                    self.log.debug(
                        "Found existing card for bug {0}".format(bug_id))
                    self.cards[bug_id] = card
                else:
                    self.log.info(
                        "Killing duplicate card for bug {0}".format(bug_id))
                    card.delete()
        self.log.info("Found {0} existing cards".format(
            len(self.untouched_cards)))

        for prj_name in parsed_args.project:
            prj = self.lp.projects[prj_name]
            for f in parsed_args.filter:
                self.log.debug(f)
                filt = json.loads(f)
                if filt['milestone']:
                    filt['milestone'] = prj.getMilestone(
                        name=filt['milestone'])
                if 'assignee' in filt:
                    filt['assignee'] = self.lp.people[filt['assignee']]
                if 'status' not in filt:
                    filt['status'] = [
                        'New', 'Incomplete', 'Opinion', 'Invalid',
                        'Won\'t Fix', 'Expired', 'Confirmed', 'Triaged',
                        'In Progress', 'Fix Committed', 'Fix Released'
                    ]
                self.log.debug(filt)
                self.log.info("Searching for tasks in project %s" % prj_name)
                for task in prj.searchTasks(**filt):
                    self.log.info("Proceeding task %s" % task)
                    retries = 3
                    for i in range(retries):
                        try:
                            self.proceed_task(task)
                        except Exception as e:
                            if i < retries:
                                self.log.exception(e)
                                self.log.warning(
                                    "Got an exception for task %s, retrying" %
                                    task)
                                continue
                            else:
                                self.log.exception(e)
                                self.log.warning("Failed to proceed task %s" %
                                                 task)
                                err_count += 1
                        break
                for series in prj.series:
                    self.log.info("Searching for tasks in {0}:{1}".format(
                        str(prj.name), str(series.name)))
                    for task in series.searchTasks(**filt):
                        self.log.info("Proceeding task %s" % task)
                        retries = 3
                        for i in range(retries):
                            try:
                                self.proceed_task(task)
                            except Exception as e:
                                if i < retries:
                                    continue
                                else:
                                    self.log.exception(e)
                                    self.log.warning(
                                        "Failed to proceed task %s" % task)
                                    err_count += 1
                            break

        if self.untouched_cards:
            self.log.info("%d cards are out of scope" %
                          len(self.untouched_cards))
            try:
                out_of_scope_list = [
                    list for list in self.board.open_lists()
                    if list.name == 'Trash/Out of scope'
                ][0]
            except IndexError:
                out_of_scope_list = self.board.add_list('Trash/Out of scope')
            for card in self.untouched_cards.values():
                card.change_list(out_of_scope_list.id)

        self.log.info("Finished with %d errors" % err_count)
        if err_count > 0:
            return 1
        return 0

    def get_task_reviews(self, task):
        self.log.debug("Searching for reviews for task {0}".format(task))
        bug = task.bug
        gerrits = [
            'https://review.openstack.org/', 'https://review.fuel-infra.org/'
        ]
        reviews = []
        # Message number 0 is description
        is_description = True
        for msg in bug.messages:
            if is_description:
                is_description = False
                continue
            for g in gerrits:
                reviews += re.findall(g + '\d+', msg.content)
                long_reviews = re.findall(g + '#/c/\d+', msg.content)
                for u in long_reviews:
                    reviews += [u.replace('#/c/', '')]
        open_reviews = []
        for rev_url in set(reviews):
            [base_url, id] = rev_url.rsplit('/', 1)
            rest = GerritRestAPI(base_url)
            try:
                review = rest.get('/changes/{0}/detail'.format(id))
                if review['status'] == 'NEW':
                    status = []
                    if 'rejected' in review['labels']['Workflow']:
                        status.append('WIP')
                    if 'disliked' in review['labels']['Verified']:
                        status.append('FAIL')
                    open_reviews.append({'url': rev_url, 'status': status})
                    self.log.debug("Found open review {0}".format(rev_url))
            except Exception:
                pass

        return open_reviews

    def get_task_list(self, task):
        list_name = 'Bad Status'
        try:
            if task.status in ['Confirmed']:
                if task.assignee is None or task.assignee.is_team:
                    list_name = 'Inbox/Need triage'
                else:
                    list_name = 'Assigned/Investigating'
            if task.status in ['Incomplete']:
                list_name = 'Incomplete/Need more info'
            if task.status in ['Triaged']:
                list_name = 'Triaged/Ready to be fixed'
            if task.status in ['In Progress']:
                if self.get_task_reviews(task):
                    list_name = 'In Progress/Need review'
                else:
                    list_name = 'In Progress/Working on fix'
            if task.status in ['Fix Committed', 'Fix Released']:
                list_name = 'Fix Committed/Done'
            if task.status in ['Invalid', 'Opinion', 'Won\'t Fix']:
                list_name = 'Won\'t Fix/Done'
            if task.status in ['New']:
                list_name = 'New/Need confirmation'
            # if (
            #     not filter(lambda x: x.startswith('team-'), task.bug.tags)
            #     and 'tech-debt' not in task.bug.tags and
            #     task.status in [
            #         'New', 'Confirmed', 'Triaged', 'In Progress',
            #         'Incomplete']
            #     ):
            #     list_name = 'New/Need confirmation'
            if 'blocked' in task.bug.tags:
                list_name = 'Blocked/On hold'
            return [
                list for list in self.board.open_lists()
                if list.name == list_name
            ][0]
        except IndexError:
            return self.board.add_list(list_name)

    def get_task_labels(self, task):
        bug = task.bug
        tags = list(set(bug.tags).intersection(self.tag_labels))
        # Each bug should have either team tag or no-team tag or tech-debt tag
        team_tags = filter(lambda x: x.startswith('team-'), bug.tags)
        if team_tags:
            tags += team_tags
        else:
            if 'tech-debt' not in bug.tags:
                tags += ['no-team']
        if not filter(lambda x: x.startswith('area-'), task.bug.tags):
            tags += ['no-area']
        # if task.importance in ['Critical', 'High']:
        #     tags.append('high-priority')
        return tags

    def get_card_title(self, task):
        bug = task.bug
        assignee_id = "unassigned"
        if task.assignee_link is not None:
            assignee_id = task.assignee_link.split('~')[-1]
        return u'Bug {0} ({1}): {2}'.format(bug.id, assignee_id,
                                            bug.title)[:200]

    def get_card_description(self, task, card_list):
        bug = task.bug
        desc = "Created by {0}\n".format(bug.owner_link.split('~')[-1])
        desc += bug.web_link + "\n"
        if card_list.name == 'In Progress/Need review':
            desc += "Reviews:\n" + "\n".join(
                map(
                    lambda x: u"{0} {1}".format(x['url'], ':'.join(x[
                        'status'])), self.get_task_reviews(task))) + "\n"
        desc += "\n----------\n" + bug.description
        return desc[:1000]

    def proceed_task(self, task):
        self.log.debug("Processing task {0}".format(task))
        bug = task.bug
        card_list = self.get_task_list(task)
        if str(bug.id) not in self.cards:
            self.log.debug("Creating card for bug {0}".format(bug.id))
            card = card_list.add_card(
                self.get_card_title(task),
                self.get_card_description(task, card_list))
            self.cards[bug.id] = card
        else:
            self.log.debug("Getting card for task {0}".format(task))
            card = self.cards[str(bug.id)]
            try:
                del self.untouched_cards[str(bug.id)]
            except KeyError:
                pass
            self.log.debug(
                ("Updating existing card for bug {0}, moving to {1} list"
                 ).format(bug.id, card_list))
            card.change_list(card_list.id)
            new_name = self.get_card_title(task)
            if new_name != card.name.decode('utf-8'):
                card.set_name(new_name)
            new_desc = self.get_card_description(task, card_list)
            if new_desc != card.description:
                card.set_description(new_desc)
        tags = self.get_task_labels(task)
        for label in card.labels:
            if label.name not in tags:
                # delete_label is not published on pypi yet
                # card.delete_label(label)
                card.client.fetch_json('/cards/' + card.id + '/idLabels/' +
                                       label.id,
                                       http_method='DELETE')
        for label_name in tags:
            try:
                label = [
                    l for l in self.board.get_labels() if l.name == label_name
                ][0]
            except IndexError:
                label = self.board.add_label(label_name, 'green')
            try:
                card.add_label(label)
            except Exception:
                pass
        self.log.debug(task)
예제 #11
0
class ServiceTrello(ServicesMgr):
    """
        Serivce Trello
    """
    # Boards own Lists own Cards

    def __init__(self, token=None, **kwargs):
        super(ServiceTrello, self).__init__(token, **kwargs)
        # app name
        self.app_name = DjangoThConfig.verbose_name
        # expiration
        self.expiry = "30days"
        # scope define the rights access
        self.scope = 'read,write'
        self.oauth = 'oauth1'
        self.service = 'ServiceTrello'

        base = 'https://www.trello.com'
        self.AUTH_URL = '{}/1/OAuthAuthorizeToken'.format(base)
        self.REQ_TOKEN = '{}/1/OAuthGetRequestToken'.format(base)
        self.ACC_TOKEN = '{}/1/OAuthGetAccessToken'.format(base)
        self.consumer_key = settings.TH_TRELLO_KEY['consumer_key']
        self.consumer_secret = settings.TH_TRELLO_KEY['consumer_secret']
        if token:
            token_key, token_secret = token.split('#TH#')
            try:
                self.trello_instance = TrelloClient(self.consumer_key,
                                                    self.consumer_secret,
                                                    token_key,
                                                    token_secret)
            except ResourceUnavailable as e:
                us = UserService.objects.get(token=token)
                logger.error(e.msg, e.error_code)
                update_result(us.trigger_id, msg=e.msg, status=False)

    def read_data(self, **kwargs):
        """
            get the data from the service

            :param kwargs: contain keyword args : trigger_id at least
            :type kwargs: dict
        """
        trigger_id = kwargs.get('trigger_id')
        data = list()
        kwargs['model_name'] = 'Trello'
        kwargs['app_label'] = 'th_trello'
        super(ServiceTrello, self).read_data(**kwargs)
        cache.set('th_trello_' + str(trigger_id), data)
        return data

    def save_data(self, trigger_id, **data):
        """
            let's save the data

            :param trigger_id: trigger ID from which to save data
            :param data: the data to check to be used and save
            :type trigger_id: int
            :type data:  dict
            :return: the status of the save statement
            :rtype: boolean
        """
        data['output_format'] = 'md'
        title, content = super(ServiceTrello, self).save_data(trigger_id, **data)
        if len(title):
            # get the data of this trigger
            t = Trello.objects.get(trigger_id=trigger_id)
            # footer of the card
            footer = self.set_card_footer(data, t)
            content += footer

            # 1 - we need to search the list and board where we will
            # store the card so ...

            # 1.a search the board_id by its name
            # by retrieving all the boards
            boards = self.trello_instance.list_boards()

            board_id = ''
            my_list = ''
            for board in boards:
                if t.board_name == board.name:
                    board_id = board.id
                    break

            if board_id:
                # 1.b search the list_id by its name
                my_board = self.trello_instance.get_board(board_id)
                lists = my_board.open_lists()
                # just get the open list ; not all the archive ones
                for list_in_board in lists:
                    # search the name of the list we set in the form
                    if t.list_name == list_in_board.name:
                        # return the (trello) list object to be able to add card at step 3
                        my_list = my_board.get_list(list_in_board.id)
                        break
                # we didnt find the list in that board -> create it
                if my_list == '':
                    my_list = my_board.add_list(t.list_name)
            else:
                # 2 if board_id and/or list_id does not exist, create it/them
                my_board = self.trello_instance.add_board(t.board_name)
                # add the list that didn't exists and return a (trello) list object
                my_list = my_board.add_list(t.list_name)

            # 3 create the card
            my_list.add_card(title, content)

            logger.debug(str('trello {} created').format(data['link']))
            status = True
        else:
            sentence = "no token or link provided for trigger ID {}".format(trigger_id)
            update_result(trigger_id, msg=sentence, status=False)
            status = False

        return status

    @staticmethod
    def set_card_footer(data, trigger):
        """
            handle the footer of the note
        """
        footer = ''
        if data.get('link'):
            provided_by = _('Provided by')
            provided_from = _('from')
            footer_from = "<br/><br/>{} <em>{}</em> {} <a href='{}'>{}</a>"
            description = trigger.trigger.description
            footer = footer_from.format(provided_by, description, provided_from, data.get('link'), data.get('link'))
            import pypandoc
            footer = pypandoc.convert(footer, 'md', format='html')
        return footer

    def auth(self, request):
        """
            let's auth the user to the Service
            :param request: request object
            :return: callback url
            :rtype: string that contains the url to redirect after auth
        """
        request_token = super(ServiceTrello, self).auth(request)
        callback_url = self.callback_url(request)

        # URL to redirect user to, to authorize your app
        auth_url_str = '{auth_url}?oauth_token={token}'
        auth_url_str += '&scope={scope}&name={name}'
        auth_url_str += '&expiration={expiry}&oauth_callback={callback_url}'
        auth_url = auth_url_str.format(auth_url=self.AUTH_URL,
                                       token=request_token['oauth_token'],
                                       scope=self.scope,
                                       name=self.app_name,
                                       expiry=self.expiry,
                                       callback_url=callback_url)

        return auth_url

    def callback(self, request, **kwargs):
        """
            Called from the Service when the user accept to activate it
            :param request: request object
            :return: callback url
            :rtype: string , path to the template
        """
        return super(ServiceTrello, self).callback(request, **kwargs)
예제 #12
0
class trello:
    def __init__(self, apiKey, TOKEN):
        self.apiKey = apiKey
        self.token = TOKEN
        self.client = TrelloClient(api_key=apiKey,
                                   api_secret='your-secret',
                                   token=TOKEN,
                                   token_secret='your-oauth-token-secret')

    def printTrello(self):
        all_boards = self.client.list_boards()
        last_board = all_boards[-1]
        print("Boards ")

        for board in all_boards:
            print("Board Name :", board.name, " Board ID", board.id)
            for list in board.all_lists():
                print("\t", "ListName :", list.name, "listID :", list.id)
                for card in list.list_cards(""):
                    print("\t\t", "cardName :", card.name, "cardID :", card.id)

                    #for card in board.all_cards():
                    #   print("\tCard Name :",card.name," Card ID",card.id)
                ####### BOARD OPERATIONS

    def getBoard(self, boardID):
        self.board = self.client.get_board(board_id=boardID)
        return self.board

    def getBoardByName(self, boardName):
        all_boards = self.client.list_boards()
        for board in all_boards:
            if board.name == boardName:
                self.board = board
                return board
        return None

    # close all boards
    def clearBoards(self):
        for board in self.client.list_boards():
            board.close()

    def createBoard(self,
                    boardName,
                    organizationID=None,
                    permission_level="private"):
        self.board = self.client.add_board(board_name=boardName,
                                           source_board=None,
                                           organization_id=organizationID,
                                           permission_level=permission_level)
        for list in self.board.get_lists(None):
            self.board.get_list(list.id).close()
        self.createList("To Do:", self.board.id, 1)
        self.createList("Doing:", self.board.id, 2)
        self.createList("Build:", self.board.id, 3)
        self.createList("Test:", self.board.id, 4)
        self.createList("Deploy:", self.board.id, 5)
        return self.board

    def closeBoardByName(self, boardName=None):
        if boardName != None:
            all_boards = self.client.list_boards()
            for board in all_boards:
                if board.name == boardName:
                    return board.close()
        else:
            if self.board != None:
                self.closeBoard(self.board.id)

    def closeBoard(self, boardId=None):
        if boardId != None:
            return self.getBoard(boardID=boardId).close()
        else:
            if self.board != None:
                self.board.close()
            else:
                return None

    def boardList(self):
        return self.client.list_boards()

    ####### END BOARD OPERATIONS

    ####### LIST OPERATIONS

    def getList(self, listID, boardID):
        return self.client.get_board(board_id=boardID).get_list(list_id=listID)

    def getListByName(self, listID, boardID):
        return self.client.get_board(board_id=boardID).get_list(list_id=listID)

    def createList(self, listName, boardID, sira=None):
        board = self.client.get_board(boardID)
        addedlist = board.add_list(listName, sira)
        return addedlist

    def closeList(self, listID, boardID):
        return self.client.get_board(boardID).get_list(listID).close()

    def closeJustListID(self, listID):  # unsafe
        url = "https://api.trello.com/1/lists/" + listID + "?closed=true&key=" + self.apiKey + "&token=" + self.token
        querystring = {}
        response = requests.request("PUT", url, params=querystring)
        return response.text

    ####### END LIST OPERATIONS

    ####### CARD OPERATIONS

    def getCard(self, cardID):
        return self.client.get_card(card_id=cardID)

    def createCard(self, boardID, listID, cardName):
        self.getList(boardID=boardID, listID=listID).add_card(name=cardName,
                                                              labels=None,
                                                              due="",
                                                              source=None,
                                                              position=None)

    def removeCard(self, cardID):
        url = "https://api.trello.com/1/cards/" + cardID + "?key=" + self.apiKey + "&token=" + self.token
        querystring = {}
        response = requests.request("PUT", url, params=querystring)
        return response.text

    def moveCard(self, cardID, desListID):
        self.getCard(cardID=cardID).change_list(list_id=desListID)

    ####### END CARD OPERATIONS

    #######  TEAM MEMBER OPERATIONS

    def addMemberBoard(self, boardID, memberID):
        board = self.client.get_board(board_id=boardID)
        board.add_member(memberID)

    # ORGANIZATION OPERATIONS

    def getOrganization(self, organizationID):
        return self.client.get_organization(organizationID)

    def getOrganizationByName(self, organizationName):
        for organization in self.listOrganizations():
            if organization.name == "":
                return organization
        return None

    def listOrganizations(self):
        self.client.list_organizations()
        return self.client.list_organizations()

    def createOrganization(self, organizationName):
        url = "https://api.trello.com/1/organizations?displayName=" + organizationName + "&desc=" + organizationName + "&key=" + self.apiKey + "&token=" + self.token
        querystring = {}
        response = requests.request("POST", url, params=querystring)
        organizationID = str.split(response.text, ",")[0].split("\"")[3]
        return organizationID

    def addOrganizationMember(self,
                              organizationID,
                              mail,
                              memberType="normal",
                              fullName="member"):
        configuredMail = str.replace(mail, "@", "%40")
        url = "https://api.trello.com/1/organizations/" + organizationID + "/members?email=" + configuredMail + "&fullName=" + fullName + "&type=" + memberType + "&key=" + self.apiKey + "&token=" + self.token
        querystring = {}
        response = requests.request("PUT", url, params=querystring)

        data = (json.loads(response.text))
        memberID = (data["memberships"][-1]["idMember"])
        return memberID

    def removeOrganizationMember(self, organizationID, memberID):
        url = "https://api.trello.com/1/organizations/" + organizationID + "/members/" + memberID + "?key=" + self.apiKey + "&token=" + self.token
        querystring = {}
        response = requests.request("DELETE", url, params=querystring)
        return response.text

    def removeOrganization(self, organizationID):
        url = "https://api.trello.com/1/organizations/" + organizationID + "?key=" + self.apiKey + "&token=" + self.token
        querystring = {}
        response = requests.request("DELETE", url, params=querystring)
        return response.text

    def addCommendToCard(self, cardID, commendText):
        url = "https://api.trello.com/1/cards/" + cardID + "/actions/comments?text=" + commendText + "&key=" + self.apiKey + "&token=" + self.token
        querystring = {}
        response = requests.request("POST", url, params=querystring)
        return response.text
예제 #13
0
from trello import TrelloClient

URL = 'https://libraryofjuggling.com/TricksByDifficulty.html'

# Get this information from
load_dotenv()
TRELLO_CLIENT = TrelloClient(api_key=os.environ['API_KEY'],
                             api_secret=os.environ['API_SECRET'],
                             token=os.environ['TOKEN'])

if __name__ == '__main__':
    html = requests.get(URL).content
    soup = BeautifulSoup(html, features='html.parser')

    # Setup Trello board
    juggling_board = TRELLO_CLIENT.add_board(board_name='Juggling')
    juggling_board.add_list('Mastered', pos=3)
    juggling_board.add_list('Learning', pos=2)
    trick_list = juggling_board.add_list('Tricks', pos=1)

    for level, unordered_list in enumerate(soup.find_all('ul', 'MainText'), 2):
        print(f"Level {level}")
        trello_label = juggling_board.add_label(f"Level {level}", color=None)

        tricks = unordered_list.find_all('a')
        for trick in tricks:
            url = 'https://libraryofjuggling.com/' + trick.attrs['href']
            trick_name = trick.text
            trick_list.add_card(name=trick_name,
                                desc=url,
                                labels=[trello_label])
예제 #14
0
    file = open("__main__.py", "x")
    file.close()
elif 'node' in projType or 'js' in projType or 'javascript' in projType:
    file = open("main.js", "x")
    file.close()
os.chdir("..")
os.chdir("..")
print('main files created')

#connects the user to trello
print('creating Trello sheet')
client = TrelloClient(api_key='PUT API KEY FOR TRELLO HERE',
                      api_secret='PUT API SECRET FOR TRELLO HERE',
                      token='PUT TRELLO CLIENT TOKEN HERE')
#creates a trello sheet
client.add_board(projName)
print("Trello added")

#creates a project document
print('creating a project document')
doc = docx.Document("template.docx")
os.chdir("projects")
os.chdir(projName)
doc.save("project.docx")
print('created project document')

#creates a github repository
print("creating github repository")
git = Github("PUT GITHUB CLIENT KEY HERE")
me = git.get_user()
me.create_repo(projName)
class TrelloMasterBoard(object):

    def __init__(self, master_boardname, sync_boards, sync_lists):
        self.master_boardname = master_boardname
        self.sync_boards      = sync_boards
        self.sync_lists       = sync_lists

        self._trello    = TrelloClient(conf.TRELLO_API_KEY, conf.TRELLO_TOKEN)

    def __find_board(self, board_name):
        for board in self._trello.list_boards():
            if board.name==board_name:
                return board
        return None


    def __get_lists(self, board, lists_names):
        lists = []
        names = [x.lower() for x in lists_names]
       
        # search for the lists
        for lst in board.list_lists():
            if lst.name.lower() in names:
                lists.append(lst)
       
        return lists

    def __get_and_create_lists(self, board, lists_names):
        """
        Search for the lists in the board. If they do not exists create them
        """
        lists = []
        names = [x.lower() for x in lists_names]
        lists_names = list(lists_names) # make a copy

        # search for the lists
        for lst in board.list_lists():
            name = lst.name.lower()

            if name in names:
                lists.append(lst)
                i = names.index(name)
                lists_names.pop(i)
                names.pop(i)
                
        # create the non existing lists
        for lst_name in lists_names:
            lst = board.add_list(lst_name)
            lists.append(lst)

        return lists

    def __card_id(self, card):
        if card.description.startswith('card-id:'):
            return card.description[8:32]
        else:
            return card.id

    def __get_label(self, board, label_name):

        if not hasattr(board, 'labels'):
            board.labels = dict([ (l.name, l) for l in board.get_labels(limit=100) ])
            
        label = board.labels.get(label_name, None)
        if label is None: 
            label = board.add_label(label_name, 'red')
            board.labels[label_name]=label
        return label


    def sync(self):
        #### FIND THE MASTER BOARD OR CREATE IT ###############################################
        masterboard = self.__find_board(self.master_boardname)
        if masterboard is None:
            masterboard = self._trello.add_board(self.master_boardname, default_lists=False)
        #######################################################################################

        #### GET ALL THE BOARD LISTS ##########################################################
        mboard_lists    = dict([(l.name.lower(), l) for l in self.__get_and_create_lists(masterboard, self.sync_lists)])
        mboard_listsids = dict([(l.id, l) for l in mboard_lists.values()])
        #######################################################################################

        #### GET ALL THE BOARD CARDS IN THE LISTS #############################################
        mboard_cardsids = {}
        for lst in mboard_lists.values():
            for c in lst.list_cards(card_filter='all'):
                mboard_cardsids[self.__card_id(c)] = c
        #######################################################################################
        

        boards = {}
        boards_lists = {}
        boards_cards = {}

        for board_name in self.sync_boards:
            board = self.__find_board(board_name)

            boards[board_name.lower()] = board
            lists    = dict([(l.name.lower(), l) for l in self.__get_and_create_lists(board, self.sync_lists)])
            listsids = dict([(l.id, l) for l in lists.values()])

            if board.id not in boards_lists:
                boards_lists[board.id] = {}
            boards_lists[board.id].update(lists)

            for list_name, lst in lists.items():
                cards = lst.list_cards(card_filter='all')
    
                if board_name.lower() not in boards_cards:
                    boards_cards[board_name.lower()] = {}



                for card in cards:
                    card_id     = self.__card_id(card)
                    boards_cards[board_name.lower()][card_id] = card

                    ### GET THE MASTER CARD, IF DOES NOT EXISTS CREATE IT #####################
                    master_card = mboard_cardsids.get(card_id, None)
                    if master_card is None:
                        master_card  = mboard_lists[list_name].add_card(card.name, "card-id:{0}".format( card.id ))
                        master_card.set_closed(card.closed)
                        master_label = self.__get_label(masterboard, board_name)
                        master_card.add_label(master_label)
                    ###########################################################################

                    ### CHECK WITCH CARD SHOULD BE UPDATED ####################################
                    if master_card.dateLastActivity<card.dateLastActivity:
                        card_to   = master_card
                        card_from = card
                        lists_id_to   = mboard_listsids
                        lists_id_from = listsids
                        lists_to   = mboard_lists
                        lists_from = lists
                    else:
                        card_to   = card
                        card_from = master_card
                        lists_id_to   = listsids
                        lists_id_from = mboard_listsids
                        lists_to   = lists
                        lists_from = mboard_lists
                    ###########################################################################

                    ### UPDATE THE NAME IF NECESSARY ##########################################
                    if card_from.name!=card_to.name:
                        card_to.set_name(card_from.name)
                    ###########################################################################

                    ### UPDATE THE NAME IF NECESSARY ##########################################
                    if card_from.closed!=card_to.closed:
                        card_to.set_closed(card_from.closed)
                    ###########################################################################


                    ### UPDATE THE DESCRIPTION IF NECESSARY ###################################
                    from_desc = card_from.description
                    to_desc   = card_to.description
                    if from_desc.startswith('card-id'): from_desc = from_desc[33:]
                    if to_desc.startswith('card-id'):   to_desc   = to_desc[33:]
                    if from_desc!=to_desc:
                        card_to.set_description( "card-id:{0}\n{1}".format( card_id, from_desc ))
                    ###########################################################################

                    ### UPDATE THE LIST IF NECESSARY ##########################################
                    list_to   = lists_id_to[card_to.list_id]
                    list_from = lists_id_from[card_from.list_id]
                    if list_to.name.lower()!=list_from.name.lower():
                        new_lst = lists_to[list_from.name.lower()]
                        card_to.change_list(new_lst.id)
                    ###########################################################################



        for card_id, card in mboard_cardsids.items():
            print(card.plugin_data)
            labels = card.labels if card.labels else []
            for label in labels:
                board    = boards.get(label.name.lower(), None)
                if board is None: continue

                if card_id not in boards_cards[board.name.lower()]:
                    old_list = mboard_listsids[card.list_id]
                    new_list = boards_lists[board.id][old_list.name.lower()]
                    new_list.add_card(card.name, "card-id:{0}\n{1}".format(card_id, card.description) )
예제 #16
0
class TrelloClient:
    def __init__(self, api_key, api_secret, token, token_secret):
        self.trello_client = Client(api_key=api_key,
                                    api_secret=api_secret,
                                    token=token,
                                    token_secret=token_secret)
        self._uid = None
        self._project = None
        self._board = None
        self._lists = None
        self._board_labels = None
        self._lists_filter = None
        self._only_my_cards = False

    @property
    def whoami(self):
        """
        Get my Trello UID

        :return: my Trello UID
        :rtype: string
        """
        if self._uid is None:
            self._uid = self.trello_client.get_member('me').id
        return self._uid

    def project(self, project):
        """
        Set the class working project

        :param project: TelloWarrior project object
        """
        if self._project == None or self._project.name != project.name:
            self._board = self.get_board(project.trello_board_name)
            self._lists = self.get_lists()
            self._board_labels = self.get_board_labels()
            self._lists_filter = project.trello_lists_filter
            self._only_my_cards = project.only_my_cards

    def get_board(self, board_name):
        """
        Get a open Trello board from name, if it does not exist create it

        :param board_name: the board name
        :return: a Tello board
        :rtype: Trello board object
        """
        for trello_board in self.trello_client.list_boards(
                board_filter='open'):
            if trello_board.name == board_name and not trello_board.closed:
                logger.debug('Trello board {} found'.format(board_name))
                return trello_board
        logger.debug('Creating Trello board {}'.format(board_name))
        return self.trello_client.add_board(board_name)

    def get_lists(self):
        """
        Get the open lists of a Trello board

        :return: a list of Trello list objects
        :rtype: list
        """
        return self._board.open_lists()

    def get_list(self, list_name):
        """
        Get a Trello list from list name, if it does not exist create it

        :param list_name: the list name
        :return: a Tello list
        :rtype: Trello list object
        """
        if self._lists == None:
            raise ClientError('get_list')
        for trello_list in self._lists:
            if trello_list.name == list_name:
                logger.debug('Trello list {} found'.format(list_name))
                return trello_list
        logger.debug('Creating Trello list {}'.format(list_name))
        trello_list = self._board.add_list(list_name)
        self._lists.append(trello_list)  # Update _lists with new list
        return trello_list

    def get_board_labels(self):
        """
        Get the labels of a Trello board

        :param board_name: the board name
        :return: a list of Trello label objects
        :rtype: list
        """
        return self._board.get_labels()

    def get_board_label(self, label_name):
        """
        Get a Trello board label from label name, if it does not exist create it

        :param label_name: the label name
        :return: a Tello label
        :rtype: Trello label object
        """
        if self._board_labels == None:
            raise ClientError('get_board_label')
        for board_label in self._board_labels:
            if board_label.name == label_name:
                logger.debug('Trello board label {} found'.format(label_name))
                return board_label
        logger.debug('Creating Trello board label {}'.format(label_name))
        board_label = self._board.add_label(label_name, 'black')
        self._board_labels.append(
            board_label)  # Update _board_labels with new label
        return board_label

    def get_cards_dict(self):
        """
        Get all cards of a list of Trello lists in a dictionary

        :return: a dict with Cards
        :rtype: dict
        """
        trello_cards_dict = {}
        if self._lists_filter is not None:
            trello_lists = filter(
                lambda trello_list: trello_list.name not in self._lists_filter,
                self._lists)
        for trello_list in trello_lists:
            logger.debug('Getting Trello cards of list {}'.format(
                trello_list.name))
            trello_cards_dict[trello_list.name] = trello_list.list_cards()
            if self._only_my_cards:
                trello_cards_dict[trello_list.name] = filter(
                    lambda trello_card: self.whoami in trello_card.member_ids,
                    trello_cards_dict[trello_list.name])
        return trello_cards_dict

    def delete_trello_card(self, trello_card_id):
        """
        Delete (forever) a Trello card by ID

        :param trello_card_id: ID of Trello card
        """
        try:
            self.trello_client.get_card(trello_card_id).delete()
        except ResourceUnavailable:
            logger.warning(
                'Cannot find Trello card with ID {} deleted in Task Warrior. Maybe you also deleted it in Trello?'
                .format(trello_card_id))
예제 #17
0
class ServiceTrello(ServicesMgr):

    # Boards own Lists own Cards

    def __init__(self, token=None):
        # app name
        self.app_name = DjangoThConfig.verbose_name
        # expiration
        self.expiry = "30days"
        # scope define the rights access
        self.scope = 'read,write'

        base = 'https://www.trello.com'
        self.AUTH_URL = '{}/1/OAuthAuthorizeToken'.format(base)
        self.REQ_TOKEN = '{}/1/OAuthGetRequestToken'.format(base)
        self.ACC_TOKEN = '{}/1/OAuthGetAccessToken'.format(base)
        self.consumer_key = settings.TH_TRELLO['consumer_key']
        self.consumer_secret = settings.TH_TRELLO['consumer_secret']
        if token:
            token_key, token_secret = token.split('#TH#')
            self.trello_instance = TrelloClient(self.consumer_key,
                                                self.consumer_secret,
                                                token_key,
                                                token_secret)

    def read_data(self, token, trigger_id, date_triggered):
        """
            get the data from the service
            as the pocket service does not have any date
            in its API linked to the note,
            add the triggered date to the dict data
            thus the service will be triggered when data will be found
            :param trigger_id: trigger ID to process
            :param date_triggered: the date of the last trigger
            :type trigger_id: int
            :type date_triggered: datetime
            :return: list of data found from the date_triggered filter
            :rtype: list
        """
        data = list()
        cache.set('th_trello_' + str(trigger_id), data)

    def process_data(self, trigger_id):
        """
            get the data from the cache
            :param trigger_id: trigger ID from which to save data
            :type trigger_id: int
        """
        cache_data = cache.get('th_trello_' + str(trigger_id))
        return PublishingLimit.get_data('th_trello_', cache_data, trigger_id)

    def save_data(self, token, trigger_id, **data):
        """
            let's save the data

            :param trigger_id: trigger ID from which to save data
            :param **data: the data to check to be used and save
            :type trigger_id: int
            :type **data:  dict
            :return: the status of the save statement
            :rtype: boolean
        """
        from th_trello.models import Trello

        title = ''
        content = ''
        status = False

        title = self.set_card_title(data)
        content = self.set_card_content(data)

        if len(title):
            # get the data of this trigger
            t = Trello.objects.get(trigger_id=trigger_id)

            # 1 - we need to search the list and board where we will
            # store the card so ...

            # 1.a search the board_id by its name
            # by retreiving all the boards
            boards = self.trello_instance.list_boards()

            board_id = ''
            my_board = ''
            my_list = ''
            for board in boards:
                if t.board_name == board.name.decode('utf-8'):
                    board_id = board.id
                    break

            if board_id:
                # 1.b search the list_id by its name
                my_board = self.trello_instance.get_board(board_id)
                lists = my_board.open_lists()
                # just get the open list ; not all the archive ones
                for list_in_board in lists:
                    # search the name of the list we set in the form
                    if t.list_name == list_in_board.name.decode('utf-8'):
                        # return the (trello) list object
                        # to be able to add card at step 3
                        my_list = my_board.get_list(list_in_board.id)
                        break
                # we didnt find the list in that board
                # create it
                if my_list == '':
                    my_list = my_board.add_list(t.list_name)

            else:
                # 2 if board_id and/or list_id does not exist, create it/them
                my_board = self.trello_instance.add_board(t.board_name)
                # add the list that didnt exists and
                # return a (trello) list object
                my_list = my_board.add_list(t.list_name)

            # 3 create the card
            # create the Trello card
            my_list.add_card(title, content)

            sentance = str('trello {} created').format(data['link'])
            logger.debug(sentance)
            status = True
        else:
            sentance = "no token or link provided for trigger ID {}"
            logger.critical(sentance.format(trigger_id))
            status = False

        return status

    def set_card_title(self, data):
        """
            handle the title from the data
        """
        title = ''
        # if no title provided, fallback to the URL which should be provided
        # by any exiting service
        title = (data['title'] if 'title' in data else data['link'])
        return title

    def set_card_content(self, data):
        """
            handle the content from the data
        """
        content = ''
        if 'content' in data:
            if type(data['content']) is list or type(data['content']) is tuple\
               or type(data['content']) is dict:
                if 'value' in data['content'][0]:
                    content = data['content'][0].value
            else:
                if type(data['content']) is str:
                    content = data['content']
                else:
                    # if not str or list or tuple
                    # or dict it could be feedparser.FeedParserDict
                    # so get the item value
                    content = data['content']['value']

        elif 'summary_detail' in data:
            if type(data['summary_detail']) is list or\
               type(data['summary_detail']) is tuple or\
               type(data['summary_detail']) is dict:
                if 'value' in data['summary_detail'][0]:
                    content = data['summary_detail'][0].value
            else:
                if type(data['summary_detail']) is str:
                    content = data['summary_detail']
                else:
                    # if not str or list or tuple
                    # or dict it could be feedparser.FeedParserDict
                    # so get the item value
                    content = data['summary_detail']['value']

        elif 'description' in data:
            content = data['description']

        return content

    def auth(self, request):
        """
            let's auth the user to the Service
        """
        callback_url = 'http://%s%s' % (
            request.get_host(), reverse('trello_callback'))

        request_token = self.get_request_token()

        # Save the request token information for later
        request.session['oauth_token'] = request_token['oauth_token']
        request.session['oauth_token_secret'] = request_token[
            'oauth_token_secret']

        # URL to redirect user to, to authorize your app
        auth_url_str = '{auth_url}?oauth_token={token}&scope={scope}&name={name}'
        auth_url_str += '&expiration={expiry}&oauth_callback={callback_url}'
        auth_url = auth_url_str.format(auth_url=self.AUTH_URL,
                                       token=request_token['oauth_token'],
                                       scope=self.scope,
                                       name=self.app_name,
                                       expiry=self.expiry,
                                       callback_url=callback_url)

        return auth_url

    def callback(self, request):
        """
            Called from the Service when the user accept to activate it
        """

        try:
            # finally we save the user auth token
            # As we already stored the object ServicesActivated
            # from the UserServiceCreateView now we update the same
            # object to the database so :
            # 1) we get the previous objet
            us = UserService.objects.get(
                user=request.user,
                name=ServicesActivated.objects.get(name='ServiceTrello'))
            # 2) Trello API require to use 4 parms consumer_key/secret +
            # token_key/secret instead of usually get just the token
            # from an access_token request. So we need to add a string
            # seperator for later use to slpit on this one
            access_token = self.get_access_token(
                request.session['oauth_token'],
                request.session['oauth_token_secret'],
                request.GET.get('oauth_verifier', '')
            )
            us.token = access_token.get('oauth_token') + \
                '#TH#' + access_token.get('oauth_token_secret')
            # 3) and save everything
            us.save()
        except KeyError:
            return '/'

        return 'trello/callback.html'

    def get_request_token(self):
        oauth = OAuth1Session(self.consumer_key,
                              client_secret=self.consumer_secret)
        return oauth.fetch_request_token(self.REQ_TOKEN)

    def get_access_token(self, oauth_token, oauth_token_secret,
                         oauth_verifier):
        # Using OAuth1Session
        oauth = OAuth1Session(self.consumer_key,
                              client_secret=self.consumer_secret,
                              resource_owner_key=oauth_token,
                              resource_owner_secret=oauth_token_secret,
                              verifier=oauth_verifier)
        oauth_tokens = oauth.fetch_access_token(self.ACC_TOKEN)

        return oauth_tokens
예제 #18
0
def create_new_board(key, token, name_of_board):
    client = TrelloClient(key, token)
    client.add_board(name_of_board)
예제 #19
0
class TrelloBoard(object):
    def __init__(self, api_key, token):
        """Creates a TrelloBoard object

        :param api_key: (str) Your Trello api key https://trello.com/1/appKey/generate
        :param token:  (str) Your Trello token
        """
        self.tc = TrelloClient(api_key=api_key, token=token)
        self._ab_id_cache = {}
        self._ab_name_cache = {}
        self._ab_slack_cache = {}
        # self._warmup_caches()

    @property
    def boards(self):
        """All the boards that can be accessed

        :return: (Board) list of Board
        """
        return self.tc.list_boards()

    @property
    def addressbook(self):
        board = self._board("Address Book")
        ab = {}
        for l in board.list_lists(list_filter="open"):
            for card in l.list_cards():
                info = yaml.load(card.desc)
                if info:
                    ab[info["id"]] = {
                        "name": card.name,
                        "slack": info["slack"]
                    }
        self._ab_id_cache = ab
        return ab

    @lru_cache(maxsize=128)
    def _org_id(self, team_name):
        """Get the id of a Trello team

        :param team_name:
        :return:
        """
        orgs = self.tc.list_organizations()
        for org in orgs:
            if org.name == team_name:
                return org.id

    @lru_cache(maxsize=128)
    def _board(self, board_name):
        logger.debug("Looking up board {}".format(board_name))
        board = [b for b in self.boards if b.name == board_name]
        if board:
            return board[0]

    @lru_cache(maxsize=128)
    def _board_by_url(self, board_url):
        board = [b for b in self.boards if b.url == board_url]
        if board:
            return board[0]

    @lru_cache(maxsize=128)
    def _member(self, member_id, board_name):
        member_id = str(member_id)
        board = self._board(board_name)

        if not board:
            return None

        for l in board.list_lists(list_filter="open"):
            for card in l.list_cards():
                if card.desc == member_id:
                    return card

    @lru_cache(maxsize=128)
    def _label(self, label_name, board_name):
        board = self._board(board_name)
        label = [l for l in board.get_labels() if l.name == label_name]
        if label:
            return label[0]

    def _warmup_caches(self):
        logger.debug("Warming up the caches")
        ids = self.addressbook
        try:
            for meetup_name, slack_name in [(n["name"], n["slack"])
                                            for n in ids.values()]:
                _ = self.contact_by_name(meetup_name)
                if slack_name:
                    _ = self.contact_by_slack_name(slack_name)
        except Exception as e:
            logger.warning("Exception {} when warming up caches".format(e))

    def create_board(self, board_name, team_name=None):
        logger.debug("Checking for board {} on {} team".format(
            board_name, team_name))
        template = self._board("Meetup Template")
        board = self._board(board_name)
        org_id = self._org_id(team_name=team_name)

        if not board:
            logger.debug("Adding board {}".format(board_name))
            self.tc.add_board(board_name=board_name,
                              source_board=template,
                              organization_id=org_id,
                              permission_level="public")

    def add_rsvp(self, name, member_id, board_name):
        logger.debug("Adding rsvp {} to {}".format(name, board_name))
        member_id = str(member_id)
        board = self._board(board_name)
        if not board:
            return None

        if not self._member(member_id, board_name):
            rsvp_list = board.list_lists(list_filter="open")[0]
            rsvp_list.add_card(name=name, desc=member_id)

    def cancel_rsvp(self, member_id, board_name):
        logger.debug("Cancelling RSVP for members id {} at {}".format(
            member_id, board_name))
        card = self._member(member_id, board_name)
        logger.debug("Card for member id {} is {}".format(member_id, card))
        canceled = self._label("Canceled", board_name)
        logger.debug("Canceled tag is {}".format(canceled))
        if card:
            card.add_label(canceled)

    def tables_detail(self, board_name):
        tables = {}
        board = self._board(board_name)
        info_card = None
        if not board:
            return None
        for table in board.list_lists(list_filter="open"):
            names = []
            title = table.name if not table.name.startswith(
                "RSVP") else "~ without a table ~"
            for card in table.list_cards():
                if card.name != "Info" and not card.labels:
                    names.append(card.name)
                elif card.name == "Info":
                    info_card = card
                elif card.labels:
                    for label in card.labels:
                        if label.name == "GM":
                            names.append(card.name + " (GM)")
                        elif label.name == "Canceled":
                            names.append(card.name + " (CANCELED)")
                        else:
                            names.append(card.name)
            if info_card:
                full_info = info_card.desc.split("Players: ", 1)
                blurb = full_info[0]
                if len(full_info) == 2:
                    players = full_info[1]
                else:
                    players = ""
            else:
                blurb, players = "", ""

            tables[title] = {"members": names, "blurb": blurb}
            tables[title]["players"] = players or "Unknown"
        resp = OrderedDict(sorted(tables.items()))
        return resp

    def table(self, board_name, list_name):
        return self.tables_detail(board_name)[list_name]

    def contact_by_name(self, member_name):
        logger.debug("Checking {}".format(member_name))
        if self._ab_name_cache.get(member_name):
            return self._ab_name_cache[member_name]
        else:
            board = self._board("Address Book")
            for l in board.list_lists(list_filter="open"):
                for card in l.list_cards():
                    desc = yaml.load(card.desc)
                    if card.name == member_name and desc["slack"]:
                        logger.debug("Desc: {}".format(desc))
                        self._ab_name_cache[member_name] = yaml.load(card.desc)
                        return self._ab_name_cache[member_name]

    def contact_by_slack_name(self, slack_name):
        if self._ab_slack_cache.get(slack_name):
            return self._ab_slack_cache[slack_name]
        else:
            board = self._board("Address Book")
            try:
                for l in board.list_lists(list_filter="open"):
                    for card in l.list_cards():
                        desc = yaml.load(card.desc)
                        if desc["slack"] == slack_name:
                            self._ab_slack_cache[slack_name] = {
                                "name": card.name,
                                "id": desc["id"]
                            }
                            return self._ab_slack_cache[slack_name]
            except:
                logger.debug("Nothing found for {}".format(slack_name))

    def contact_by_id(self, member_id):
        if self._ab_id_cache.get(member_id):
            return self._ab_id_cache[member_id]
        else:
            board = self._board("Address Book")
            for l in board.list_lists(list_filter="open"):
                for card in l.list_cards():
                    info = yaml.load(card.desc)
                    if info:
                        if info['id'] == member_id and info["slack"]:
                            self._ab_id_cache[member_id] = {
                                "name": card.name,
                                "slack": info["slack"]
                            }
                            return self._ab_id_cache[member_id]

    def add_contact(self, member_name, member_id):
        member_id = str(member_id)
        if self._ab_id_cache.get(member_id):
            return True

        board = self._board("Address Book")
        ab_list = board.list_lists(list_filter="open")[0]
        info = yaml.dump({
            "id": member_id,
            "slack": None
        },
                         default_flow_style=False)
        no_slack = self._label("NoSlack", "Address Book")

        for card in ab_list.list_cards():
            desc = yaml.load(card.desc)
            if desc["id"] == member_id:
                return True

        ab_list.add_card(name=member_name, desc=info, labels=[no_slack])

    def add_table(self, title, info, board_url):
        board = self._board_by_url(board_url)
        table_numbers = [
            int(n.name.split(".", 1)[0])
            for n in board.list_lists(list_filter="open")
            if n.name[0].isnumeric()
        ]
        ordinal = max(table_numbers) + 1 if table_numbers else 1
        title = "{}. {}".format(ordinal, title)
        table = board.add_list(name=title, pos="bottom")
        info = "\n\nPlayers:".join(info.split("Players:"))
        table.add_card("Info", desc=info)
        return "Table *{}* added to *{}*".format(title, board.name)
예제 #20
0
class ServiceTrello(ServicesMgr):

    # Boards own Lists own Cards

    def __init__(self, token=None):
        super(ServiceTrello, self).__init__(token)
        # app name
        self.app_name = DjangoThConfig.verbose_name
        # expiration
        self.expiry = "30days"
        # scope define the rights access
        self.scope = 'read,write'

        base = 'https://www.trello.com'
        self.AUTH_URL = '{}/1/OAuthAuthorizeToken'.format(base)
        self.REQ_TOKEN = '{}/1/OAuthGetRequestToken'.format(base)
        self.ACC_TOKEN = '{}/1/OAuthGetAccessToken'.format(base)
        self.consumer_key = settings.TH_TRELLO['consumer_key']
        self.consumer_secret = settings.TH_TRELLO['consumer_secret']
        if token:
            token_key, token_secret = token.split('#TH#')
            self.trello_instance = TrelloClient(self.consumer_key,
                                                self.consumer_secret,
                                                token_key,
                                                token_secret)

    def read_data(self, **kwargs):
        """
            get the data from the service

            :param kwargs: contain keyword args : trigger_id at least
            :type kwargs: dict
        """
        trigger_id = kwargs['trigger_id']
        data = list()
        cache.set('th_trello_' + str(trigger_id), data)

    def process_data(self, **kwargs):
        """
            get the data from the cache
            :param kwargs: contain keyword args : trigger_id at least
            :type kwargs: dict
        """
        kw = {'cache_stack': 'th_trello',
              'trigger_id': str(kwargs['trigger_id'])}
        return super(ServiceTrello, self).process_data(**kw)

    def save_data(self, trigger_id, **data):
        """
            let's save the data

            :param trigger_id: trigger ID from which to save data
            :param data: the data to check to be used and save
            :type trigger_id: int
            :type data:  dict
            :return: the status of the save statement
            :rtype: boolean
        """
        from th_trello.models import Trello

        status = False
        kwargs = {'output_format': 'md'}
        title, content = super(ServiceTrello, self).save_data(trigger_id,
                                                              data,
                                                              **kwargs)

        if len(title):
            # get the data of this trigger
            t = Trello.objects.get(trigger_id=trigger_id)
            # footer of the card
            footer = self.set_card_footer(data, t)
            content += footer

            # 1 - we need to search the list and board where we will
            # store the card so ...

            # 1.a search the board_id by its name
            # by retreiving all the boards
            boards = self.trello_instance.list_boards()

            board_id = ''
            my_list = ''
            for board in boards:
                if t.board_name == board.name.decode('utf-8'):
                    board_id = board.id
                    break

            if board_id:
                # 1.b search the list_id by its name
                my_board = self.trello_instance.get_board(board_id)
                lists = my_board.open_lists()
                # just get the open list ; not all the archive ones
                for list_in_board in lists:
                    # search the name of the list we set in the form
                    if t.list_name == list_in_board.name.decode('utf-8'):
                        # return the (trello) list object
                        # to be able to add card at step 3
                        my_list = my_board.get_list(list_in_board.id)
                        break
                # we didnt find the list in that board
                # create it
                if my_list == '':
                    my_list = my_board.add_list(t.list_name)

            else:
                # 2 if board_id and/or list_id does not exist, create it/them
                my_board = self.trello_instance.add_board(t.board_name)
                # add the list that didnt exists and
                # return a (trello) list object
                my_list = my_board.add_list(t.list_name)

            # 3 create the card
            # create the Trello card
            my_list.add_card(title, content)

            sentance = str('trello {} created').format(data['link'])
            logger.debug(sentance)
            status = True
        else:
            sentance = "no token or link provided for trigger ID {}"
            logger.critical(sentance.format(trigger_id))
            status = False

        return status

    def set_card_footer(self, data, trigger):
        """
            handle the footer of the note
        """
        footer = ''
        if 'link' in data:
            provided_by = _('Provided by')
            provided_from = _('from')
            footer_from = "<br/><br/>{} <em>{}</em> {} <a href='{}'>{}</a>"

            description = trigger.trigger.description
            footer = footer_from.format(
                provided_by, description, provided_from,
                data['link'], data['link'])

        return footer

    def auth(self, request):
        """
            let's auth the user to the Service
        """
        request_token = super(ServiceTrello, self).auth(request)
        callback_url = self.callback_url(request, 'trello')

        # URL to redirect user to, to authorize your app
        auth_url_str = '{auth_url}?oauth_token={token}'
        auth_url_str += '&scope={scope}&name={name}'
        auth_url_str += '&expiration={expiry}&oauth_callback={callback_url}'
        auth_url = auth_url_str.format(auth_url=self.AUTH_URL,
                                       token=request_token['oauth_token'],
                                       scope=self.scope,
                                       name=self.app_name,
                                       expiry=self.expiry,
                                       callback_url=callback_url)

        return auth_url

    def callback(self, request, **kwargs):
        """
            Called from the Service when the user accept to activate it
        """
        kwargs = {'access_token': '', 'service': 'ServiceTrello',
                  'return': 'trello'}
        return super(ServiceTrello, self).callback(request, **kwargs)
예제 #21
0
class TrelloCmd(Command):
    "Update trello board from launchpad filters"

    log = logging.getLogger(__name__)

    def get_parser(self, prog_name):
        parser = super(TrelloCmd, self).get_parser(prog_name)
        parser.add_argument(
            '--filter', type=str, action='append', required=True,
            help="List of params for searchTasks",
        )
        parser.add_argument(
            '--project', type=str, action='append', required=True,
            help="Project"
        )
        parser.add_argument(
            '--board', type=str, required=True,
            help="Trello board name"
        )
        parser.add_argument(
            '--trello-key', type=str, required=False,
            help="You can get one at https://trello.com/app-key"
        )
        parser.add_argument(
            '--trello-secret', type=str, required=False,
            help="You can get one at https://trello.com/app-key"
        )
        parser.add_argument(
            '--trello-token', type=str, required=False,
            help="You can get one at https://trello.com/1/connect?" +
                 "key=YOUR_TRELLO_KEY&name=bugfix-app&response_type=token&" +
                 "scope=read,write&expiration=never"
        )
        parser.add_argument(
            '--trello-token-secret', type=str, required=False,
        )
        parser.add_argument(
            '--create-board', action='store_true',
            help='Create Trello board if not exists'
        )
        parser.add_argument(
            '--use-labels', nargs='+',
            help='Labels for cards', default=[
                'tricky', 'low-hanging-fruit', 'tech-debt'
            ]
        )
        return parser

    def take_action(self, parsed_args):
        err_count = 0
        logging.getLogger("requests").setLevel(logging.WARNING)
        self.log.info('Connecting to Launchpad')
        self.lp = Launchpad.login_with(
            'lp-report-bot', 'production', version='devel')
        self.tr = TrelloClient(
            api_key=parsed_args.trello_key,
            api_secret=parsed_args.trello_secret,
            token=parsed_args.trello_token,
            token_secret=parsed_args.trello_token_secret)
        try:
            self.board = [
                board for board in self.tr.list_boards()
                if board.name == parsed_args.board
            ][0]
        except IndexError:
            if parsed_args.create_board:
                self.board = self.tr.add_board(parsed_args.board)
                # for label in self.board.all_lists():
                #    #label.delete()
                #    #                    self.client.fetch_json(
                #    #            '/cards/' + self.id,
                #    #            http_method='DELETE')
                for list in self.board.open_lists():
                    list.close()
            else:
                raise Exception(
                    "Board {0} doesn't exist. Use --create-board argument" +
                    " in order to create it".format(parsed_args.board))
        self.log.info("Working with board {0}".format(self.board))
        self.tag_labels = parsed_args.use_labels
        self.cards = dict()
        self.untouched_cards = dict()
        for card in self.board.open_cards():
            groups = re.search('(\d+)', card.name)
            if not (groups is None):
                bug_id = groups.group(0)
                if bug_id not in self.cards:
                    self.untouched_cards[bug_id] = card
                    self.log.debug(
                        "Found existing card for bug {0}".format(bug_id))
                    self.cards[bug_id] = card
                else:
                    self.log.info(
                        "Killing duplicate card for bug {0}".format(bug_id))
                    card.delete()
        self.log.info("Found {0} existing cards".format(
            len(self.untouched_cards)))

        for prj_name in parsed_args.project:
            prj = self.lp.projects[prj_name]
            for f in parsed_args.filter:
                self.log.debug(f)
                filt = json.loads(f)
                if filt['milestone']:
                    filt['milestone'] = prj.getMilestone(
                        name=filt['milestone'])
                if 'assignee' in filt:
                    filt['assignee'] = self.lp.people[filt['assignee']]
                if 'status' not in filt:
                    filt['status'] = [
                        'New', 'Incomplete', 'Opinion', 'Invalid',
                        'Won\'t Fix', 'Expired', 'Confirmed', 'Triaged',
                        'In Progress', 'Fix Committed', 'Fix Released'
                    ]
                self.log.debug(filt)
                self.log.info("Searching for tasks in project %s" % prj_name)
                for task in prj.searchTasks(**filt):
                    self.log.info("Proceeding task %s" % task)
                    retries = 3
                    for i in range(retries):
                        try:
                            self.proceed_task(task)
                        except Exception as e:
                            if i < retries:
                                self.log.exception(e)
                                self.log.warning(
                                    "Got an exception for task %s, retrying"
                                    % task)
                                continue
                            else:
                                self.log.exception(e)
                                self.log.warning(
                                    "Failed to proceed task %s" % task)
                                err_count += 1
                        break
                for series in prj.series:
                    self.log.info("Searching for tasks in {0}:{1}".format(
                        str(prj.name), str(series.name)))
                    for task in series.searchTasks(**filt):
                        self.log.info("Proceeding task %s" % task)
                        retries = 3
                        for i in range(retries):
                            try:
                                self.proceed_task(task)
                            except Exception as e:
                                if i < retries:
                                    continue
                                else:
                                    self.log.exception(e)
                                    self.log.warning(
                                        "Failed to proceed task %s" % task)
                                    err_count += 1
                            break

        if self.untouched_cards:
            self.log.info("%d cards are out of scope" % len(
                self.untouched_cards))
            try:
                out_of_scope_list = [
                    list for list in self.board.open_lists()
                    if list.name == 'Trash/Out of scope'][0]
            except IndexError:
                out_of_scope_list = self.board.add_list('Trash/Out of scope')
            for card in self.untouched_cards.values():
                card.change_list(out_of_scope_list.id)

        self.log.info("Finished with %d errors" % err_count)
        if err_count > 0:
            return 1
        return 0

    def get_task_reviews(self, task):
        self.log.debug("Searching for reviews for task {0}".format(task))
        bug = task.bug
        gerrits = [
            'https://review.openstack.org/', 'https://review.fuel-infra.org/']
        reviews = []
        # Message number 0 is description
        is_description = True
        for msg in bug.messages:
            if is_description:
                is_description = False
                continue
            for g in gerrits:
                reviews += re.findall(g + '\d+', msg.content)
                long_reviews = re.findall(g + '#/c/\d+', msg.content)
                for u in long_reviews:
                    reviews += [u.replace('#/c/', '')]
        open_reviews = []
        for rev_url in set(reviews):
            [base_url, id] = rev_url.rsplit('/', 1)
            rest = GerritRestAPI(base_url)
            try:
                review = rest.get('/changes/{0}/detail'.format(id))
                if review['status'] == 'NEW':
                    status = []
                    if 'rejected' in review['labels']['Workflow']:
                        status.append('WIP')
                    if 'disliked' in review['labels']['Verified']:
                        status.append('FAIL')
                    open_reviews.append({'url': rev_url, 'status': status})
                    self.log.debug("Found open review {0}".format(rev_url))
            except Exception:
                pass

        return open_reviews

    def get_task_list(self, task):
        list_name = 'Bad Status'
        try:
            if task.status in ['Confirmed']:
                if task.assignee is None or task.assignee.is_team:
                    list_name = 'Inbox/Need triage'
                else:
                    list_name = 'Assigned/Investigating'
            if task.status in ['Incomplete']:
                list_name = 'Incomplete/Need more info'
            if task.status in ['Triaged']:
                list_name = 'Triaged/Ready to be fixed'
            if task.status in ['In Progress']:
                if self.get_task_reviews(task):
                    list_name = 'In Progress/Need review'
                else:
                    list_name = 'In Progress/Working on fix'
            if task.status in ['Fix Committed', 'Fix Released']:
                list_name = 'Fix Committed/Done'
            if task.status in ['Invalid', 'Opinion', 'Won\'t Fix']:
                list_name = 'Won\'t Fix/Done'
            if task.status in ['New']:
                list_name = 'New/Need confirmation'
            # if (
            #     not filter(lambda x: x.startswith('team-'), task.bug.tags)
            #     and 'tech-debt' not in task.bug.tags and
            #     task.status in [
            #         'New', 'Confirmed', 'Triaged', 'In Progress',
            #         'Incomplete']
            #     ):
            #     list_name = 'New/Need confirmation'
            if 'blocked' in task.bug.tags:
                list_name = 'Blocked/On hold'
            return [
                list for list in self.board.open_lists()
                if list.name == list_name][0]
        except IndexError:
            return self.board.add_list(list_name)

    def get_task_labels(self, task):
        bug = task.bug
        tags = list(set(bug.tags).intersection(self.tag_labels))
        # Each bug should have either team tag or no-team tag or tech-debt tag
        team_tags = filter(lambda x: x.startswith('team-'), bug.tags)
        if team_tags:
            tags += team_tags
        else:
            if 'tech-debt' not in bug.tags:
                tags += ['no-team']
        if not filter(lambda x: x.startswith('area-'), task.bug.tags):
            tags += ['no-area']
        # if task.importance in ['Critical', 'High']:
        #     tags.append('high-priority')
        return tags

    def get_card_title(self, task):
        bug = task.bug
        assignee_id = "unassigned"
        if task.assignee_link is not None:
            assignee_id = task.assignee_link.split('~')[-1]
        return u'Bug {0} ({1}): {2}'.format(
            bug.id, assignee_id, bug.title)[:200]

    def get_card_description(self, task, card_list):
        bug = task.bug
        desc = "Created by {0}\n".format(bug.owner_link.split('~')[-1])
        desc += bug.web_link + "\n"
        if card_list.name == 'In Progress/Need review':
            desc += "Reviews:\n" + "\n".join(map(
                lambda x: u"{0} {1}".format(x['url'], ':'.join(x['status'])),
                self.get_task_reviews(task)
            )) + "\n"
        desc += "\n----------\n" + bug.description
        return desc[:1000]

    def proceed_task(self, task):
        self.log.debug("Processing task {0}".format(task))
        bug = task.bug
        card_list = self.get_task_list(task)
        if str(bug.id) not in self.cards:
            self.log.debug("Creating card for bug {0}".format(bug.id))
            card = card_list.add_card(
                self.get_card_title(task),
                self.get_card_description(task, card_list))
            self.cards[bug.id] = card
        else:
            self.log.debug("Getting card for task {0}".format(task))
            card = self.cards[str(bug.id)]
            try:
                del self.untouched_cards[str(bug.id)]
            except KeyError:
                pass
            self.log.debug(
                (
                    "Updating existing card for bug {0}, moving to {1} list"
                ).format(bug.id, card_list))
            card.change_list(card_list.id)
            new_name = self.get_card_title(task)
            if new_name != card.name.decode('utf-8'):
                card.set_name(new_name)
            new_desc = self.get_card_description(task, card_list)
            if new_desc != card.description:
                card.set_description(new_desc)
        tags = self.get_task_labels(task)
        for label in card.labels:
            if label.name not in tags:
                # delete_label is not published on pypi yet
                # card.delete_label(label)
                card.client.fetch_json(
                    '/cards/' + card.id + '/idLabels/' + label.id,
                    http_method='DELETE')
        for label_name in tags:
            try:
                label = [
                    l for l in self.board.get_labels()
                    if l.name == label_name][0]
            except IndexError:
                label = self.board.add_label(label_name, 'green')
            try:
                card.add_label(label)
            except Exception:
                pass
        self.log.debug(task)
예제 #22
0
class TarDriver:

    #Конструктор класса
    def __init__(
            self,
            trello_apiKey='',  #apiKey для подключения к trello
            trello_token='',  #apiToken для подключения к trello
            local_timezone='Asia/Tomsk'):

        self.API_KEY = trello_apiKey
        self.TOKEN = trello_token
        self.local_timezone = tz(local_timezone)
        self.filter_dates = []
        self.database_is_updating = False

        #Подключение к Trello
        try:
            self.trello_client = TrelloClient(
                api_key=self.API_KEY,
                token=self.TOKEN,
            )
        except Exception as err:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [ERROR] Failed to connect to Trello via API: {err}'
            )
        else:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [MSG] Connection to Trello established successful'
            )

        #Создание файла БД и таблиц в БД
        try:
            self.db = TinyDB('tar_database.json')
            #self.db.drop_tables()      !!!!!!!!!!!!!!!!!!!!!

            self.report = self.db.table('report')
            self.worktime = self.db.table('worktime')
            self.local_boards = self.db.table('boards')
            self.local_lists = self.db.table('lists')
            self.local_cards = self.db.table('cards')
            self.local_persons = self.db.table('persons')
            self.local_cards_has_persons = self.db.table('cards_has_persons')

        except Exception as err:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [ERROR] Failed to setup tar_database: {err}'
            )
        else:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [MSG] tar_database created'
            )

        self.basic_template = [{
            0:
            'Перечень Задач',
            'cards': [
                'П3 - Описание автоматизируемых функций',
                'П5 - Описание информационного обеспечения',
                'В1 - Описание входных сигналов и данных',
                'В2 - Описание выходных сигналов (сообщений)',
                'ПА - Описание программного обеспечения',
                'ПБ - Описание алгоритма', 'П6 - Описание информационной базы',
                'С9 - Чертеж форм (видеокадра)', 'И3 - Руководство оператора',
                'И3 - Руководство программиста',
                'И4 - Инструкция по ведению БД',
                'ПМИ - Программа и методика испытаний',
                'ПО ПЛК - Программа контроллера',
                'ПО Панели - Программа панели оператора',
                'ПО АРМ - Программа рабочего места оператора',
                'ПО БД - База данных', 'Ежедневная планерка',
                'Планирование цели (спринт)',
                'Анализ завершения цели (спринта)'
            ]
        }, {
            1: 'Комплекс Задач',
            'cards': []
        }, {
            2: 'В Работе',
            'cards': []
        }, {
            3: 'Согласование выполнения',
            'cards': []
        }, {
            4: 'Завершены',
            'cards': []
        }, {
            5: 'Отменены',
            'cards': []
        }]

        self.db.drop_table('worktime')
        self.worktime.insert({
            'work_day_starts': '09:00:00',
            'work_day_ends': '18:00:00',
            'work_day_duration': '09:00:00',
            'lunch_hours_starts': '13:00:00',
            'lunch_hours_ends': '14:00:00',
            'lunch_duration': '01:00:00',
            'day_work_hours': '08:00:00',
            'work_days': '5',
            'week_work_hours': '1 day, 16:00:00',
            'update_period': '00:02:00'
        })

    def add_board(self, board):
        #Добавление новой доски в БД
        try:
            self.local_boards.insert({
                'board_id':
                board.id,
                'board_name':
                board.name,
                'board_description':
                board.description,
                'board_last_modified':
                str(board.date_last_activity)
            })

            for list_ in board.list_lists():
                self.local_lists.insert({
                    'list_id':
                    list_.id,
                    'list_name':
                    list_.name,
                    'list_last_modified':
                    str(datetime.now().strftime("%Y-%m-%d %H:%M:%S")),
                    'board_id':
                    board.id,
                    'board_name':
                    board.name
                })

                for card in list_.list_cards():

                    self.local_cards.insert({
                        'card_id': card.id,
                        'card_name': card.name,
                        'list_id': list_.id,
                        'list_name': list_.name,
                        'board_id': board.id,
                        'board_name': board.name
                    })

                    if len(card.member_id) > 0:
                        for person in self.team:
                            if person.id in card.member_id:
                                query_result = self.local_persons.get(
                                    where('person_id') == str(person.id))
                                self.local_cards_has_persons.insert({
                                    'card_id':
                                    card.id,
                                    'card_name':
                                    card.name,
                                    'person_id':
                                    person.id,
                                    'person_name':
                                    query_result['person_fullname'],
                                    'list_id':
                                    list_.id,
                                    'list_name':
                                    list_.name,
                                    'board_id':
                                    board.id,
                                    'board_name':
                                    board.name
                                })

        except Exception as err:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [ERROR] Failed to add "{board.name}": {err}'
            )
        else:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [MSG] "{board.name}" added successful'
            )

    def delete_board(self, board_id, board_name=''):
        #Удаление доски из БД
        try:
            #Удаляем записи из таблицы local_cards_has_persons
            self.local_cards_has_persons.remove(
                where('board_id') == str(board_id))
            #Удаляем записи из таблицы local_cards
            self.local_cards.remove(where('board_id') == str(board_id))
            #Удаляем записи из таблицы local_lists
            self.local_lists.remove(where('board_id') == str(board_id))
            #Удаляем записи из таблицы local_boards
            self.local_boards.remove(where('board_id') == str(board_id))
        except Exception as err:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [ERROR] Failed to delete {board_id}: {err}'
            )
        else:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [MSG] {board_id} deleted successful'
            )

    def update_board(self, board):
        #Обновление доски в БД
        datetime_format = "%Y-%m-%d %H:%M:%S.%f%z"

        try:
            query_result = self.local_boards.get(
                where('board_id') == str(board.id))

        except Exception as err:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [MSG] Updating "{board.name}"...{err}'
            )
            self.delete_board(board_id=board.id, board_name=board.name)
            self.add_board(board=board)
        else:
            board_date_last_activity = self.unify_time(
                datetime.strptime(query_result['board_last_modified'],
                                  datetime_format))

            if self.unify_time(
                    board.date_last_activity) > board_date_last_activity:
                print(
                    f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [MSG] Updating "{board.name}"...'
                )
                self.delete_board(board_id=board.id, board_name=board.name)
                self.add_board(board=board)

    def fill_main_boards(self):
        #Заполнение таблиц local_boards, local_lists, local_cards
        print(
            f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [MSG] Filling "local_boards / local_lists / local_cards" tables...'
        )
        try:
            for board in self.trello_client.list_boards():
                self.add_board(board=board)
        except Exception as err:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [ERROR] Failed to fill "local_boards / local_lists / local_cards" tables: {err}'
            )
        else:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [MSG] "local_boards / local_lists / local_cards" tables filled successful'
            )

    def fill_persons(self, team_board_name='КАДРЫ'):
        #Заполнение таблицы local_persons
        try:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [MSG] Filling "local_persons" table...'
            )
            for board in self.trello_client.list_boards():
                if board.name == team_board_name:
                    self.team = board.get_members(filters='all')
                    for list_ in board.list_lists():
                        for card in list_.list_cards():
                            if len(card.member_id) > 0:
                                for person in self.team:
                                    if person.id in card.member_id:
                                        self.local_persons.insert({
                                            'person_id':
                                            person.id,
                                            'person_username':
                                            person.username,
                                            'person_fullname':
                                            card.name,
                                            'status':
                                            list_.name,
                                            'last_modified':
                                            str(datetime.now().strftime(
                                                "%Y-%m-%d %H:%M:%S"))
                                        })
                    break
        except Exception as err:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [ERROR] Failed to fill "local_persons" table: {err}'
            )
        else:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [MSG] "local_persons" table filled successful'
            )

    def fill_cards_has_persons(self):
        #Заполнение таблицы cards_has_persons
        try:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [MSG] Filling "cards_has_persons" table...'
            )
            for board in self.trello_client.list_boards():
                for list_ in board.list_lists():
                    for card in list_.list_cards():
                        if len(card.member_id) > 0:
                            for person in self.team:
                                if person.id in card.member_id:
                                    query_result = self.local_persons.get(
                                        where('person_id') == str(person.id))
                                    self.local_cards_has_persons.insert({
                                        'card_id':
                                        card.id,
                                        'card_name':
                                        card.name,
                                        'person_id':
                                        person.id,
                                        'person_name':
                                        query_result['person_fullname'],
                                        'list_id':
                                        list_.id,
                                        'list_name':
                                        list_.name,
                                        'board_id':
                                        board.id,
                                        'board_name':
                                        board.name
                                    })
        except Exception as err:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [ERROR] Failed to fill "cards_has_persons" table: {err}'
            )
        else:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [MSG] "cards_has_persons" table filled successful'
            )

    def fill_database(self):
        self.fill_persons()
        self.fill_main_boards()

    def update_database(self, update_on_change=False):

        time_format = "%H:%M:%S"

        self.db.drop_table('persons')
        self.fill_persons()

        update = True

        while update:

            self.database_is_updating = True

            update_period_time = (time.strptime(self.get_update_period(),
                                                time_format))
            update_period_seconds = timedelta(
                hours=update_period_time.tm_hour,
                minutes=update_period_time.tm_min,
                seconds=update_period_time.tm_sec).total_seconds()

            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [MSG] Checking for updates...'
            )

            trello_boards = self.trello_client.list_boards()
            local_boards = self.local_boards.all()

            if len(trello_boards) > len(
                    local_boards):  #в trello добавили доску
                #ищем какую
                tempBoards = []

                for board in local_boards:
                    tempBoards.append(board['board_id'])

                for board in trello_boards:
                    if board.id not in tempBoards:  #новая доска обнаружена
                        self.add_board(board=board)

                print(
                    f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [MSG] Checking for updates finished'
                )

            elif len(trello_boards) < len(
                    local_boards):  #в trello удалили доску
                #ищем какую
                tempBoards = []

                for board in trello_boards:
                    tempBoards.append(board.id)

                for board in local_boards:
                    if board[
                            'board_id'] not in tempBoards:  #новая доска обнаружена
                        self.delete_board(board_id=board['board_id'],
                                          board_name=board['board_name'])

                print(
                    f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [MSG] Checking for updates finished'
                )

            else:  #обновляем все доски. Новых / удаленных не обнаружено
                for board in trello_boards:
                    self.update_board(board=board)

                print(
                    f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [MSG] Checking for updates finished'
                )

                if not update_on_change:
                    time.sleep(update_period_seconds)

                    self.database_is_updating = False

    def get_persons_active_tasks(self, person_id, active_list_name='В Работе'):
        query_result = self.local_cards_has_persons.search(
            (where('person_id') == str(person_id))
            & (where('list_name') == str(active_list_name)))
        return len(query_result)

    # !!!!! Изменить чтоб читал пользователей доски, возможно вернуть board_has_persons
    def get_project_members(self, board_id):
        temp_persons = []
        board_persons = []
        query_result = self.local_cards_has_persons.search(
            where('board_id') == str(board_id))
        for result in query_result:
            if result['person_id'] not in temp_persons:
                temp_persons.append(result['person_id'])
                board_persons.append({
                    'person_id': result['person_id'],
                    'person_name': result['person_name']
                })
        return board_persons

    def get_tasks_on_board(self, board_id, list_name='В работе'):
        tasks = []
        query_result = self.local_cards.search(
            where('board_id') == str(board_id))
        for result in query_result:

            if result['list_name'] == list_name:

                query_result_ = self.local_cards_has_persons.search(
                    where('list_id') == str(result['list_id']))
                for result_ in query_result_:
                    if result_['card_name'] == result['card_name']:
                        task = {
                            'task_name': result['card_name'],
                            'task_member': result_['person_name'],
                            'card_in_work_time': result['card_in_work_time']
                        }
                        tasks.append(task)
                        break

        return tasks

    def get_lists_by_board_id(self, board_id):
        query_result = self.local_lists.search(
            (where('board_id') == str(board_id)))
        return query_result

    def get_active_tasks_by_person(self, person_id):
        query_result = self.local_cards_has_persons.search(
            (where('person_id') == str(person_id))
            & ((where('list_name') == str('В Работе'))))
        return query_result

    def get_curr_stage_percent(self, board_id, board_template):
        tasks_planned = self.local_cards.search(
            (where('board_id') == str(board_id))
            & (where('list_name') == str('Комплекс задач')))
        tasks_in_progress = self.local_cards.search(
            (where('board_id') == str(board_id))
            & (where('list_name') == str('В Работе')))
        tasks_on_hold = self.local_cards.search(
            (where('board_id') == str(board_id))
            & (where('list_name') == str('Согласование Выполнения')))
        tasks_done = self.local_cards.search(
            (where('board_id') == str(board_id))
            & (where('list_name') == str('Завершены')))

        if (len(tasks_planned) + len(tasks_in_progress) + len(tasks_on_hold) +
                len(tasks_done)) == 0:
            return 0
        else:
            return round((len(tasks_done) /
                          (len(tasks_planned) + len(tasks_in_progress) +
                           len(tasks_on_hold) + len(tasks_done))) * 100.0)

    def create_new_project(self,
                           project_template,
                           project_name='Новый проект',
                           project_description=''):
        self.trello_client.add_board(board_name=project_name,
                                     source_board=None,
                                     organization_id=None,
                                     permission_level='private',
                                     default_lists=False)

        for board in self.trello_client.list_boards():
            if board.name == project_name:
                board.set_description(desc=project_description)

                for list_ in range(len(project_template) - 1, -1, -1):
                    board.add_list(name=project_template[list_].get(list_),
                                   pos=None)

                for _list in board.list_lists():
                    for list_ in range(0, len(project_template)):
                        if _list.name == project_template[list_].get(list_):
                            for card in project_template[list_]['cards']:
                                _list.add_card(name=card,
                                               desc=None,
                                               labels=None,
                                               due="null",
                                               source=None,
                                               position=None,
                                               assign=None,
                                               keep_from_source="all")
                                print(
                                    f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [MSG] Card {card} was added'
                                )
                            break

    def utc_to_local(self, utc_dt):
        return utc_dt.replace(tzinfo=timezone.utc,
                              microsecond=0).astimezone(tz=None)

    def set_workhours(self, workhours=['09:00:00', '18:00:00']):
        format_ = '%H:%M:%S'
        try:
            work_day_starts = datetime.strptime(workhours[0], format_).time()
            work_day_ends = datetime.strptime(workhours[1], format_).time()
        except Exception as err:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [ERROR] {err}'
            )
            pass
            #self.worktime.update({ 'work_day_starts': str(datetime.strptime('9:00:00', format_).time())})
            #self.worktime.update({ 'work_day_ends': str(datetime.strptime('18:00:00', format_).time())})
        else:
            if work_day_starts < work_day_ends:
                self.worktime.update({'work_day_starts': str(work_day_starts)})
                self.worktime.update({'work_day_ends': str(work_day_ends)})

                work_day_duration = timedelta(hours = work_day_ends.hour, minutes = work_day_ends.minute, seconds = work_day_ends.second) \
                                            - timedelta(hours = work_day_starts.hour, minutes = work_day_starts.minute, seconds = work_day_starts.second)

                self.worktime.update(
                    {'work_day_duration': str(work_day_duration)})

                self.calculate_work_hours()

    def get_workhours(self):
        return self.worktime.get(where('work_day_starts') != None)

    def set_lunch_hours(self, lunch_hours=['13:00:00', '14:00:00']):
        format_ = '%H:%M:%S'
        try:
            lunch_hours_starts = datetime.strptime(lunch_hours[0],
                                                   format_).time()
            lunch_hours_ends = datetime.strptime(lunch_hours[1],
                                                 format_).time()
        except Exception as err:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [ERROR] {err}'
            )
            pass
        else:
            if lunch_hours_starts < lunch_hours_ends:
                self.worktime.update(
                    {'lunch_hours_starts': str(lunch_hours_starts)})
                self.worktime.update(
                    {'lunch_hours_ends': str(lunch_hours_ends)})

                lunch_duration = timedelta(hours = lunch_hours_ends.hour, minutes = lunch_hours_ends.minute, seconds = lunch_hours_ends.second) \
                                            - timedelta(hours = lunch_hours_starts.hour, minutes = lunch_hours_starts.minute, seconds = lunch_hours_starts.second)

                self.worktime.update({'lunch_duration': str(lunch_duration)})

                self.calculate_work_hours()

    def get_lunch_hours(self):
        return self.worktime.get(where('lunch_hours_starts') != None)

    def calculate_work_hours(self):
        format_ = '%H:%M:%S'
        str_work_day_duration = self.worktime.get(
            where('work_day_duration') != None)['work_day_duration']
        str_lunch_duration = self.worktime.get(
            where('lunch_duration') != None)['lunch_duration']

        time_work_day_duration = datetime.strptime(str_work_day_duration,
                                                   format_).time()
        time_lunch_duration = datetime.strptime(str_lunch_duration,
                                                format_).time()

        day_work_hours = timedelta(hours = time_work_day_duration.hour, minutes = time_work_day_duration.minute, seconds = time_work_day_duration.second) \
                                            - timedelta(hours = time_lunch_duration.hour, minutes = time_lunch_duration.minute, seconds = time_lunch_duration.second)

        self.worktime.update({'day_work_hours': str(day_work_hours)})

        work_days = self.worktime.get(where('work_days') != None)['work_days']

        week_work_hours = timedelta(seconds=int(work_days) *
                                    day_work_hours.total_seconds())
        self.worktime.update({'week_work_hours': str(week_work_hours)})

    def is_integer(self, n):
        try:
            float(n)
        except ValueError:
            return False
        else:
            return float(n).is_integer()

    def set_workdays(self, workdays='5'):
        if self.is_integer(workdays):
            if (int(workdays) >= 1) and (int(workdays) <= 7):
                self.worktime.update({'work_days': str(workdays)})
        else:
            print(
                f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [ERROR] workdays not a number'
            )

    def get_workdays(self):
        try:
            return ((self.worktime.get(
                where('work_days') != None))['work_days'])
        except:
            return '--:--'

    def set_database_update_period(self, update_period='01:00:00'):
        format_ = '%H:%M:%S'
        try:
            update_period = datetime.strptime(update_period, format_).time()
        except Exception as err:
            pass
        else:
            self.worktime.update({'update_period': str(update_period)})

    def get_update_period(self):
        try:
            return ((self.worktime.get(
                where('update_period') != None))['update_period'])
        except:
            return '--:--'

    def unify_time(self, datetime):
        return datetime.astimezone(self.local_timezone).replace(microsecond=0)

    def filter_work_hours(self, start_date, end_date):
        time_format = "%H:%M:%S"

        result_time = timedelta(hours=0, minutes=0, seconds=0)
        calculated_end_time = timedelta(hours=0, minutes=0, seconds=0)
        twelve_hours_delta = timedelta(hours=12, minutes=0)
        twenty_four_hours_delta = timedelta(hours=23, minutes=59, seconds=59)

        work_day_starts = self.worktime.get(
            where('work_day_starts') != None)['work_day_starts']
        work_day_ends = self.worktime.get(
            where('work_day_ends') != None)['work_day_ends']
        lunch_starts = self.worktime.get(
            where('lunch_hours_starts') != None)['lunch_hours_starts']
        lunch_ends = self.worktime.get(
            where('lunch_hours_ends') != None)['lunch_hours_ends']
        day_work_hours = self.worktime.get(
            where('day_work_hours') != None)['day_work_hours']

        work_day_starts = datetime.strptime(work_day_starts,
                                            time_format).time()
        work_day_ends = datetime.strptime(work_day_ends, time_format).time()
        lunch_starts = datetime.strptime(lunch_starts, time_format).time()
        lunch_ends = datetime.strptime(lunch_ends, time_format).time()
        day_work_hours = datetime.strptime(day_work_hours, time_format).time()

        while start_date <= end_date:
            till_the_end_of_he_day_delta = twenty_four_hours_delta - timedelta(
                hours=start_date.hour,
                minutes=start_date.minute,
                seconds=start_date.second)
            calculated_end_time = (start_date + till_the_end_of_he_day_delta)

            if calculated_end_time >= end_date:
                if calculated_end_time.time() > end_date.time():
                    calculated_end_time = end_date

            if start_date.weekday(
            ) < 5:  #этот день не выходной // сделать параметром чтоб менять первый день недели
                if (calculated_end_time.time() < work_day_starts
                    ):  #промежуток кончился раньше рабочего дня
                    pass

                elif (calculated_end_time.time() > work_day_starts) and (
                        calculated_end_time.time() <= lunch_starts
                ):  #промежуток кончился после начала рабочего дня но раньше обеда:

                    if start_date.time() <= work_day_starts:
                        result_time += timedelta(hours=calculated_end_time.hour, minutes=calculated_end_time.minute, seconds=calculated_end_time.second) - \
                                                    timedelta(hours=work_day_starts.hour, minutes=work_day_starts.minute, seconds=work_day_starts.second)

                    else:
                        result_time += timedelta(hours=calculated_end_time.hour, minutes=calculated_end_time.minute, seconds=calculated_end_time.second) - \
                                                    timedelta(hours=start_date.hour, minutes=start_date.minute, seconds=start_date.second)

                elif (calculated_end_time.time() > lunch_starts) and (
                        calculated_end_time.time() < lunch_ends
                ):  #промежуток кончился после начала обеда но раньше конца обеда:
                    if start_date.time() <= work_day_starts:
                        result_time += timedelta(hours=lunch_starts.hour, minutes=lunch_starts.minute, seconds=lunch_starts.second) - \
                                                    timedelta(hours=work_day_starts.hour, minutes=work_day_starts.minute, seconds=work_day_starts.second)

                    elif (start_date.time() > work_day_starts) and (
                            start_date.time() < lunch_starts):
                        result_time += timedelta(hours=lunch_starts.hour, minutes=lunch_starts.minute, seconds=lunch_starts.second) - \
                                                    timedelta(hours=start_date.hour, minutes=start_date.minute, seconds=start_date.second)

                    elif (start_date.time() >= lunch_starts):
                        pass

                elif (calculated_end_time.time() >= lunch_ends) and (
                        calculated_end_time.time() < work_day_ends
                ):  #промежуток кончился после конца обеда но раньше конца дня
                    if start_date.time() <= work_day_starts:
                        result_time += (timedelta(hours=lunch_starts.hour, minutes=lunch_starts.minute, seconds=lunch_starts.second) - \
                                                    timedelta(hours=work_day_starts.hour, minutes=work_day_starts.minute, seconds=work_day_starts.second)) + \
                                                    (timedelta(hours=calculated_end_time.hour, minutes=calculated_end_time.minute, seconds=calculated_end_time.second) - \
                                                    timedelta(hours=lunch_ends.hour, minutes=lunch_ends.minute, seconds=lunch_ends.second))

                    elif (start_date.time() > work_day_starts) and (
                            start_date.time() < lunch_starts):
                        result_time += (timedelta(hours=lunch_starts.hour, minutes=lunch_starts.minute, seconds=lunch_starts.second) - \
                                                    timedelta(hours=start_date.hour, minutes=start_date.minute, seconds=start_date.second)) + \
                                                    (timedelta(hours=calculated_end_time.hour, minutes=calculated_end_time.minute, seconds=calculated_end_time.second) - \
                                                    timedelta(hours=lunch_ends.hour, minutes=lunch_ends.minute, seconds=lunch_ends.second))

                    elif (start_date.time() >=
                          lunch_starts) and (start_date.time() < lunch_ends):
                        result_time += (timedelta(hours=calculated_end_time.hour, minutes=calculated_end_time.minute, seconds=calculated_end_time.second) - \
                                        timedelta(hours=lunch_ends.hour, minutes=lunch_ends.minute, seconds=lunch_ends.second))

                    elif (start_date.time() >= lunch_ends):
                        result_time += (timedelta(hours=calculated_end_time.hour, minutes=calculated_end_time.minute, seconds=calculated_end_time.second) - \
                                        timedelta(hours=start_date.hour, minutes=start_date.minute, seconds=start_date.second))

                elif (calculated_end_time.time() >=
                      work_day_ends):  #промежуток кончился позже рабочего дня
                    if start_date.time() <= work_day_starts:
                        result_time += timedelta(hours=day_work_hours.hour,
                                                 minutes=day_work_hours.minute,
                                                 seconds=day_work_hours.second)

                    elif (start_date.time() > work_day_starts) and (
                            start_date.time() < lunch_starts):
                        result_time += (timedelta(hours=lunch_starts.hour, minutes=lunch_starts.minute, seconds=lunch_starts.second) - \
                                                    timedelta(hours=start_date.hour, minutes=start_date.minute, seconds=start_date.second)) + \
                                                    (timedelta(hours=work_day_ends.hour, minutes=work_day_ends.minute, seconds=work_day_ends.second) - \
                                                    timedelta(hours=lunch_ends.hour, minutes=lunch_ends.minute, seconds=lunch_ends.second))

                    elif (start_date.time() >=
                          lunch_starts) and (start_date.time() < lunch_ends):
                        result_time += (timedelta(hours=work_day_ends.hour, minutes=work_day_ends.minute, seconds=work_day_ends.second) - \
                                        timedelta(hours=lunch_ends.hour, minutes=lunch_ends.minute, seconds=lunch_ends.second))

                    elif (start_date.time() >=
                          lunch_ends) and (start_date.time() <= work_day_ends):
                        result_time += (timedelta(hours=work_day_ends.hour, minutes=work_day_ends.minute, seconds=work_day_ends.second) - \
                                        timedelta(hours=start_date.hour, minutes=start_date.minute, seconds=start_date.second))

                    elif (start_date.time() > work_day_ends):
                        pass

            start_date += (till_the_end_of_he_day_delta + timedelta(minutes=1))

        return result_time

    def filter_reports_time(self, start_date, end_date, disable_filter=False):

        datetime_format = "%Y-%m-%d %H:%M:%S"
        filter_start_date = self.unify_time(
            datetime.strptime(self.filter_dates[0], datetime_format))
        filter_end_date = self.unify_time(
            datetime.strptime(self.filter_dates[1], datetime_format))

        result_time = timedelta(hours=0, minutes=0, seconds=0)

        if not disable_filter:

            #1
            if (start_date < filter_start_date) and (end_date <
                                                     filter_start_date):
                return result_time
            #2
            elif (start_date < filter_start_date) and (
                (end_date > filter_start_date) and
                (end_date < filter_end_date)):
                start_date = filter_start_date

                return self.filter_work_hours(start_date=start_date,
                                              end_date=end_date)
            #3
            elif (start_date < filter_start_date) and (end_date >
                                                       filter_end_date):
                start_date = filter_start_date
                end_date = filter_end_date

                return self.filter_work_hours(start_date=start_date,
                                              end_date=end_date)
            #4
            elif ((start_date > filter_start_date) and
                  (start_date < filter_end_date)) and (end_date <
                                                       filter_end_date):
                self.filter_work_hours(start_date=start_date,
                                       end_date=end_date)
            #5
            elif ((start_date > filter_start_date) and
                  (start_date < filter_end_date)) and (end_date >
                                                       filter_end_date):
                end_date = filter_end_date

                return self.filter_work_hours(start_date=start_date,
                                              end_date=end_date)
            #6
            elif (start_date > filter_end_date):
                return result_time

        else:
            #print("filter enabled!")
            return self.filter_work_hours(start_date=start_date,
                                          end_date=end_date)

    def get_card_stats_by_lists(self, card, disable_filter=False):

        board = self.trello_client.get_board(board_id=card.board_id)
        lists = board.list_lists()
        time_in_lists = {
            list_.id: {
                "time": timedelta(minutes=0)
            }
            for list_ in lists
        }

        ordered_list_movements = sorted(card.list_movements(),
                                        key=itemgetter("datetime"))

        if len(ordered_list_movements) == 0:

            time_in_lists[card.list_id]['time'] += self.filter_reports_time(
                start_date=card.created_date,
                end_date=self.unify_time(datetime.now()),
                disable_filter=disable_filter)  #!!!!!!!

        elif len(ordered_list_movements) == 1:
            time_start = card.created_date
            time_end = self.unify_time(ordered_list_movements[0]['datetime'])
            list_id = ordered_list_movements[0]['source']['id']

            time_in_lists[list_id]['time'] += self.filter_reports_time(
                start_date=time_start,
                end_date=time_end,
                disable_filter=disable_filter)

            time_start = self.unify_time(ordered_list_movements[0]['datetime'])
            time_end = self.unify_time(datetime.now())
            list_id = ordered_list_movements[0]['destination']['id']

            time_in_lists[list_id]['time'] += self.filter_reports_time(
                start_date=time_start,
                end_date=time_end,
                disable_filter=disable_filter)

        else:

            for change_index in range(0, len(ordered_list_movements)):
                list_id = ordered_list_movements[change_index]['source']['id']

                if change_index == 0:

                    time_in_lists[list_id]['time'] += self.filter_reports_time(
                        start_date=card.created_date,
                        end_date=self.unify_time(
                            ordered_list_movements[change_index]['datetime']),
                        disable_filter=disable_filter)

                elif change_index > 0:

                    time_start = ordered_list_movements[change_index -
                                                        1]['datetime']
                    time_end = ordered_list_movements[change_index]['datetime']

                    time_in_lists[list_id]['time'] += self.filter_reports_time(
                        start_date=self.unify_time(time_start),
                        end_date=self.unify_time(time_end),
                        disable_filter=disable_filter)

                    if change_index + 1 == len(ordered_list_movements):

                        time_start = ordered_list_movements[change_index][
                            'datetime']
                        time_end = datetime.now()

                        list_id = ordered_list_movements[change_index][
                            'destination']['id']
                        time_in_lists[list_id][
                            'time'] += self.filter_reports_time(
                                start_date=self.unify_time(time_start),
                                end_date=self.unify_time(time_end),
                                disable_filter=disable_filter)

        return time_in_lists

    def get_project_report(self, board_id, lists, members):
        self.db.drop_table('report')
        for list_id in lists:
            for member_id in members:
                query_result = self.local_cards_has_persons.search(
                    (where('board_id') == str(board_id))
                    & (where('person_id') == str(member_id)))

                for result in query_result:
                    try:
                        card = self.trello_client.get_card(
                            card_id=result['card_id'])
                        print(
                            f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [MSG]: got card {result["card_name"], result["card_id"]}'
                        )
                    except Exception as err:
                        print(
                            f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: [ERROR]: {err}'
                        )
                    else:
                        card_lists_time = self.get_card_stats_by_lists(
                            card=card)

                        for time in card_lists_time:
                            if time == list_id:
                                key = f'{time}'
                                if card_lists_time.get(
                                        key)['time'] > timedelta(minutes=1):
                                    list_name_query = self.local_lists.get(
                                        (where('list_id') == str(time)))

                                    self.report.insert({
                                        'person_id':
                                        result['person_id'],
                                        'person_name':
                                        result['person_name'],
                                        'card_id':
                                        result['card_id'],
                                        'card_name':
                                        result['card_name'],
                                        'list_id':
                                        time,
                                        'list_name':
                                        list_name_query['list_name'],
                                        'list_time':
                                        str(card_lists_time.get(key)['time']),
                                        'board_id':
                                        result['board_id'],
                                        'board_name':
                                        result['board_name']
                                    })

    def convert_seconds_to_readable_time(self, seconds):
        min, sec = divmod(seconds, 60)
        hour, min = divmod(min, 60)
        return "%d:%02d:%02d" % (hour, min, sec)
예제 #23
0
class ServiceTrello(ServicesMgr):

    # Boards own Lists own Cards

    def __init__(self, token=None):
        # app name
        self.app_name = DjangoThConfig.verbose_name
        # expiration
        self.expiry = "30days"
        # scope define the rights access
        self.scope = 'read,write'

        base = 'https://www.trello.com'
        self.AUTH_URL = '{}/1/OAuthAuthorizeToken'.format(base)
        self.REQ_TOKEN = '{}/1/OAuthGetRequestToken'.format(base)
        self.ACC_TOKEN = '{}/1/OAuthGetAccessToken'.format(base)
        self.consumer_key = settings.TH_TRELLO['consumer_key']
        self.consumer_secret = settings.TH_TRELLO['consumer_secret']
        if token:
            token_key, token_secret = token.split('#TH#')
            self.trello_instance = TrelloClient(self.consumer_key,
                                                self.consumer_secret,
                                                token_key, token_secret)

    def read_data(self, token, trigger_id, date_triggered):
        """
            get the data from the service
            as the pocket service does not have any date
            in its API linked to the note,
            add the triggered date to the dict data
            thus the service will be triggered when data will be found
            :param trigger_id: trigger ID to process
            :param date_triggered: the date of the last trigger
            :type trigger_id: int
            :type date_triggered: datetime
            :return: list of data found from the date_triggered filter
            :rtype: list
        """
        data = list()
        cache.set('th_trello_' + str(trigger_id), data)

    def process_data(self, trigger_id):
        """
            get the data from the cache
            :param trigger_id: trigger ID from which to save data
            :type trigger_id: int
        """
        cache_data = cache.get('th_trello_' + str(trigger_id))
        return PublishingLimit.get_data('th_trello_', cache_data, trigger_id)

    def save_data(self, token, trigger_id, **data):
        """
            let's save the data

            :param trigger_id: trigger ID from which to save data
            :param **data: the data to check to be used and save
            :type trigger_id: int
            :type **data:  dict
            :return: the status of the save statement
            :rtype: boolean
        """
        from th_trello.models import Trello

        title = ''
        content = ''
        status = False

        title = self.set_card_title(data)
        content = self.set_card_content(data)

        if len(title):
            # get the data of this trigger
            t = Trello.objects.get(trigger_id=trigger_id)

            # 1 - we need to search the list and board where we will
            # store the card so ...

            # 1.a search the board_id by its name
            # by retreiving all the boards
            boards = self.trello_instance.list_boards()

            board_id = ''
            my_board = ''
            my_list = ''
            for board in boards:
                if t.board_name == board.name.decode('utf-8'):
                    board_id = board.id
                    break

            if board_id:
                # 1.b search the list_id by its name
                my_board = self.trello_instance.get_board(board_id)
                lists = my_board.open_lists()
                # just get the open list ; not all the archive ones
                for list_in_board in lists:
                    # search the name of the list we set in the form
                    if t.list_name == list_in_board.name.decode('utf-8'):
                        # return the (trello) list object
                        # to be able to add card at step 3
                        my_list = my_board.get_list(list_in_board.id)
                        break
                # we didnt find the list in that board
                # create it
                if my_list == '':
                    my_list = my_board.add_list(t.list_name)

            else:
                # 2 if board_id and/or list_id does not exist, create it/them
                my_board = self.trello_instance.add_board(t.board_name)
                # add the list that didnt exists and
                # return a (trello) list object
                my_list = my_board.add_list(t.list_name)

            # 3 create the card
            # create the Trello card
            my_list.add_card(title, content)

            sentance = str('trello {} created').format(data['link'])
            logger.debug(sentance)
            status = True
        else:
            sentance = "no token or link provided for trigger ID {}"
            logger.critical(sentance.format(trigger_id))
            status = False

        return status

    def set_card_title(self, data):
        """
            handle the title from the data
        """
        title = ''
        # if no title provided, fallback to the URL which should be provided
        # by any exiting service
        title = (data['title'] if 'title' in data else data['link'])
        return title

    def set_card_content(self, data):
        """
            handle the content from the data
        """
        content = ''
        if 'content' in data:
            if type(data['content']) is list or type(data['content']) is tuple\
               or type(data['content']) is dict:
                if 'value' in data['content'][0]:
                    content = data['content'][0].value
            else:
                if type(data['content']) is str:
                    content = data['content']
                else:
                    # if not str or list or tuple
                    # or dict it could be feedparser.FeedParserDict
                    # so get the item value
                    content = data['content']['value']

        elif 'summary_detail' in data:
            if type(data['summary_detail']) is list or\
               type(data['summary_detail']) is tuple or\
               type(data['summary_detail']) is dict:
                if 'value' in data['summary_detail'][0]:
                    content = data['summary_detail'][0].value
            else:
                if type(data['summary_detail']) is str:
                    content = data['summary_detail']
                else:
                    # if not str or list or tuple
                    # or dict it could be feedparser.FeedParserDict
                    # so get the item value
                    content = data['summary_detail']['value']

        elif 'description' in data:
            content = data['description']

        return content

    def auth(self, request):
        """
            let's auth the user to the Service
        """
        callback_url = 'http://%s%s' % (request.get_host(),
                                        reverse('trello_callback'))

        request_token = self.get_request_token()

        # Save the request token information for later
        request.session['oauth_token'] = request_token['oauth_token']
        request.session['oauth_token_secret'] = request_token[
            'oauth_token_secret']

        # URL to redirect user to, to authorize your app
        auth_url_str = '{auth_url}?oauth_token={token}&scope={scope}&name={name}'
        auth_url_str += '&expiration={expiry}&oauth_callback={callback_url}'
        auth_url = auth_url_str.format(auth_url=self.AUTH_URL,
                                       token=request_token['oauth_token'],
                                       scope=self.scope,
                                       name=self.app_name,
                                       expiry=self.expiry,
                                       callback_url=callback_url)

        return auth_url

    def callback(self, request):
        """
            Called from the Service when the user accept to activate it
        """

        try:
            # finally we save the user auth token
            # As we already stored the object ServicesActivated
            # from the UserServiceCreateView now we update the same
            # object to the database so :
            # 1) we get the previous objet
            us = UserService.objects.get(
                user=request.user,
                name=ServicesActivated.objects.get(name='ServiceTrello'))
            # 2) Trello API require to use 4 parms consumer_key/secret +
            # token_key/secret instead of usually get just the token
            # from an access_token request. So we need to add a string
            # seperator for later use to slpit on this one
            access_token = self.get_access_token(
                request.session['oauth_token'],
                request.session['oauth_token_secret'],
                request.GET.get('oauth_verifier', ''))
            us.token = access_token.get('oauth_token') + \
                '#TH#' + access_token.get('oauth_token_secret')
            # 3) and save everything
            us.save()
        except KeyError:
            return '/'

        return 'trello/callback.html'

    def get_request_token(self):
        oauth = OAuth1Session(self.consumer_key,
                              client_secret=self.consumer_secret)
        return oauth.fetch_request_token(self.REQ_TOKEN)

    def get_access_token(self, oauth_token, oauth_token_secret,
                         oauth_verifier):
        # Using OAuth1Session
        oauth = OAuth1Session(self.consumer_key,
                              client_secret=self.consumer_secret,
                              resource_owner_key=oauth_token,
                              resource_owner_secret=oauth_token_secret,
                              verifier=oauth_verifier)
        oauth_tokens = oauth.fetch_access_token(self.ACC_TOKEN)

        return oauth_tokens
예제 #24
0
class TrelloBoard(object):
    def __init__(self, api_key, token):
        """Creates a TrelloBoard object

        :param api_key: (str) Your Trello api key https://trello.com/1/appKey/generate
        :param token:  (str) Your Trello token
        """
        self.tc = TrelloClient(api_key=api_key, token=token)

    @property
    def boards(self) -> List[Board]:
        """All the boards that can be accessed

        :return: (Board) list of Board
        """
        return self.tc.list_boards()

    @lru_cache(maxsize=128)
    def _org_id(self, team_name: str) -> str:
        """Get the id of a Trello team

        :param team_name:
        :return:
        """
        orgs = self.tc.list_organizations()
        for org in orgs:
            if org.name == team_name:
                return org.id

    @lru_cache(maxsize=128)
    def _board(self, board_name):
        logger.debug("Looking up board {}".format(board_name))
        board = [b for b in self.boards if b.name == board_name]
        try:
            return board[0]
        except IndexError as e:
            raise NoBoardError from e

    @lru_cache(maxsize=128)
    def _board_by_url(self, board_url):
        board = [b for b in self.boards if b.url == board_url]
        if board:
            return board[0]

    @lru_cache(maxsize=128)
    def _member(self, member_id: int, board_name: str) -> Optional[Card]:
        member_id = str(member_id)
        try:
            board = self._board(board_name)
        except NoBoardError:
            return

        for l in board.list_lists(list_filter="open"):
            for card in l.list_cards():
                sleep(0.1)
                if card.desc == member_id:
                    return card

    @lru_cache(maxsize=128)
    def _label(self, label_name, board_name):
        board = self._board(board_name)
        label = [l for l in board.get_labels() if l.name == label_name]
        if label:
            return label[0]

    def participants(self, board_name):
        board = self._board(board_name)
        members = []
        for l in board.list_lists(list_filter="open"):
            for card in l.list_cards():
                sleep(0.1)
                try:
                    members.append(int(card.desc))
                except ValueError:
                    pass
        return members

    def create_board(self, board_name, team_name=None):
        logger.debug("Checking for board {} on {} team".format(
            board_name, team_name))
        template = self._board("Meetup Template")
        org_id = self._org_id(team_name=team_name)
        try:
            self._board(board_name)
        except NoBoardError:
            self.tc.add_board(board_name=board_name,
                              source_board=template,
                              organization_id=org_id,
                              permission_level="public")

    def add_rsvp(self, name, member_id, board_name):
        logger.debug("Adding rsvp {} to {}".format(name, board_name))
        try:
            board = self._board(board_name)
        except NoBoardError:
            logger.debug("Board {} not found".format(board_name))
            return

        if not self._member(member_id, board_name):
            logger.debug("Member {} does not exist in {}. Adding them.".format(
                member_id, board_name))
            rsvp_list = board.list_lists(list_filter="open")[0]
            logger.debug("RSVP list for {}: {}".format(board_name, rsvp_list))
            rsvp_list.add_card(name=name, desc=str(member_id))

    def cancel_rsvp(self, member_id, board_name):
        logger.debug("Canceling RSVP for members id {} at {}".format(
            member_id, board_name))
        member_card = self._member(member_id, board_name)
        logger.debug("Card for member id {} is {}".format(
            member_id, member_card))
        canceled = self._label("Canceled", board_name)
        logger.debug("Canceled tag is {}".format(canceled))
        if member_card:
            member_card.add_label(canceled)

    def tables_for_event(self, event_name: str) -> Dict[int, GameTable]:
        tables = {}
        info_card = None
        board = self._board(event_name)

        for board_list in board.list_lists(list_filter="open"):
            if board_list.name.startswith("RSVP"):
                title = "Without a table :disappointed:"
                table_number = 9999
            else:
                table_number, title = board_list.name.split(". ", maxsplit=1)
                table_number = int(table_number)

            table = GameTable(number=table_number, title=title)

            for card in board_list.list_cards():
                sleep(0.1)
                if card.name == "Info":
                    info_card = card
                elif card.labels:
                    for label in card.labels:
                        if label.name == "GM":
                            table.gm = card.name
                else:
                    table.add_player(card.name)

            if info_card:
                full_info = info_card.desc.split("Players: ", 1)
                table.blurb = full_info[0]
                if len(full_info) == 2:
                    try:
                        table.max_players = int(full_info[1])
                    except ValueError:
                        pass

            tables[table_number] = table
        return OrderedDict(sorted(tables.items()))

    def table(self, board_name: str, table_number: int) -> GameTable:
        return self.tables_for_event(board_name)[table_number]

    def add_table(self, title, info, board_url):
        board = self._board_by_url(board_url)
        table_numbers = [
            int(n.name.split(".", 1)[0])
            for n in board.list_lists(list_filter="open")
            if n.name[0].isnumeric()
        ]
        ordinal = max(table_numbers) + 1 if table_numbers else 1
        title = "{}. {}".format(ordinal, title)
        table = board.add_list(name=title, pos="bottom")
        info = "\n\nPlayers:".join(info.split("Players:"))
        table.add_card("Info", desc=info)
        return "Table *{}* added to *{}*".format(title, board.name)