Exemple #1
0
    def __init__(self, cli=None, caching=True, baseurl="https://api.github.com/repos"):

        self.cli = cli #cement cli object
        self.datadict = {}
        self.baseurl = baseurl
        self.fetched = []
        self.cache_age = None
        self.cache_max_age = int(self.cli.config.get('github', 'cache_max_age'))
        self.repo_admins = ['mpdehaan', 'jctanner', 'jimi-c']
        self.lastrun = None

        #import epdb; epdb.st()
        if self.cli.pargs.repo is not None:
            self.repo = self.cli.pargs.repo
        else:
            self.repo = self.cli.config.get('github', 'repo')

        if self.cli.pargs.username is not None:
            self.username = self.cli.pargs.username
        else:
            self.username = self.cli.config.get('github', 'username')

        if self.cli.pargs.password is not None:
            self.password = self.cli.pargs.password
        else:
            self.password = self.cli.config.get('github', 'password')

        self.openedurl = baseurl + "/" + self.repo + "/issues?state=open"
        self.closedurl = baseurl + "/" + self.repo + "/issues?state=closed"

        self.homedir = os.path.expanduser("~")
        #import epdb; epdb.st()
        self.cachedir = os.path.join(self.homedir, ".cache", "github", self.repo.replace("/", "_"))
        self.cachefile = os.path.join(self.cachedir, "requests_cache")
        if not os.path.isdir(self.cachedir):
            os.makedirs(self.cachedir)

        #import epdb; epdb.st()
        # requests caching magic
        #import requests_cache
        #if not self.cli.pargs.no_cache:
        if caching:
            """
            if os.path.isfile(self.cachefile + ".sqlite"):
                st = os.stat(self.cachefile + ".sqlite")
                self.cache_age = time.time() - st.st_mtime
                print "# CACHE-AGE: ",self.cache_age
                if self.cache_age > self.cache_max_age:
                    os.remove(self.cachefile + ".sqlite")
            """                    
            #import requests_cache
            #requests_cache.install_cache(self.cachefile)
            pass
        else:
            #if os.path.isfile(self.cachefile):
            #    os.remove(self.cachefile)
            #requests_cache.install_cache(self.cachefile)
            pass
            
        self.resty = Resty(username=self.username, password=self.password)
Exemple #2
0
    def __init__(self, cli=None, baseurl="https://api.github.com/repos"):

        self.cli = cli #cement cli object
        self.datadict = {}
        self.baseurl = baseurl
        self.fetched = []
        self.cache_age = None
        try:
            self.cache_max_age = int(self.cli.config.get('github', 'cache_max_age'))
        except:
            self.cache_max_age = 300
        self.repo_admins = ['mpdehaan', 'jctanner', 'jimi-c']

        ## REPO
        if self.cli.pargs.repo is not None:
            self.repo = self.cli.pargs.repo
        else:
            self.repo = self.cli.config.get('github', 'repo')

        ## USERNAME
        if self.cli.pargs.username is not None:
            self.username = self.cli.pargs.username
        else:
            try:
                self.username = self.cli.config.get('github', 'username')
            except:
                self.username = None

        ## PASSWORD
        if self.cli.pargs.password is not None:
            self.password = self.cli.pargs.password
        else:
            try:
                self.password = self.cli.config.get('github', 'password')
            except:
                self.password = None

        ## TOKEN
        if self.cli.pargs.token is not None:
            self.token = self.cli.pargs.token
        else:
            try:
                self.token = self.cli.config.get('github', 'token')
            except:
                self.token = None

        self.openedurl = baseurl + "/" + self.repo + "/issues?state=open"
        self.closedurl = baseurl + "/" + self.repo + "/issues?state=closed"

        self.homedir = os.path.expanduser("~")
        self.cachedir = os.path.join(self.homedir, ".cache", "github", self.repo.replace("/", "_"))
        self.cachefile = os.path.join(self.cachedir, "requests_cache")
        if not os.path.isdir(self.cachedir):
            os.makedirs(self.cachedir)

        #if not self.cli.pargs.no_cache:
        #    requests_cache.install_cache(self.cachefile)
        #else:
        #    pass
        requests_cache.install_cache(self.cachefile)
            
        self.resty = Resty(username=self.username, password=self.password, token=self.token)
Exemple #3
0
class GithubIssues(object):
    def __init__(self, cli=None, baseurl="https://api.github.com/repos"):

        self.cli = cli #cement cli object
        self.datadict = {}
        self.baseurl = baseurl
        self.fetched = []
        self.cache_age = None
        try:
            self.cache_max_age = int(self.cli.config.get('github', 'cache_max_age'))
        except:
            self.cache_max_age = 300
        self.repo_admins = ['mpdehaan', 'jctanner', 'jimi-c']

        ## REPO
        if self.cli.pargs.repo is not None:
            self.repo = self.cli.pargs.repo
        else:
            self.repo = self.cli.config.get('github', 'repo')

        ## USERNAME
        if self.cli.pargs.username is not None:
            self.username = self.cli.pargs.username
        else:
            try:
                self.username = self.cli.config.get('github', 'username')
            except:
                self.username = None

        ## PASSWORD
        if self.cli.pargs.password is not None:
            self.password = self.cli.pargs.password
        else:
            try:
                self.password = self.cli.config.get('github', 'password')
            except:
                self.password = None

        ## TOKEN
        if self.cli.pargs.token is not None:
            self.token = self.cli.pargs.token
        else:
            try:
                self.token = self.cli.config.get('github', 'token')
            except:
                self.token = None

        self.openedurl = baseurl + "/" + self.repo + "/issues?state=open"
        self.closedurl = baseurl + "/" + self.repo + "/issues?state=closed"

        self.homedir = os.path.expanduser("~")
        self.cachedir = os.path.join(self.homedir, ".cache", "github", self.repo.replace("/", "_"))
        self.cachefile = os.path.join(self.cachedir, "requests_cache")
        if not os.path.isdir(self.cachedir):
            os.makedirs(self.cachedir)

        #if not self.cli.pargs.no_cache:
        #    requests_cache.install_cache(self.cachefile)
        #else:
        #    pass
        requests_cache.install_cache(self.cachefile)
            
        self.resty = Resty(username=self.username, password=self.password, token=self.token)

    def _get_one_issue(self, k):
        url = self.openedurl = self.baseurl + "/" + self.repo + "/issues/" + k
        #i = self.get_one_page(url, usecache=False)                
        i = self.resty.get_one_page(url, usecache=False)
        data = json.loads(i.content)
        return data

    def get_all(self, closed=True):

        os.environ['TZ'] = "BST"
        time.tzset()

        # load pickled cache
        datadict = self._get_datadict_cache()

        # figure out what needs to be updated
        last_closed = None
        for k in sorted(datadict.keys()):
            if 'closed_at' in datadict[k]:
                if datadict[k]['closed_at']:
                    if not last_closed:
                        last_closed = datadict[k]['closed_at']
                        last_closed = datetime.strptime(last_closed, "%Y-%m-%dT%H:%M:%SZ")
                    else:
                        if datadict[k].get('closed_at', None):
                            this_time = datetime.strptime(datadict[k]['closed_at'], "%Y-%m-%dT%H:%M:%SZ")
                            if this_time > last_closed:
                                last_closed = this_time


        new_combined = {}
        if not last_closed and datadict == {}:
            # fetch all history
            opendict = self.get_open()
            closeddict = self.get_closed()
            datadict =  dict(opendict.items() + closeddict.items())

        else:

            # create dict for updated issues
            new_open = self.get_new(closed=False, lasttime=last_closed)
            new_closed = self.get_new(closed=True, lasttime=last_closed)
            new_combined = dict(new_open.items() + new_closed.items())

            for k in sorted(new_combined.keys()):
                #k = str(k)
                datadict[str(k)] = new_combined[k]
                # kill the pygithub obj for this issue
                this_file = os.path.join(self.cachedir, k + ".pygithub")
                if os.path.isfile(this_file):
                    os.remove(this_file)

        # test for complete list
        sorted_keys = sorted([int(x) for x in datadict.keys()])
        #sorted_keys = [str(x) for x in sorted_keys]
        for kx in range(sorted_keys[0], sorted_keys[-1]):
            kx = str(kx)
            if kx not in datadict:
                #import epdb; epdb.st()
                datadict[kx] = self._get_one_issue(kx)
                self._pickle_single_datadict_cache((kx, datadict[kx]))
                #import epdb; epdb.st()
            if 'documentation_url' in datadict[kx]:
                datadict[kx] = self._get_one_issue(kx)
                self._pickle_single_datadict_cache((kx, datadict[kx]))
            if 'message' in datadict[kx]:
                if datadict[kx]['message'] == 'Not Found':
                    del datadict[kx]

        # simple processing
        datadict = self._set_dict_keys_to_string(datadict)
        datadict = self._get_types(datadict)
        datadict = self._get_ages(datadict)
        datadict = self._get_usernames(datadict)
        datadict = self._get_labels(datadict)
        datadict = self._set_dict_keys_to_string(datadict)

        # get events+comments and save incrementally
        for k in sorted([int(x) for x in datadict.keys()]):
            k = str(k)
            try:
                if not 'events' in datadict[str(k)] or str(k) in new_combined.keys():
                    datadict[k]['events'] = self.get_events(datadict[k]['events_url'])
            except Exception, e:
                dict[k]['events'] = None
                #import epdb; epdb.st()
            if not 'comments' in datadict[str(k)] or str(k) in new_combined.keys():    
                datadict[k]['comments']  = self.get_comments(datadict[k]['comments_url'])
            self._pickle_single_datadict_cache((k, datadict[k]))

        # closure stats for each issue
        datadict = self.get_closure_info(datadict)

        # fetch the pygithub data
        datadict = self.load_pygithub_objects(datadict)
        datadict = self._get_closed_by(datadict)

        # save everything
        self._put_datadict_cache(datadict)
        self.datadict = datadict

        #import epdb; epdb.st()
        # kill closed if defined
        if not closed:
            for k in sorted([int(x) for x in self.datadict.keys()]):
                #import epdb; epdb.st()
                if self.datadict[str(k)]['state'] == 'closed':
                    self.datadict.pop(str(k), None)
