Redmine tickets and expired tickets in both python-redmine and REST

List of Redmine tickets and expired tickets Code to reduce with DataFrame.

The test code is still being written.

It is used in the flow of dataframe-> Mardown table-> Chat input.

Markdown table, chat input library I have already made it.


#!/opt/anaconda3/bin/python3
# -*- coding: utf-8 -*-

"""Manage Redmine ticket status

Obtain ticket information and analyze the situation

  Todo:
     *Only Redmine and RocketChat yet. Make the same for other OSS

"""

################################################
# library
################################################

import datetime
import redminelib
import requests
import pandas as pd
import sys

from pprint import pprint
from redminelib import Redmine

#Individual library Load

################################################
#Unique exception definition
################################################
#class MaxRetryError(Exception):
#    pass
#

################################################
#Information definition
################################################
#
## Redmine Instance
#HOST = "http://xxxxxxxxxxxxxxxxxxxxxxxxxx:3100/"
#API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx47ac"
#
##Extraction target
#PROJECT_ID = 'wbs-kaisui'
#PROJECT_NAME = 'Individual WBS (development promotion)'
#TRACKER_NAME ='things to do'
#
##Dataframe columns definition
#COLUMNS = ['Project','Tracker','Sprint','Parent ticket ID','Ticket ID','TicketTitle','In charge', 'Status','start date','Deadline']
#
##Search conditions
#QUERY = 'start date<= @TODAY and deadline< @TODAY'
#
##Sort condition
#SORT_KEY = ['In charge','Deadline']
#
################################################
# RedmineTicketManager 
################################################