Exemple #4
0
class GithubIssues(object):
    def __init__(self, cli=None, caching=True, baseurl="https://api.github.com/repos"):

        self.cli = cli #cement cli object
        self.datadict = {}
        self.baseurl = baseurl
        self.fetched = []
        self.cache_age = None
        self.cache_max_age = int(self.cli.config.get('github', 'cache_max_age'))
        self.repo_admins = ['mpdehaan', 'jctanner', 'jimi-c']
        self.lastrun = None

        #import epdb; epdb.st()
        if self.cli.pargs.repo is not None:
            self.repo = self.cli.pargs.repo
        else:
            self.repo = self.cli.config.get('github', 'repo')

        if self.cli.pargs.username is not None:
            self.username = self.cli.pargs.username
        else:
            self.username = self.cli.config.get('github', 'username')

        if self.cli.pargs.password is not None:
            self.password = self.cli.pargs.password
        else:
            self.password = self.cli.config.get('github', 'password')

        self.openedurl = baseurl + "/" + self.repo + "/issues?state=open"
        self.closedurl = baseurl + "/" + self.repo + "/issues?state=closed"

        self.homedir = os.path.expanduser("~")
        #import epdb; epdb.st()
        self.cachedir = os.path.join(self.homedir, ".cache", "github", self.repo.replace("/", "_"))
        self.cachefile = os.path.join(self.cachedir, "requests_cache")
        if not os.path.isdir(self.cachedir):
            os.makedirs(self.cachedir)

        #import epdb; epdb.st()
        # requests caching magic
        #import requests_cache
        #if not self.cli.pargs.no_cache:
        if caching:
            """
            if os.path.isfile(self.cachefile + ".sqlite"):
                st = os.stat(self.cachefile + ".sqlite")
                self.cache_age = time.time() - st.st_mtime
                print "# CACHE-AGE: ",self.cache_age
                if self.cache_age > self.cache_max_age:
                    os.remove(self.cachefile + ".sqlite")
            """                    
            #import requests_cache
            #requests_cache.install_cache(self.cachefile)
            pass
        else:
            #if os.path.isfile(self.cachefile):
            #    os.remove(self.cachefile)
            #requests_cache.install_cache(self.cachefile)
            pass
            
        self.resty = Resty(username=self.username, password=self.password)


    def get_open(self):
        # QUICK LOAD
        self.openeddata = self.get_all_pages(self.openedurl)
        self.datadict = self.data_to_dict(self.openeddata)


    def get_closed(self):
        # SAFE LOAD
        thisurl, urlset, pages = self.resty.get_all_urls(self.closedurl)
        self._pages_to_dict(pages)

    def get_new(self):

        #print "GET_NEW"

        os.environ['TZ'] = "BST"
        time.tzset()
        ts = time.strftime("%Y-%m-%dT%H:%M:%SZ")            
        if self.lastrun == None:
            self.lastrun = ts
            newurl = self.baseurl + "/" + self.repo + "/issues?state=open"
        else:
            newurl = self.baseurl + "/" + self.repo + "/issues?state=open;since=%s" % self.lastrun

        self.datadict = self.resty.data_to_dict(newurl, key="number")
        self.lastrun = ts


    def __get_new(self):
        """
        since   string  
        Only issues updated at or after this time are returned. 
        This is a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ.
        """
        # https://api.github.com/repos/ansible/ansible/issues?state=open;since=2014-01-30T00:00:00Z        

        # * find newest "last updated" from datadict?
        # * find new updates since cache last modified?

        #from datetime import date, timedelta
        #ys = date.today() - timedelta(1)

        ts = time.strftime("%Y-%m-%dT00:00:00Z")
        if os.path.isfile(self.cachefile + ".sqlite"):
            # match the github api timezone
            os.environ['TZ'] = "BST"
            time.tzset()

            # get the exact date of last cache modification
            st = os.stat(self.cachefile + ".sqlite")
            x = time.localtime(st.st_mtime)

            # make a suitable string for filtering updated tickets
            #ts = time.strftime("%Y-%m-%dT%H:%M:%SZ", x)            
            ts = time.strftime("%Y-%m-%dT%H:%M:00Z", x)            


        #import epdb; epdb.st()
        #if ts is None:
        #    self.get_open()
        #else:
        newurl = self.baseurl + "/" + self.repo + "/issues?state=open;since=%s" % ts
        #newurl = self.baseurl + "/" + self.repo + "/issues?since=%s" % ts
        thisurl = None
        urlset = None
        pages = None
        thisurl, urlset, pages = self._get_all_urls(newurl, urls=[], usecache=False)
        print "new pages: %s" % len(pages)
        self._pages_to_dict(pages)

        #print "GOT NEW"
        #import epdb; epdb.st()

    ##########################
    # PAGINATION
    ##########################

    def _pages_to_dict(self, pages):            
        #import epdb; epdb.st()
        print "RESET DATADICT"
        self.datadict = {}
        for gp in pages:
            #import epdb; epdb.st()
            thisdata = None
            try:
                thisdata = json.loads(gp.text or gp.content)
            except ValueError:
                epdb.st()
                sys.exit(1)

            print "_pages_to_dict:",gp.url
            #pprint(thisdata)
            for t in thisdata:
                #print t['number'] 
                try:
                    self.datadict[t['number']] = t
                except TypeError:
                    epdb.st()

    ##########################
    # PROCESSING
    ##########################

    def _get_types(self):
        for x in self.datadict.keys():
            if self.datadict[x]['pull_request']['html_url'] is not None:
                self.datadict[x]['type'] = 'pull_request'
            else:
                self.datadict[x]['type'] = 'issue'

    def _get_ages(self):
        for x in self.datadict.keys():
            # 2013-01-02T22:14:22Z

            start = self.datadict[x]['created_at'] 
            start = eval(self._safe_string(start))
            start = datetime.strptime(start, "%Y-%m-%dT%H:%M:%SZ")

            end = self.datadict[x]['closed_at']
            end = eval(self._safe_string(end))
            if end is not 'None':
                end = datetime.strptime(end, "%Y-%m-%dT%H:%M:%SZ")
            else:
                #print end
                #import epdb; epdb.st()
                end = datetime.now()

            age = end - start
            age = age.days
            if age > 1000:
                epdb.st()

            self.datadict[x]['age'] = age
            #epdb.st()

    def _get_usernames(self):
        for x in self.datadict.keys():
            #epdb.st()
            thisuser = None
            if 'user' in self.datadict[x]:
                if 'login' in self.datadict[x]['user']:
                    thisuser = self.datadict[x]['user']['login']
            if thisuser is None:
                self.datadict[x]['user'] = "******"
            else:
                self.datadict[x]['user'] = thisuser

    def _get_labels(self):
        for x in self.datadict.keys():
            #if x == 277:
            #    epdb.st()
            if 'labels_url' in self.datadict[x]:
                del self.datadict[x]['labels_url']
            if 'labels' in self.datadict[x]:
                labels = []
                if len(self.datadict[x]['labels']) > 0:
                    for l in self.datadict[x]['labels']:
                        labels.append(l['name'])
                    self.datadict[x]['labels'] = str(labels)
                    #epdb.st()
            else:
                self.datadict[x]['labels'] = str([])


    ##########################
    # OUTPUTS
    ##########################

    # FIXME
    def display(self, sort_by=None):
        self.show_pr_sorted(sort_by=sort_by)

    #FIXME
    def showkeys(self):
        keys = []
        for k1 in self.datadict.keys():
            for k2 in self.datadict[k1].keys():
                keys.append(k2)           
        keys = sorted(set(keys))

        for k in keys:
            print k

    # GOOD
    def show_all(self):
        #import epdb; epdb.st()
        self.get_open()
        self.get_pull_request_patches()
        self.get_pull_request_commits()
        self._print_datadict()

    # GOOD
    def show_closed(self):
        #pass
        self.get_closed()
        self._get_types()
        self._get_ages()
        self._get_usernames()
        self._get_labels()
        self.get_pull_request_patches()
        self.get_pull_request_commits()
        self.get_events()
        self.get_closure_info()
        self.get_comments()
        #self.merged_or_not()
        self._print_datadict()

    # GOOD
    def _print_datadict(self):
        columns = ['number', 'created_at', 'closed_at', 'title']
        sorted_x = sorted(set(self.datadict.keys()))
        for x in sorted_x:
            for k in self.datadict[x].keys():
                if k not in columns:
                    if k != 'body':
                        columns.append(str(k))

        header = ""
        for col in columns:
            header += col + ";"
        print header

        for x in sorted_x:
            outline = ""
            for col in columns:
                if col not in self.datadict[x]:
                    self.datadict[x][col] = None

                outline += self._safe_string(self.datadict[x][col]) + ";"

            try:             
                print outline
            except UnicodeEncodeError:
                #epdb.st()
                pass

            """
            if x == 16:
                epdb.st()
            """


    def _safe_string(self, data):

        if type(data) is unicode:
            try:
                data = data.encode('ascii')
            except UnicodeEncodeError:
                data = "UNICODE"

        if type(data) == int:
            data = str(data)
        if type(data) == list:
            if len(data) > 0:
                data = data[0]
            data = "%s" % str(data)                

        if type(data) == str:
            if '\n' in data:
                data = str(data.split('\n')[0])

        if type(data) == dict:
            #epdb.st()
            data = "DICT"
   
        if type(data) == bool:
            data = str(data)
    
        if data is None:
            data = "None"

        if type(data) != str:
            epdb.st()

        if ':' in data and '{' in data and len(data) > 100:
            #epdb.st()
            data = "DICT"

        if data.startswith("https://"):
            pass

        data = data.replace('"', '')
        data = data.replace("'", "")

        return "\"" + data + "\""
        

    # FIXME: REFACTOR
    def show_data(self, datadict):
        for k in sorted(datadict.keys()):
            print str(k) + " - " + datadict[k]['title']
        print "\n## %s issues total ##\n" % len(sorted(datadict.keys()))

    # FIXME: REFACTOR
    def show_pr_sorted(self, sort_by=None):
        x = {}

        for k in self.datadict.keys():
            if sort_by in self.datadict[k]:
                x[k] = self.datadict[k][sort_by]

        if sort_by is None:
            sorted_x = sorted(set(self.datadict.keys()))
            print "\n#ISSUE;TITLE\n"
            for x in sorted_x:
                print "%s;\"%s\"" % (x, self.datadict[x]['title'])

        else:
            sorted_x = sorted(x.iteritems(), key=operator.itemgetter(1))
            print "\n#ISSUE;%s;TITLE\n" % sort_by
            for x in sorted_x:
                number, sort_val = x
                print "%s;%s;\"%s\"" % (number, sort_val, self.datadict[number]['title'])

    def show_pr_by_users(self):
        self.get_open()
        users = {}
        for k in self.datadict.keys():
            #import epdb; epdb.st()
            thisuser = self.datadict[k]['user']['login']
            pr = False
            if self.datadict[k]['pull_request']['diff_url'] is not None:
                pr = True

            if pr:
                if thisuser not in users:
                    users[thisuser] =[]
                if k not in users[thisuser]:
                    users[thisuser].append(k)
                
        #import epdb; epdb.st()

        if not self.cli.pargs.html:
            #import epdb; epdb.st()
            for k in sorted(users, key=lambda k: len(users[k]), reverse=True):
                print len(users[k]),":",k
                for x in users[k]:
                    try:
                        print "\t",x,self.datadict[x]['title']
                    except UnicodeEncodeError:
                        print "\t",x," non-ascii title"
                    #import epdb; epdb.st()

        else:
            pass
            #self._pr_file_age_to_html(files)
            self._pr_users_to_html(users)

    def show_pr_by_file(self):
        self.get_open()
        self.get_pull_request_patches()
        self.get_pull_request_commits()

        files = {}

        for k in self.datadict.keys():
            #print k
            if 'patch_files_filenames' not in self.datadict[k]:
                continue
            kfiles = self.datadict[k]['patch_files_filenames']
            tmpfiles = []

            # remove a/
            for x in kfiles:
                xtmp = list(x)
                xtmp[0] = ''
                xtmp[1] = ''
                xnew = "".join(x)
                tmpfiles.append(xnew)

            # add this PR# to the files list
            for x in tmpfiles:
                if x not in files:
                    files[x] = []
                if k not in files[x]:
                    files[x].append(k)

        if not self.cli.pargs.html:
            #import epdb; epdb.st()
            for k in sorted(files, key=lambda k: len(files[k]), reverse=True):
                print len(files[k]),":",k
                for x in files[k]:
                    try:
                        print "\t",x,self.datadict[x]['title']
                    except UnicodeEncodeError:
                        print "\t",x," non-ascii title"
                    #import epdb; epdb.st()

        else:
            self._pr_file_age_to_html(files)

    def _pr_file_age_to_html(self, files):
        print "<html>"
        print "<head>"
        print "<title>PRs for files</title>"
        print """<style>
        #outer {
            margin: 0 ;
            background-color:white; /*just to display the example*/
        }

        #inner {
            /*or move the whole container 50px to the right side*/
            margin-left:50px; 
            margin-right:-50px;
        }
    </style>"""
        print "</head>"
        print "<body>"

        for k in sorted(files, key=lambda k: len(files[k]), reverse=True):
            print '<div id="outer">\n<div id="outer">%s : %s</div>\n' % (len(files[k]), k) 
            for x in sorted(files[k]):
                #import epdb; epdb.st()
                thisurl = self.datadict[x]['html_url']
                thisid = '<a href="%s">%s</a>' %(thisurl, x)
                try:
                    print '<div id="inner">%s : %s</div>\n' % (thisid, self.datadict[x]['title'])
                except UnicodeEncodeError:
                    print '<div id="inner">%s : %s</div>\n' % (thisid, "UNICODE")
            print '</div>\n' 


        print "</body>"
        print "</html>"

    def _pr_users_to_html(self, users):
        print "<html>"
        print "<head>"
        print "<title>PRs for users</title>"
        print """<style>
        #outer {
            margin: 0 ;
            background-color:white; /*just to display the example*/
        }

        #inner {
            /*or move the whole container 50px to the right side*/
            margin-left:50px; 
            margin-right:-50px;
        }
    </style>"""
        print "</head>"
        print "<body>"

        for k in sorted(users, key=lambda k: len(users[k]), reverse=True):
            print '<div id="outer">\n<div id="outer">%s : %s</div>\n' % (len(users[k]), k) 
            for x in sorted(users[k]):
                #import epdb; epdb.st()
                thisurl = self.datadict[x]['html_url']
                thisid = '<a href="%s">%s</a>' %(thisurl, x)
                try:
                    print '<div id="inner">%s : %s</div>\n' % (thisid, self.datadict[x]['title'])
                except UnicodeEncodeError:
                    print '<div id="inner">%s : %s</div>\n' % (thisid, "UNICODE")
            print '</div>\n' 


        print "</body>"
        print "</html>"



    def show_pr_merge_commits(self):
        self.get_open()
        self.get_pull_request_patches()
        self.get_pull_request_commits()

         
        if not self.cli.pargs.html:
            sorted_x = sorted(set(self.datadict.keys()))
            for x in sorted_x:
                if 'pr_commit_merge_count' in self.datadict[x]:
                    if self.datadict[x]['pr_commit_merge_count'] > 0:
                        print x,self.datadict[x]['pr_commit_merge_count'],self.datadict[x]['title']
        else:
            self._merge_commit_to_html()

    def _merge_commit_to_html(self):
        print "<html>"
        print "<head>"
        print "<title>PRs with merge commits</title>"
        print """<style>
        #outer {
            margin: 0 ;
            background-color:white; /*just to display the example*/
        }

        #inner {
            /*or move the whole container 50px to the right side*/
            margin-left:50px; 
            margin-right:-50px;
        }
    </style>"""
        print "</head>"
        print "<body>"

        sorted_x = sorted(set(self.datadict.keys()))
        for x in sorted_x:
            if 'pr_commit_merge_count' in self.datadict[x]:
                if self.datadict[x]['pr_commit_merge_count'] > 0:

                    thisurl = self.datadict[x]['html_url']
                    thisid = '<a href="%s">%s</a>' %(thisurl, x)
                    thiscount = self.datadict[x]['pr_commit_merge_count']

                    try:
                        print '<div id="outer">%s : %s : %s</div>\n' % (thisid, thiscount, self.datadict[x]['title'])
                    except UnicodeEncodeError:
                        print '<div id="outer">%s : %s : %s</div>\n' % (thisid, thiscount, "UNICODE")


        print "</body>"
        print "</html>"

    # "stray" commits
    def show_pr_commit_count(self):

        """
        Hi, this ticket now has a lot of stray commits in it.
        Please make sure you start a new branch for each new 
        submission you would like to make, for instance a branch 
        like "postgres_alter_role" and submit only from those new 
        branches, and use "git pull --rebase" vs "git pull" or "git rebase" 
        vs "git merge" to avoid merge commits.
        Can you please close and resubmit this one without the stray commits?    
        """
    
        self.get_open()
        self.get_pull_request_patches()
        self.get_pull_request_commits()


        commit_counts = {}
         
        for x in [ x for x in self.datadict.keys() if 'pr_commit_count' in self.datadict[x] ]:
            if self.datadict[x]['pr_commit_count'] > 1:
                #pprint(self.datadict[x])
                #import epdb; epdb.st()
                #print x,self.datadict[x]['pr_commit_count'],self.datadict[x]['title']
                commit_counts[x] = self.datadict[x]['pr_commit_count']

        sorted_x = sorted(commit_counts.iteritems(), key=operator.itemgetter(1))             
        sorted_x.reverse()
        for x in sorted_x:
            k, v = x
            try:
                thistitle = str(self.datadict[k]['title'])
            except UnicodeEncodeError:    
                thistitle = "UNICODE ERROR"

            print k,v,thistitle


    ##########################
    # PATCH ENUMERATION
    ##########################

    def get_pull_request_commits(self):
        # http://developer.github.com/v3/pulls/
        # https://api.github.com/repos/ansible/ansible/pulls/2476/commits
        #import epdb; epdb.st()
        for x in self.datadict.keys():
            if self.datadict[x]['pull_request']['patch_url'] is not None:
                self.datadict[x]['pr_commit_merge_count'] = 0
                self.datadict[x]['pr_commit_count'] = 0
                commits_url = self.baseurl + "/" + self.repo + "/pulls/" + str(x) + "/commits"
                y = self.resty.get_one_page(commits_url)
                #import epdb; epdb.st()
                if y.ok:
                    self.datadict[x]['pull_commits'] = json.loads(y.content)

                    for pc in self.datadict[x]['pull_commits']:
                        self.datadict[x]['pr_commit_count'] += 1
                        #import epdb; epdb.st()
                        if pc['commit']['message'].startswith('Merge branch'):
                            self.datadict[x]['pr_commit_merge_count'] += 1

    def get_pull_request_patches(self):
        for k in self.datadict.keys():
            #epdb.st()
            i = self.datadict[k]['number']
            pr = self.datadict[k]['pull_request']
            
            if pr['patch_url'] is not None:

                patchfile = os.path.join(self.cachedir, "%s.patch" % k)

                if not os.path.isfile(patchfile):
                    patch_page = self.resty.get_one_page(pr['patch_url'])
                    self.datadict[k]['patch_text'] = patch_page.text
                else:
                    patch_text = open(patchfile).read()
                    self.datadict[k]['patch_text'] = patch_text

                # generate synthetic meta            
                patch_meta = self.parse_patch(self.datadict[k]['patch_text'])
                for pk in patch_meta.keys():
                    self.datadict[k][pk] = patch_meta[pk]
                    
                try:
                    open(patchfile, "wb").write(self.datadict[k]['patch_text'])
                except UnicodeEncodeError:
                    pass
                except:
                    import epdb; epdb.st()

    def parse_patch(self, rawtext):
        rdict = {}

        patch_lines = rawtext.split('\n')
        rdict['patch_length'] = len(patch_lines)
        rdict['patch_files_filenames'] = []
        rdict['patch_files_new'] = 0
        rdict['patch_has_tests'] = False

        for line in patch_lines:
            if 'files changed' in line \
                and 'insertions' in line \
                and 'deletions' in line:

                sections = line.split(',')
                for section in sections:
                    section = section.replace('(+)','')
                    section = section.replace('(-)','')

                    if 'files changed' in section:
                        rdict['patch_files_changed'] = shlex.split(section)[0]

                    if 'insertions' in section:
                        rdict['patch_insertions'] = shlex.split(section)[0]

            if line.startswith('new file mode'):
                rdict['patch_files_new'] += 1
            if line.startswith('diff --git'):
                tmpline = line.replace('diff --git ', '')
                thisfilename = shlex.split(tmpline)[0]
                rdict['patch_files_filenames'].append(thisfilename)
                if thisfilename.startswith('a/test/'):
                    rdict['patch_has_tests'] = True

        return rdict

                         
    ##########################
    # EVENTS ENUMERATION
    ##########################

    def get_events(self):
        for k in self.datadict.keys():
            if 'events_url' in self.datadict[k]:
                i = self.get_one_page(self.datadict[k]['events_url'])
                idict = json.loads(i.content)
                self.datadict[k]['events'] = idict
                del self.datadict[k]['events_url']

    def get_closure_info(self):
        eventtypes = ['assigned', 'referenced', 'closed', 'subscribed', 'merged']
        found = []

        for k in self.datadict.keys():
            if 'events' in self.datadict[k]:

                self.datadict[k]['merged'] = False
                self.datadict[k]['closure_count'] = 0
                self.datadict[k]['reopen_count'] = 0

                for ev in self.datadict[k]['events']:

                    if ev['event'] not in found:
                        found.append(ev['event'])

                    if ev['event'] == 'merged':
                        self.datadict[k]['merged'] = True
                        self.datadict[k]['merged_by'] = ev['actor']['login']

                    if ev['event'] == 'closed':
                        self.datadict[k]['closed'] = True
                        self.datadict[k]['closure_count'] += 1
                        try:
                            if 'actor' in ev:
                                if ev['actor'] is not None:
                                    if 'login' in ev['actor']:
                                        self.datadict[k]['closed_by'] = ev['actor']['login']
                            else:
                                self.datadict[k]['closed_by'] = None
                        except TypeError:
                            print k
                            epdb.st()

                    if ev['event'] == 'reopened':
                        self.datadict[k]['reopened'] = True
                        self.datadict[k]['reopen_count'] += 1
                        self.datadict[k]['reopened_by'] = ev['actor']['login']

                    #if ev['event'] not in eventtypes:                    
                    #    epdb.st()
        #epdb.st()
        return None
            


    ##########################
    # COMMENTS ENUMERATION
    ##########################

    def get_comments(self, usecache=True):
        for k in self.datadict.keys():
            if 'comments_url' in self.datadict[k]:

                """
                if not usecache:
                    if requests_cache.get_cache().has_url(self.datadict[k]['comments_url']):
                        requests_cache.get_cache().delete_url(self.datadict[k]['comments_url'])
                """

                i = self.resty.get_one_page(self.datadict[k]['comments_url'])
                idict = json.loads(i.content)
                self.datadict[k]['comments'] = idict
                #import epdb; epdb.st()

        self.get_closure_comment()

    def get_closure_comment(self):

        for k in self.datadict.keys():
            # created_at == timestamp for this comment
            # self.datadict[k]['events'][0]['created_at'] 

            #if self.datadict[k]['type'] == 'pull_request' and self.datadict[k]['state'] == 'closed':
            if self.datadict[k]['state'] == 'closed':

                # result
                closure_comment_ids = []
                closure_comment_texts = []
                closure_comment_objs = []

                closed_times = []

                for ev in self.datadict[k]['events']:
                    if ev['event'] == 'closed':
                        thisdate = ev['created_at']
                        thisdate = datetime.strptime(thisdate, "%Y-%m-%dT%H:%M:%SZ")
                        closed_times.append(thisdate)

                for closed_time in closed_times:
                    first_comment_id = None
                    first_comment_obj = None
                    first_comment_author = None
                    first_comment_date = None
                    exact_match = False

                    for com in self.datadict[k]['comments']:
                        if com['user']['login'] in self.repo_admins:

                            thisid = com['id']
                            this_author = com['user']['login']
                            thisdate = com['created_at']
                            thisdate = datetime.strptime(thisdate, "%Y-%m-%dT%H:%M:%SZ")

                            #epdb.st()
                            #if thisdate == closed_time and k == '875':
                            if thisdate == closed_time:
                                #print "DEBUG"
                                #epdb.st()
                                exact_match = True
                                first_comment_date =  thisdate
                                first_comment_obj = com
                                frist_comment_author = this_author
                                first_comment_id = thisid

                            if thisdate > closed_time and not exact_match:
                                #epdb.st()
                                if first_comment_date is None or thisdate < first_comment_date:
                                    first_comment_date =  thisdate
                                    first_comment_obj = com
                                    frist_comment_author = this_author
                                    first_comment_id = thisid

                    if first_comment_id is not None and first_comment_id not in closure_comment_ids:
                        closure_comment_ids.append(first_comment_id)
                        closure_comment_objs.append(first_comment_obj)
                        try:
                            closure_comment_texts.append(first_comment_obj['body'])
                        except:
                            epdb.st()

                # more than one closure comment? What now?
                if len(closure_comment_ids) > 0 \
                    and self.datadict[k]['type'] == 'pull_request' \
                    and not self.datadict[k]['merged'] \
                    and self.datadict[k]['closed_by'] in self.repo_admins:

                    #pass
                    #epdb.st()
                    for t in closure_comment_texts:
                        tmpstring = t.replace('\n', '')
                        open("/tmp/reasons.txt", "a").write("##########################\n")
                        try:
                            open("/tmp/reasons.txt", "a").write("%s:: %s\n" % (k, tmpstring))
                        except UnicodeEncodeError:
                            open("/tmp/reasons.txt", "a").write("%s:: UNICODEERROR\n" % k)
                        open("/tmp/reasons.txt", "a").write("##########################\n")
                        #if '?' in t:
                        #    epdb.st()

    ##########################
    # POST(s)
    ##########################

    def add_comment(self, issue_id, body):
        """ Create a new comment on an issue """

        """
        http://developer.github.com/v3/issues/comments/#create-a-comment

        POST /repos/:owner/:repo/issues/:number/comments

        {
          "body": "a new comment"
        }

        http://stackoverflow.com/questions/9733638/post-json-using-python-request

        url = "http://localhost:8080"
        data = {'sender': 'Alice', 'receiver': 'Bob', 'message': 'We did it!'}
        headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
        r = requests.post(url, data=json.dumps(data), headers=headers)        
        """
        #import epdb; epdb.st()

        url = self.baseurl + "/" + self.repo + "/issues/%s/comments" % issue_id
        data = {'body': body}
        headers = {'Content-type': 'application/json', 'Accept': 'application/json'}
        i = requests.post(  url, 
                            data=json.dumps(data), 
                            headers=headers, 
                            auth=(self.username, self.password))
        
        #import epdb; epdb.st()
        return i.ok

    def delete_comment(self, comment_id):
        """ Remove a comment """

        # http://developer.github.com/v3/issues/comments/#delete-a-comment
        # DELETE /repos/:owner/:repo/issues/comments/:id

        #import epdb; epdb.st()
        url = self.baseurl + "/" + self.repo + "/issues/comments/%s" % comment_id
        i = requests.delete(url, auth=(self.username, self.password))

        return i.ok        


    def set_description(self, k, template_text):
        """
        # http://developer.github.com/v3/issues/#edit-an-issue
        {
          "title": "Found a bug",
          "body": "I'm having a problem with this.",
          "assignee": "octocat",
          "milestone": 1,
          "state": "open",
          "labels": [
            "Label1",
            "Label2"
          ]
        } 
        PATCH /repos/:owner/:repo/issues/:number
        """

        data = { "title": self.datadict[k]['title'],
                 "body": template_text,
                 "assignee": self.datadict[k]['assignee'],
                 "milestone": self.datadict[k]['milestone'],
                 "state": self.datadict[k]['state'],
                 "labels": self.datadict[k]['labels']
               }

        url = self.baseurl + "/" + self.repo + "/issues/%s" % k                  
        headers = {'Content-type': 'application/json', 'Accept': 'application/json'}
        i = requests.patch(  url, 
                            data=json.dumps(data), 
                            headers=headers, 
                            auth=(self.username, self.password))

        return i.ok
Exemple #5
0
    def setUp(self):
        self.Resty1 = Resty()
        self.code = mock.Mock(return_value=200)
        self.testUrls = [
            'https://httpstat.us/100', 'https://httpstat.us/101',
            'https://httpstat.us/102', 'https://httpstat.us/103',
            'https://httpstat.us/200', 'https://httpstat.us/201',
            'https://httpstat.us/202', 'https://httpstat.us/203',
            'https://httpstat.us/204', 'https://httpstat.us/205',
            'https://httpstat.us/206', 'https://httpstat.us/300',
            'https://httpstat.us/301', 'https://httpstat.us/302',
            'https://httpstat.us/303', 'https://httpstat.us/304',
            'https://httpstat.us/305', 'https://httpstat.us/306',
            'https://httpstat.us/307', 'https://httpstat.us/308',
            'https://httpstat.us/400', 'https://httpstat.us/401',
            'https://httpstat.us/402', 'https://httpstat.us/403',
            'https://httpstat.us/404', 'https://httpstat.us/405',
            'https://httpstat.us/406', 'https://httpstat.us/407',
            'https://httpstat.us/408', 'https://httpstat.us/409',
            'https://httpstat.us/410', 'https://httpstat.us/411',
            'https://httpstat.us/412', 'https://httpstat.us/413',
            'https://httpstat.us/414', 'https://httpstat.us/415',
            'https://httpstat.us/416', 'https://httpstat.us/417',
            'https://httpstat.us/418', 'https://httpstat.us/421',
            'https://httpstat.us/422', 'https://httpstat.us/423',
            'https://httpstat.us/425', 'https://httpstat.us/426',
            'https://httpstat.us/428', 'https://httpstat.us/429',
            'https://httpstat.us/431', 'https://httpstat.us/451',
            'https://httpstat.us/500', 'https://httpstat.us/501',
            'https://httpstat.us/502', 'https://httpstat.us/503',
            'https://httpstat.us/504', 'https://httpstat.us/505',
            'https://httpstat.us/506', 'https://httpstat.us/507',
            'https://httpstat.us/511', 'https://httpstat.us/520',
            'https://httpstat.us/522', 'https://httpstat.us/524'
        ]
        self.urls_source = dedent("""\
            https://httpstat.us/100\n\
            https://httpstat.us/101\n\
            https://httpstat.us/102\n\
            https://httpstat.us/103\n\
            https://httpstat.us/200\n\
            https://httpstat.us/201\n\
            https://httpstat.us/202\n\
            https://httpstat.us/203\n\
            https://httpstat.us/204\n\
            https://httpstat.us/205\n\
            https://httpstat.us/206\n\
            https://httpstat.us/300\n\
            https://httpstat.us/301\n\
            https://httpstat.us/302\n\
            https://httpstat.us/303\n\
            https://httpstat.us/304\n\
            https://httpstat.us/305\n\
            https://httpstat.us/306\n\
            https://httpstat.us/307\n\
            https://httpstat.us/308\n\
            https://httpstat.us/400\n\
            https://httpstat.us/401\n\
            https://httpstat.us/402\n\
            https://httpstat.us/403\n\
            https://httpstat.us/404\n\
            https://httpstat.us/405\n\
            https://httpstat.us/406\n\
            https://httpstat.us/407\n\
            https://httpstat.us/408\n\
            https://httpstat.us/409\n\
            https://httpstat.us/410\n\
            https://httpstat.us/411\n\
            https://httpstat.us/412\n\
            https://httpstat.us/413\n\
            https://httpstat.us/414\n\
            https://httpstat.us/415\n\
            https://httpstat.us/416\n\
            https://httpstat.us/417\n\
            https://httpstat.us/418\n\
            https://httpstat.us/421\n\
            https://httpstat.us/422\n\
            https://httpstat.us/423\n\
            https://httpstat.us/425\n\
            https://httpstat.us/426\n\
            https://httpstat.us/428\n\
            https://httpstat.us/429\n\
            https://httpstat.us/431\n\
            https://httpstat.us/451\n\
            https://httpstat.us/500\n\
            https://httpstat.us/501\n\
            https://httpstat.us/502\n\
            https://httpstat.us/503\n\
            https://httpstat.us/504\n\
            https://httpstat.us/505\n\
            https://httpstat.us/506\n\
            https://httpstat.us/507\n\
            https://httpstat.us/511\n\
            https://httpstat.us/520\n\
            https://httpstat.us/522\n\
            https://httpstat.us/524\n""")

        self.codes = [
            100, 101, 102, 103, 200, 201, 202, 203, 204, 205, 206, 300, 301,
            302, 303, 304, 305, 306, 307, 308, 400, 401, 402, 403, 404, 405,
            406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418,
            421, 422, 423, 425, 426, 428, 429, 431, 451, 500, 501, 502, 503,
            504, 505, 506, 507, 511, 520, 522, 524
        ]

        self.messages = [
            'Continue', 'Switching Protocols', 'Processing', 'Early Hints',
            'OK', 'Created', 'Accepted', 'Non-Authoritative Information',
            'No Content', 'Reset Content', 'Partial Content',
            'Multiple Choices', 'Moved Permanently', 'Found', 'See Other',
            'Not Modified', 'Use Proxy', 'Unused', 'Temporary Redirect',
            'Permanent Redirect', 'Bad Request', 'Unauthorized',
            'Payment Required', 'Forbidden', 'Not Found', 'Method Not Allowed',
            'Not Acceptable', 'Proxy Authentication Required',
            'Request Timeout', 'Conflict', 'Gone', 'Length Required',
            'Precondition Failed', 'Request Entity Too Large',
            'Request-URI Too Long', 'Unsupported Media Type',
            'Requested Range Not Satisfiable', 'Expectation Failed',
            'I\'m a teapot', 'Misdirected Request', 'Unprocessable Entity',
            'Locked', 'Too Early', 'Upgrade Required', 'Precondition Required',
            'Too Many Requests', 'Request Header Fields Too Large',
            'Unavailable For Legal Reasons', 'Internal Server Error',
            'Not Implemented', 'Bad Gateway', 'Service Unavailable',
            'Gateway Timeout', 'HTTP Version Not Supported',
            'Variant Also Negotiates', 'Insufficient Storage',
            'Network Authentication Required',
            'Web server is returning an unknown error', 'Connection timed out',
            'A timeout occurred'
        ]

        self.msgList = [
            [100, 'Continue', 'https://httpstat.us/100'],
            [101, 'Switching Protocols', 'https://httpstat.us/101'],
            [102, 'Processing', 'https://httpstat.us/102'],
            [103, 'Early Hints', 'https://httpstat.us/103'],
            [200, 'OK', 'https://httpstat.us/200'],
            [201, 'Created', 'https://httpstat.us/201'],
            [202, 'Accepted', 'https://httpstat.us/202'],
            [203, 'Non-Authoritative Information', 'https://httpstat.us/203'],
            [204, 'No Content', 'https://httpstat.us/204'],
            [205, 'Reset Content', 'https://httpstat.us/205'],
            [206, 'Partial Content', 'https://httpstat.us/206'],
            [300, 'Multiple Choices', 'https://httpstat.us/300'],
            [301, 'Moved Permanently', 'https://httpstat.us/301'],
            [302, 'Found', 'https://httpstat.us/302'],
            [303, 'See Other', 'https://httpstat.us/303'],
            [304, 'Not Modified', 'https://httpstat.us/304'],
            [305, 'Use Proxy', 'https://httpstat.us/305'],
            [306, 'Unused', 'https://httpstat.us/306'],
            [307, 'Temporary Redirect', 'https://httpstat.us/307'],
            [308, 'Permanent Redirect', 'https://httpstat.us/308'],
            [400, 'Bad Request', 'https://httpstat.us/400'],
            [401, 'Unauthorized', 'https://httpstat.us/401'],
            [402, 'Payment Required', 'https://httpstat.us/402'],
            [403, 'Forbidden', 'https://httpstat.us/403'],
            [404, 'Not Found', 'https://httpstat.us/404'],
            [405, 'Method Not Allowed', 'https://httpstat.us/405'],
            [406, 'Not Acceptable', 'https://httpstat.us/406'],
            [407, 'Proxy Authentication Required', 'https://httpstat.us/407'],
            [408, 'Request Timeout', 'https://httpstat.us/408'],
            [409, 'Conflict', 'https://httpstat.us/409'],
            [410, 'Gone', 'https://httpstat.us/410'],
            [411, 'Length Required', 'https://httpstat.us/411'],
            [412, 'Precondition Failed', 'https://httpstat.us/412'],
            [413, 'Request Entity Too Large', 'https://httpstat.us/413'],
            [414, 'Request-URI Too Long', 'https://httpstat.us/414'],
            [415, 'Unsupported Media Type', 'https://httpstat.us/415'],
            [
                416, 'Requested Range Not Satisfiable',
                'https://httpstat.us/416'
            ], [417, 'Expectation Failed', 'https://httpstat.us/417'],
            [418, 'I\'m a teapot', 'https://httpstat.us/418'],
            [421, 'Misdirected Request', 'https://httpstat.us/421'],
            [422, 'Unprocessable Entity', 'https://httpstat.us/422'],
            [423, 'Locked', 'https://httpstat.us/423'],
            [425, 'Too Early', 'https://httpstat.us/425'],
            [426, 'Upgrade Required', 'https://httpstat.us/426'],
            [428, 'Precondition Required', 'https://httpstat.us/428'],
            [429, 'Too Many Requests', 'https://httpstat.us/429'],
            [
                431, 'Request Header Fields Too Large',
                'https://httpstat.us/431'
            ],
            [451, 'Unavailable For Legal Reasons', 'https://httpstat.us/451'],
            [500, 'Internal Server Error', 'https://httpstat.us/500'],
            [501, 'Not Implemented', 'https://httpstat.us/501'],
            [502, 'Bad Gateway', 'https://httpstat.us/502'],
            [503, 'Service Unavailable', 'https://httpstat.us/503'],
            [504, 'Gateway Timeout', 'https://httpstat.us/504'],
            [505, 'HTTP Version Not Supported', 'https://httpstat.us/505'],
            [506, 'Variant Also Negotiates', 'https://httpstat.us/506'],
            [507, 'Insufficient Storage', 'https://httpstat.us/507'],
            [
                511, 'Network Authentication Required',
                'https://httpstat.us/511'
            ],
            [
                520, 'Web server is returning an unknown error',
                'https://httpstat.us/520'
            ], [522, 'Connection timed out', 'https://httpstat.us/522'],
            [524, 'A timeout occurred', 'https://httpstat.us/524']
        ]