class RedmineTicketManager():
    """Redmine Ticket Management Class

    Attributes:
        API_KEY (int)   :Redmine admin API key
        HOST (bool)     :Redmine host

    """
    
    def __init__(self, HOST, API_KEY):
        """Redmine instance generation

        API_KEY,Returns a Redmine instance from HOST

        Args:
            HOST (str):      Redmine Host 
            API_KEY (str):API according to admin_KEY

        Returns:

        Raises:
            TypeError:Defective argument type
            Exception:Insufficient Redmine instance generation

        Examples:
            >>> redmine = RedmineUserManager(HOST, API_KEY) 

        Note:
The Redmine group must exist in advance.
Even if a new group is listed, it will not be created automatically on Redmine.
            __init__Please note that you must not return bool

        """ 

        #Argument check type
        if not isinstance(HOST, str):
            print(f'Arguments: HOST type is incorrect str<-> {type(HOST)}')
            raise TypeError

        if not isinstance(API_KEY, str):
            print(f'Arguments: API_KEY type is incorrect str<-> {type(API_KEY)}')
            raise TypeError

        #Added TODO argument check
            
        #Redmine instance generation (self.redmineļ¼‰
        try:
            self.redmine = Redmine(HOST, key=API_KEY)
        except exception as e: 
            print(f'Redmine Instance generation failed')
            print(f'Error details:{e}')
        else:
            if not isinstance(self.redmine, redminelib.Redmine):
                print(f'Redmine Instance generation failed')
                raise Exception

    ########################################################    
    #Expired ticket search process
    ########################################################
    def createOverdueTicket(self, PROJECT_ID, TRACKER_NAME):
        '''Search for expired tickets

Search for overdue tickets by specifying the project and tracker.

        Args:
            PROJECT_ID :str Redmine project ID (not project name)
            TRACKER_NAME:str Redmine Search target tracker name

        Returns:
All tickets that hit the project, tracker limit: DataFrame
Hit project, tracker conditions, and overdue tickets: DataFrame

        Raises:
            TypeError:Defective argument type
            Exception:Exceptions when searching for tickets

        Examples:
            >>> RTM = RedmineTicketManager(HOST, API_KEY)
            >>> df, df_overdue = RTM.createOverdueTicket(PROJECT_ID, TRACKER_NAME)

        Note:

        '''
        #Dataframe columns definition
        COLUMNS = ['Project','Tracker','Sprint','Parent ticket ID','Ticket ID','TicketTitle','In charge', 'Status','start date','Deadline']
       
        #Search conditions
        QUERY = 'start date<= @TODAY and deadline< @TODAY'
        
        #Sort condition
        SORT_KEY = ['In charge','Deadline']

        #Date generation today
        TODAY = datetime.date.today()
        pprint(f'Judgment reference date: {TODAY}')
        INIT_DATE = datetime.date(1900, 1, 1)
        pprint(f'The first day: {INIT_DATE}')
        print('-'*80)

        print(f'Explore Redmine tickets')

        try:
            #Get all tickets with administrator privileges
            tickets=self.redmine.issue.filter(
                project_id=PROJECT_ID,
                tracker_name=TRACKER_NAME,
            )

        except Exception as e:
            print(f'Ticket acquisition failed:{PROJECT_ID} {TRACKER_NAME}')
            print(f'Error details:{e}')
            print()
        else:
            #Ticket delay judgment processing, data creation

            #Prepare a temporary container
            _list = []

            #Ticket molding process
            for ticket in tickets:
                #Get information from ticket instance
                projectName = ticket.project.name
                id = ticket.id
                subject = ticket.subject
                authorName = ticket.author.name
                authorID = ticket.author.id
                status = ticket.status.name
                tracker = ticket.tracker.name
                fixed_version = ticket.fixed_version.name
                
                #If the person in charge is not set, it is displayed as not set
                try:
                    assigned_to = ticket.assigned_to.name
                except:
                    assigned_to = "Not set"
            
                #If the start date is not set, set the start date
                try:
                    startDate = ticket.start_date
                except:
                    startDate = INIT_DATE 
            
                #If no due date is set, set the start date
                try:
                    dueDate = ticket.due_date
                except:
                    dueDate = INIT_DATE 
                    
                #Set to 0 if parent ticket is not set
                try:
                    parent = ticket.parent
                except:
                    parent = 0 
            
                #Accumulation
                _list.append([projectName, tracker, fixed_version, parent, id, subject, assigned_to, status, startDate, dueDate])
            

            print(f'Ticket extraction is complete')

            #DataFrame conversion
            df = pd.DataFrame(_list)
            df.columns = COLUMNS

            #Delayed ticket extraction
            #For those whose start date is before today and whose deadline is overdue today
            df_overdue = df.query(QUERY).sort_values(SORT_KEY).reset_index(drop=True)

            print(f'Extracted overdue Redmine tickets')
            print(f'Number of overdue tickets: {len(df_overdue)}')

            return df ,df_overdue


    ########################################################    
    #Expired ticket search process by REST
    ########################################################
    def createOverdueTicketByREST(self, RESTAPI, HEADERS, PROJECT_ID, TRACKER_ID):
        '''Search for expired tickets By REST

Search for overdue tickets by specifying the project and tracker.
        python_Get information directly using REST without using redmine.

        Args:
            PROJECT_ID :str Redmine project ID (not project name)
            TRACKER_ID:str Redmine Search target tracker ID (not tracker name)

        Returns:
All tickets that hit the project, tracker limit: DataFrame
Hit project, tracker conditions, and overdue tickets: DataFrame

        Raises:
            TypeError:Defective argument type
            Exception:Exceptions when searching for tickets

        Examples:
            >>> RTM = RedmineTicketManagerByREST(HOST, API_KEY)
            >>> df, df_overdue = RTM.createOverdueTicketByREST(PROJECT_ID, TRACKER_ID)

        Note:
Whether it's a NAME or an ID, it feels awkward to use properly. .. ..
        '''
        #request parameter generation
        #URL = f'http://192.168.10.104:3100' 
        #API = f'{URL}/projects/{PROJECT_ID}/issues.json?tracker_id={TRACKER_ID}'
        #HEADERS = { 'Content-Type': 'application/json', 'X-Redmine-API-Key': '864b3f0933e8084295d47380bf07a168ba2947ac'}

        #Date generation today
        TODAY = datetime.date.today()
        pprint(f'Judgment reference date: {TODAY}')

        #Dataframe columns definition
        COLUMNS = ['Project','Tracker','Sprint','Parent ticket ID','Ticket ID','TicketTitle','In charge', 'Status','start date','Deadline']
       
        #Search conditions
        QUERY = 'start date<= @TODAY and deadline< @TODAY'
        
        #Sort condition
        SORT_KEY = ['In charge','Deadline']

        #Search processing execution
        ##Prepare a container
        _list = []

        #Search implementation
        try:
            response = requests.get(RESTAPI, 
                                    headers=HEADERS)
        except Exception as e:
            print(f'Redmine ticket search failed') 
            print(f'{e}')
        else:
            #Result processing
            for _ in response.json()['issues']:
                
                #In some cases, the parent and the person in charge do not exist as data
                #Must be judged by try ~ catch
                try:
                    parent = _['parent']['id']
                except:
                    parent = 0
                    
                try:
                    assigned_to = _['assigned_to']['name']
                except:
                    assigned_to = "None"
            
                #There is a start and a deadline.
                #None is included if there is no setting
                if _['start_date'] != None:
                    startDate = _['start_date']
                else:
                    startDate = '1900-01-01'
                    
                if _['due_date'] != None:
                    dueDate = _['due_date']
                else:
                    dueDate = '1900-01-01'
                
                #Data accumulation
                _list.append([_['project']['name'], 
                              _['tracker']['name'],
                              _['fixed_version']['name'],
                              parent,
                              _['id'],
                              _['description'],
                              assigned_to,
                              _['status']['name'],
                              startDate,
                              dueDate,
                             ])

        #Convert to DataFrame and perform extraction process
        df = pd.DataFrame(_list)
        df.columns = COLUMNS
        df['start date'] = pd.to_datetime(df['start date'])
        df['Deadline'] = pd.to_datetime(df['Deadline']) 

        #Delayed ticket extraction
        #For those whose start date is before today and whose deadline is overdue today
        df_overdue = df.query(QUERY).sort_values(SORT_KEY).reset_index(drop=True)

        print(f'Extracted overdue Redmine tickets')
        print(f'Number of overdue tickets: {len(df_overdue)}')

        return df ,df_overdue

Recommended Posts

Redmine tickets and expired tickets in both python-redmine and REST