Exemple #6
0
class RestyTest(unittest.TestCase):
    def setUp(self):
        self.Resty1 = Resty()
        self.code = mock.Mock(return_value=200)
        self.testUrls = [
            'https://httpstat.us/100', 'https://httpstat.us/101',
            'https://httpstat.us/102', 'https://httpstat.us/103',
            'https://httpstat.us/200', 'https://httpstat.us/201',
            'https://httpstat.us/202', 'https://httpstat.us/203',
            'https://httpstat.us/204', 'https://httpstat.us/205',
            'https://httpstat.us/206', 'https://httpstat.us/300',
            'https://httpstat.us/301', 'https://httpstat.us/302',
            'https://httpstat.us/303', 'https://httpstat.us/304',
            'https://httpstat.us/305', 'https://httpstat.us/306',
            'https://httpstat.us/307', 'https://httpstat.us/308',
            'https://httpstat.us/400', 'https://httpstat.us/401',
            'https://httpstat.us/402', 'https://httpstat.us/403',
            'https://httpstat.us/404', 'https://httpstat.us/405',
            'https://httpstat.us/406', 'https://httpstat.us/407',
            'https://httpstat.us/408', 'https://httpstat.us/409',
            'https://httpstat.us/410', 'https://httpstat.us/411',
            'https://httpstat.us/412', 'https://httpstat.us/413',
            'https://httpstat.us/414', 'https://httpstat.us/415',
            'https://httpstat.us/416', 'https://httpstat.us/417',
            'https://httpstat.us/418', 'https://httpstat.us/421',
            'https://httpstat.us/422', 'https://httpstat.us/423',
            'https://httpstat.us/425', 'https://httpstat.us/426',
            'https://httpstat.us/428', 'https://httpstat.us/429',
            'https://httpstat.us/431', 'https://httpstat.us/451',
            'https://httpstat.us/500', 'https://httpstat.us/501',
            'https://httpstat.us/502', 'https://httpstat.us/503',
            'https://httpstat.us/504', 'https://httpstat.us/505',
            'https://httpstat.us/506', 'https://httpstat.us/507',
            'https://httpstat.us/511', 'https://httpstat.us/520',
            'https://httpstat.us/522', 'https://httpstat.us/524'
        ]
        self.urls_source = dedent("""\
            https://httpstat.us/100\n\
            https://httpstat.us/101\n\
            https://httpstat.us/102\n\
            https://httpstat.us/103\n\
            https://httpstat.us/200\n\
            https://httpstat.us/201\n\
            https://httpstat.us/202\n\
            https://httpstat.us/203\n\
            https://httpstat.us/204\n\
            https://httpstat.us/205\n\
            https://httpstat.us/206\n\
            https://httpstat.us/300\n\
            https://httpstat.us/301\n\
            https://httpstat.us/302\n\
            https://httpstat.us/303\n\
            https://httpstat.us/304\n\
            https://httpstat.us/305\n\
            https://httpstat.us/306\n\
            https://httpstat.us/307\n\
            https://httpstat.us/308\n\
            https://httpstat.us/400\n\
            https://httpstat.us/401\n\
            https://httpstat.us/402\n\
            https://httpstat.us/403\n\
            https://httpstat.us/404\n\
            https://httpstat.us/405\n\
            https://httpstat.us/406\n\
            https://httpstat.us/407\n\
            https://httpstat.us/408\n\
            https://httpstat.us/409\n\
            https://httpstat.us/410\n\
            https://httpstat.us/411\n\
            https://httpstat.us/412\n\
            https://httpstat.us/413\n\
            https://httpstat.us/414\n\
            https://httpstat.us/415\n\
            https://httpstat.us/416\n\
            https://httpstat.us/417\n\
            https://httpstat.us/418\n\
            https://httpstat.us/421\n\
            https://httpstat.us/422\n\
            https://httpstat.us/423\n\
            https://httpstat.us/425\n\
            https://httpstat.us/426\n\
            https://httpstat.us/428\n\
            https://httpstat.us/429\n\
            https://httpstat.us/431\n\
            https://httpstat.us/451\n\
            https://httpstat.us/500\n\
            https://httpstat.us/501\n\
            https://httpstat.us/502\n\
            https://httpstat.us/503\n\
            https://httpstat.us/504\n\
            https://httpstat.us/505\n\
            https://httpstat.us/506\n\
            https://httpstat.us/507\n\
            https://httpstat.us/511\n\
            https://httpstat.us/520\n\
            https://httpstat.us/522\n\
            https://httpstat.us/524\n""")

        self.codes = [
            100, 101, 102, 103, 200, 201, 202, 203, 204, 205, 206, 300, 301,
            302, 303, 304, 305, 306, 307, 308, 400, 401, 402, 403, 404, 405,
            406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418,
            421, 422, 423, 425, 426, 428, 429, 431, 451, 500, 501, 502, 503,
            504, 505, 506, 507, 511, 520, 522, 524
        ]

        self.messages = [
            'Continue', 'Switching Protocols', 'Processing', 'Early Hints',
            'OK', 'Created', 'Accepted', 'Non-Authoritative Information',
            'No Content', 'Reset Content', 'Partial Content',
            'Multiple Choices', 'Moved Permanently', 'Found', 'See Other',
            'Not Modified', 'Use Proxy', 'Unused', 'Temporary Redirect',
            'Permanent Redirect', 'Bad Request', 'Unauthorized',
            'Payment Required', 'Forbidden', 'Not Found', 'Method Not Allowed',
            'Not Acceptable', 'Proxy Authentication Required',
            'Request Timeout', 'Conflict', 'Gone', 'Length Required',
            'Precondition Failed', 'Request Entity Too Large',
            'Request-URI Too Long', 'Unsupported Media Type',
            'Requested Range Not Satisfiable', 'Expectation Failed',
            'I\'m a teapot', 'Misdirected Request', 'Unprocessable Entity',
            'Locked', 'Too Early', 'Upgrade Required', 'Precondition Required',
            'Too Many Requests', 'Request Header Fields Too Large',
            'Unavailable For Legal Reasons', 'Internal Server Error',
            'Not Implemented', 'Bad Gateway', 'Service Unavailable',
            'Gateway Timeout', 'HTTP Version Not Supported',
            'Variant Also Negotiates', 'Insufficient Storage',
            'Network Authentication Required',
            'Web server is returning an unknown error', 'Connection timed out',
            'A timeout occurred'
        ]

        self.msgList = [
            [100, 'Continue', 'https://httpstat.us/100'],
            [101, 'Switching Protocols', 'https://httpstat.us/101'],
            [102, 'Processing', 'https://httpstat.us/102'],
            [103, 'Early Hints', 'https://httpstat.us/103'],
            [200, 'OK', 'https://httpstat.us/200'],
            [201, 'Created', 'https://httpstat.us/201'],
            [202, 'Accepted', 'https://httpstat.us/202'],
            [203, 'Non-Authoritative Information', 'https://httpstat.us/203'],
            [204, 'No Content', 'https://httpstat.us/204'],
            [205, 'Reset Content', 'https://httpstat.us/205'],
            [206, 'Partial Content', 'https://httpstat.us/206'],
            [300, 'Multiple Choices', 'https://httpstat.us/300'],
            [301, 'Moved Permanently', 'https://httpstat.us/301'],
            [302, 'Found', 'https://httpstat.us/302'],
            [303, 'See Other', 'https://httpstat.us/303'],
            [304, 'Not Modified', 'https://httpstat.us/304'],
            [305, 'Use Proxy', 'https://httpstat.us/305'],
            [306, 'Unused', 'https://httpstat.us/306'],
            [307, 'Temporary Redirect', 'https://httpstat.us/307'],
            [308, 'Permanent Redirect', 'https://httpstat.us/308'],
            [400, 'Bad Request', 'https://httpstat.us/400'],
            [401, 'Unauthorized', 'https://httpstat.us/401'],
            [402, 'Payment Required', 'https://httpstat.us/402'],
            [403, 'Forbidden', 'https://httpstat.us/403'],
            [404, 'Not Found', 'https://httpstat.us/404'],
            [405, 'Method Not Allowed', 'https://httpstat.us/405'],
            [406, 'Not Acceptable', 'https://httpstat.us/406'],
            [407, 'Proxy Authentication Required', 'https://httpstat.us/407'],
            [408, 'Request Timeout', 'https://httpstat.us/408'],
            [409, 'Conflict', 'https://httpstat.us/409'],
            [410, 'Gone', 'https://httpstat.us/410'],
            [411, 'Length Required', 'https://httpstat.us/411'],
            [412, 'Precondition Failed', 'https://httpstat.us/412'],
            [413, 'Request Entity Too Large', 'https://httpstat.us/413'],
            [414, 'Request-URI Too Long', 'https://httpstat.us/414'],
            [415, 'Unsupported Media Type', 'https://httpstat.us/415'],
            [
                416, 'Requested Range Not Satisfiable',
                'https://httpstat.us/416'
            ], [417, 'Expectation Failed', 'https://httpstat.us/417'],
            [418, 'I\'m a teapot', 'https://httpstat.us/418'],
            [421, 'Misdirected Request', 'https://httpstat.us/421'],
            [422, 'Unprocessable Entity', 'https://httpstat.us/422'],
            [423, 'Locked', 'https://httpstat.us/423'],
            [425, 'Too Early', 'https://httpstat.us/425'],
            [426, 'Upgrade Required', 'https://httpstat.us/426'],
            [428, 'Precondition Required', 'https://httpstat.us/428'],
            [429, 'Too Many Requests', 'https://httpstat.us/429'],
            [
                431, 'Request Header Fields Too Large',
                'https://httpstat.us/431'
            ],
            [451, 'Unavailable For Legal Reasons', 'https://httpstat.us/451'],
            [500, 'Internal Server Error', 'https://httpstat.us/500'],
            [501, 'Not Implemented', 'https://httpstat.us/501'],
            [502, 'Bad Gateway', 'https://httpstat.us/502'],
            [503, 'Service Unavailable', 'https://httpstat.us/503'],
            [504, 'Gateway Timeout', 'https://httpstat.us/504'],
            [505, 'HTTP Version Not Supported', 'https://httpstat.us/505'],
            [506, 'Variant Also Negotiates', 'https://httpstat.us/506'],
            [507, 'Insufficient Storage', 'https://httpstat.us/507'],
            [
                511, 'Network Authentication Required',
                'https://httpstat.us/511'
            ],
            [
                520, 'Web server is returning an unknown error',
                'https://httpstat.us/520'
            ], [522, 'Connection timed out', 'https://httpstat.us/522'],
            [524, 'A timeout occurred', 'https://httpstat.us/524']
        ]

    """tests for get_status_codes"""

    def test_get_status_codes(self):
        """test msgList build"""
        self.Resty1._state['quiet'] = True
        codes = iter(self.codes)
        result_get_status_codes = self.Resty1.get_status_codes(
            self.testUrls, timeout=0.0002, iter_codes=codes)
        # assert result of tested method call
        self.assertEqual(result_get_status_codes, self.msgList, 'Not equal')

    """tests for get_1_code"""

    def test_get_1_code(self):
        """test single line message formatting"""
        result_get_1_code = self.Resty1.get_1_code('https://httpstat.us/200',
                                                   timeout=0.0002,
                                                   code=self.code)
        # assert result of tested method call
        self.assertEqual(result_get_1_code,
                         '200: OK - https://httpstat.us/200', 'Not equal')

    """tests for get_urls"""

    def test_get_urls(self):
        """test file parsing into per-line list"""
        with mock.patch('builtins.open',
                        mock.mock_open(read_data=self.urls_source)):
            with open('/dev/null') as f:
                # print('contents of file from the mock patch: ' + f.read())
                result_test_get_urls = self.Resty1.get_urls(f)
                # function returns a set that is sorted for testing
                self.assertEqual(sorted(result_test_get_urls), self.testUrls,
                                 'Not equal')

    """tests for time_proc"""

    def test_time_proc(self):
        """test time formatting"""
        self.Resty1._state['quiet'] = False
        result_test_time_proc = self.Resty1.time_proc(4200)
        self.assertEqual(result_test_time_proc,
                         [[1, 10], 'Finished in 1:10:0'], 'Not equal')

    """tests for generate_report"""

    def test_generate_report(self):
        """test file formatting"""
        # result_generate_report = self.Resty1.ggenerate_report(self.msgList)
        # self.assertEqual(result_test_generate_report, 'result_output', 'Not equal')
        pass