Beispiel #1
0
def projectTestMake(PARENT_CASE):
    #create new FogBugzConnect object to talk to FBAPI
    fbConnection = FogBugzConnect()
    #get estimate
    print "Please provide an estimate for the test: ",
    est = raw_input()
    fbConnection.createTestCase(PARENT_CASE,estimate=est)
Beispiel #2
0
def ls():
    gitConnection = GitConnect()
    (user,repo) = gitConnection.getUserRepo()
    fbConnection = FogBugzConnect()
    if repo=="DrewCrawfordApps": repo = "Hackity-Hack"
    elif repo=="Briefcase-wars": repo = "Briefcase Wars"
    fbConnection.listCases(repo)
Beispiel #3
0
def chargeback(case):
    import re
    fbConnection = FogBugzConnect()
    events = fbConnection.allEvents(case)
    total_time = 0

    def parsecase(match):
        if match:
            (fromt,tot) = match.groups(0)
            import dateutil.parser
            fromd = dateutil.parser.parse(fromt)
            tod = dateutil.parser.parse(tot)
            return (tod - fromd).total_seconds()
        return 0
    for event in events:
        match = re.match("recharge: A record was removed from this ticket: From (.*) to (.*)(?=ixPerson)",event)
        total_time += parsecase(match)

        match = re.match("recharge: A record was added to this ticket: From (.*) to (.*)(?=ixPerson)",event)

        total_time -= parsecase(match)

    total_time += fbConnection.getElapsed(case) * 60.0 * 60.0
    (pcase,test) = fbConnection.getCaseTuple(case,oldTestCasesOK=True,exceptOnFailure=False)
    if test:
        total_time += fbConnection.getElapsed(test) * 60.0 * 60.0
    juche.info(" %d hours" % (total_time / 60.0 / 60.0) )
    return total_time / 60.0 / 60.0
Beispiel #4
0
def projectStart(CASE_NO, fromSpec):
    """
    print "Case no: " + str(CASE_NO)
    print "from: " + fromSpec
    """
    #create new gitConnect object to talk to git
    gitConnection = GitConnect()

    #check for unsaved changes to source code
    gitConnection.checkForUnsavedChanges()

    #create new FogBugzConnect object to talk to FBAPI
    fbConnection = FogBugzConnect()
    #check for FogBugz case and clock in
    fbConnection.startCase(CASE_NO)



    #checkout or create branch with CASE_NO
    gitConnection.checkoutBranch(CASE_NO,fromSpec,fbConnection)

    settings = get_setting_dict()
    if not "viewOnStart" in settings or settings["viewOnStart"] == 1:
        fbConnection.view(CASE_NO)

    juche.info("Use work ship to commit your changes")
Beispiel #5
0
def autoTestMake(CASE_NO,fbConnection=None):
    juche.info("autotestmake %d" % CASE_NO)
    if not fbConnection: fbConnection = FogBugzConnect()
    (implement,test)  = fbConnection.getCaseTuple(CASE_NO,oldTestCasesOK=True,exceptOnFailure=False)
    if not test:
        ixTester = fbConnection.optimalIxTester(CASE_NO)
        fbConnection.createTestCase(CASE_NO,ixTester=ixTester)
        return True
    return False
Beispiel #6
0
    def test_chargeback(self):
        f = FogBugzConnect()
        self.assertAlmostEqual(chargeback(1111),-5.123611111111112) #I'm not 100% sure that this test makes any sense

        #The sum of a set of tickets should be the same as the sum of the chargebacked tickets.
        #Note that chargeback adds up the test cases for us, but f.getElapsed does not
        self.assertAlmostEqual(chargeback(1997)+chargeback(2427)+chargeback(2431)+chargeback(2695),f.getElapsed(1997)+f.getElapsed(2427)+f.getElapsed(2431)+f.getElapsed(2695)+f.getElapsed(2186)+f.getElapsed(2521))
Beispiel #7
0
 def __init__(self, dir=None):
     if not dir:
         dir = "%s/mockrepo/" % tempfile.gettempdir()
         if not os.path.exists(dir):
             os.makedirs(dir)
     self.dir = dir
     self.git = GitConnect(self.dir)
     self.fb = FogBugzConnect()
Beispiel #8
0
def workConfig(settingString):
    ALLOWED_SETTINGS = ["viewOnStart"]
    if len(settingString.split("=")) < 2:
        printUsageString()
        raise Exception("stacktraceplease")

    setting = settingString.split("=")[0]
    value = settingString.split("=")[1]
    if setting and value:
        fbConnection = FogBugzConnect()
        settings = fbConnection.getCredentials()
        if(not setting in ALLOWED_SETTINGS):
            juche.warn("setting not known. Will be added anyway.")
        fbConnection.setSetting(setting, value)
    else:
        printUsageString()
        raise Exception("stacktraceplease")
Beispiel #9
0
def projectStartTest(CASE_NO):
    gitConnection = GitConnect()
    gitConnection.checkForUnsavedChanges()

    fbConnection = FogBugzConnect()

    #get the appropriate cases out of FogBugz
    (parent,test) = fbConnection.getCaseTuple(CASE_NO)
    if not fbConnection.isReadyForTest(parent):
        print "This doesn't look ready to be tested (resolved/implemented)... are you sure about this? (press Enter to continue)"
        raw_input()



    gitConnection.fetch()
    gitConnection.checkoutBranch(parent,None,fbConnection)

    fbConnection.startCase(test,enforceNoTestCases=False)
    gitHubConnection = GitHubConnect()
    gitHubConnection.openPullRequestByName("work-%d" % CASE_NO)
Beispiel #10
0
def projectStop():
    #create new gitConnect object to talk to git
    gitConnection = GitConnect()

    #check for unsaved changes to source code
    gitConnection.checkForUnsavedChanges()

    #create new FogBugzConnect object to talk to FBAPI
    fbConnection = FogBugzConnect()

    caseno = gitConnection.extractCaseFromBranch()



    #stop working on case and checkout master
    branch = gitConnection.getBranch()
    gitConnection.pushChangesToOriginBranch(branch)
    gitConnection.checkoutMaster()

    #clock out of project
    fbConnection.stopWork(caseno)
Beispiel #11
0
def projectFailTest():
    gitConnection = GitConnect()
    gitConnection.checkForUnsavedChanges()

    reasons = {"0":"Failed a unit test.","1":"Failed a UI test"}
    for reason in reasons.keys():
        print reason," ",reasons[reason]
    print "Or just type why it failed."
    reason = raw_input()
    if reason in reasons.keys():
        reason = reasons[reason]
        purgatory = False
    else:
        purgatory = True



    caseno = gitConnection.extractCaseFromBranch()
    gitConnection.pushChangesToOriginBranch(gitConnection.getBranch())
    gitConnection.checkoutMaster()

    fbConnection = FogBugzConnect()
    (parent,test) = fbConnection.getCaseTuple(caseno)
    if purgatory: fbConnection.commentOn(parent,PURGATORY_STMT) #this signals buildbot to fail the case back to the implementer after PURGATORY expires
    #buildbot special-cases Inspect passes to be in PURGATORY, so no signaling is required for the pass case

    fbConnection.fbConnection.assign(ixBug=parent,ixPersonAssignedTo=magic.BUILDBOT_IXPERSON,sEvent="Terribly sorry but your case FAILED a test: %s" % reason)
    fbConnection.stopWork(test)

    # play sounds!
    getstatusoutput ("afplay -v 7 %s/media/dundundun.aiff" % sys.prefix)
Beispiel #12
0
def _fixFors_to_EBS_dates(abbreviatedTest=False):
    juche.dictate(fixing_dates_per_ebs=1)
    fbConnection = FogBugzConnect()
    fixfors = fbConnection.dependencyOrder(fbConnection.listFixFors())
    if abbreviatedTest:
        fixfors = fixfors[:5]
    for item in fixfors:
        name = fbConnection.nameForFixFor(fbConnection.fixForDetail(item))
        if name.startswith("Never"): continue
        juche.info("processing %s %s" % (name,item))
        date = fbConnection.getShipDate(item)
        from dateutil.parser import parse
        if not abbreviatedTest:
            fbConnection.editFixForShipDate(item,parse(date))
        #It's bad to leave EBS in a partially-edited state.  Therefore we simply log the output and don't actually write any changes when in abbreviated test mode.
        else:
            juche.warning("Not editing the ship date in %s to %s because this is an abbreviated test." % (item,date))
Beispiel #13
0
def projectIntegrate(CASE_NO,defaultgitConnection=GitConnect()):
    if not defaultgitConnection:
        interactive = True
    else:
        interactive = False
    gitConnection = defaultgitConnection


    gitConnection.checkForUnsavedChanges() #see referencing note on line 369

    fbConnection = FogBugzConnect()
#still open here
    # make sure integration is even worth it...
    fbConnection.ensureReadyForTest(CASE_NO)


    gitConnection.checkoutExistingBranch(CASE_NO)

    integrate_to = fbConnection.getIntegrationBranch(CASE_NO)
    gitConnection.checkoutExistingBranchRaw(integrate_to)
    gitConnection.resetHard_INCREDIBLY_DESTRUCTIVE_COMMAND() #this is safe because we check for unsafe changes on line 357.
    gitHubConnection = GitHubConnect(gitConnect=gitConnection)
    gitHubConnection.closePullRequestbyName("work-%d" % CASE_NO)
    #check for test case
    try:
        (parent, test) = fbConnection.getCaseTuple(CASE_NO,oldTestCasesOK=True)
    except:
            if interactive:
                juche.warn("no test case! Press enter to continue")
                raw_input()

    if not interactive:
        if not gitConnection.mergeIn("work-%d" % CASE_NO,pretend=True):
            return False
    gitConnection.mergeIn("work-%d" % CASE_NO)

    fbConnection.commentOn(CASE_NO,"Merged into %s" % integrate_to)
    fbConnection.closeCase(CASE_NO)
    if not interactive:
        gitConnection.pushChangesToOriginBranch(branch=integrate_to)
    return True
Beispiel #14
0
def complain(ixComplainAboutPerson,sComplainAboutProject):
    fbConnection = FogBugzConnect()
    response = fbConnection.fbConnection.search(q="status:active assignedto:=%d project:%s" % (ixComplainAboutPerson,sComplainAboutProject),cols="hrsCurrEst,hrsElapsed,sPersonAssignedTo,sFixFor,sCategory")
    for case in response.cases:
        #print case
        if case.hrscurrest.contents[0]=="0":
            juche.info("%s's case %s has no estimate" % (case.spersonassignedto.contents[0], case["ixbug"]))
            fbConnection.commentOn(case["ixbug"],"This next test could take a very, VERY long time.")
        if case.sfixfor.contents[0]=="Undecided" and (case.scategory.contents[0] == "bug" or case.scategory.contents[0] == "feature"):
            juche.info("%s needs a milestone" % case["ixbug"])
            fbConnection.commentOn(case["ixbug"],"If you choose not to decide, you still have made a choice.  (Don't think about it, don't think about it...)  It's a paradox!  There IS no answer.")
        est = float(case.hrscurrest.contents[0])
        act = float(case.hrselapsed.contents[0])
        if est - act < 0:
            juche.info("%s's case %s requires updated estimate" % (case.spersonassignedto.contents[0], case["ixbug"]))
            fbConnection.commentOn(case["ixbug"],"I'll give you credit:  I guess you ARE listening to me.  But for the record:  You don't have to go THAT slowly.")
Beispiel #15
0
def _fixFors_test_quickly_dates(abbreviatedTest=False):
    juche.dictate(fixing_test_milestones=1)
    fbConnection = FogBugzConnect()
    fixfors_raw = fbConnection.listFixFors()
    fixfors = fbConnection.dependencyOrder(fixfors_raw)
    from dateutil.parser import parse
    import datetime
    if abbreviatedTest:
        fixfors = fixfors[:5]
    for testMilestone in fixfors:

        testMilestone_raw = fbConnection.fixForDetail(testMilestone)
        testName = fbConnection.nameForFixFor(testMilestone_raw)
        if testName.startswith("Never"): continue
        juche.dictate(testMilestone=testMilestone,testName=testName)
        if not testName.endswith("-test"): continue
        if testName=="Undecided-test": continue
        matched = False
        if testMilestone_raw.ixproject.contents==[]: continue
        for item in fbConnection.listFixFors(ixProject=int(testMilestone_raw.ixproject.contents[0])):
            #print testName[:-5],fbConnection.nameForFixFor(item)
            if item.sfixfor.contents[0]==testName[:-5]:
                #print "matching",testName,fbConnection.nameForFixFor(item)
                matched = True
                break
        if not matched:
            juche.info(testMilestone_raw)
            raise Exception("Cannot match "+testName)
        if item.dt.contents==[]:
            juche.info("Can't set %s because the non-test milestone has no completion date." % testName)
            continue
        date = item.dt.contents[0]
        newDate = parse(date)+datetime.timedelta(hours=6) #turns out that using 1 day produces weird results.  If the next implementation milestone is completed within 24 hours, lots of weird things can happen
        juche.info("setting %s to %s"% (testName,newDate))
        if not abbreviatedTest:
            fbConnection.editFixForShipDate(testMilestone,newDate)
        #It's bad to leave EBS in a partially-edited state.  Therefore we simply log the output and don't actually write any changes when in abbreviated test mode.
        else:
            juche.warn("Not editing the ship date in %s to %s because this is an abbreviated test." % (testMilestone,newDate))
Beispiel #16
0
def projectPassTest():
    gitConnection = GitConnect()
    gitConnection.checkForUnsavedChanges()
    caseno = gitConnection.extractCaseFromBranch()
    gitConnection.pushChangesToOriginBranch(gitConnection.getBranch())
    gitConnection.checkoutMaster()

    fbConnection = FogBugzConnect()
    (parent,test) = fbConnection.getCaseTuple(caseno)
    statuses = fbConnection.getStatuses(test)
    for i in range(0,len(statuses.keys())):
        print i,":  ",statuses[sorted(statuses.keys())[i]]

    print "Choose your adventure: ",
    choice = input()
    ix = sorted(statuses.keys())[choice]

    fbConnection.resolveCase(test,ixstatus=ix)
    fbConnection.closeCase(test)

    fbConnection.fbConnection.assign(ixBug=parent,ixPersonAssignedTo=magic.BUILDBOT_IXPERSON)

    # play sounds!
    getstatusoutput("afplay -v 7 %s/media/longcheer.aiff" % sys.prefix)
Beispiel #17
0
def releaseNotes(project,milestone):
    fb = FogBugzConnect()
    ixMilestone = fb.getIxFixFor(project,milestone)
    notes = fb.releaseNotes(ixMilestone)
    for note in notes:
        print note
Beispiel #18
0
def projectView(CASE_NO):
    fbConnection = FogBugzConnect()
    fbConnection.view(CASE_NO)
Beispiel #19
0
def recharge(fr,to):
    import dateutil.parser
    fbConnection = FogBugzConnect()
    fbConnection.setParentIfUnset(fr,to)
    #cannot create a time record for a closed case...
    mustOpen = not fbConnection.isOpen(to)
    if mustOpen:
        fbConnection.reopen(to,"work.py recharge")
    results = fbConnection.listTimeRecords(fr)
    time_interval = 0
    my_records = []
    for record in results:
        #print record
        if record.fdeleted.contents[0]!="false":
            juche.warn("Skipping deleted record %s" % record)
            continue
        if len(record.dtend)==0:
            juche.warn("Skipping open time record %s" % record)
            continue
        my_records.append(record)
    r = 0
    for record in my_records:
        juche.info("%d: %s-%s" % (r,record.dtstart.contents[0],record.dtend.contents[0]))
        r += 1

    def parse_range(astr): # http://stackoverflow.com/questions/4726168/parsing-command-line-input-for-numbers
        result=set()
        for part in astr.split(','):
            x=part.split('-')
            result.update(range(int(x[0]),int(x[-1])+1))
        return sorted(result)

    strl = raw_input("records  (syntax like 22-27,51-64): ")
    results = parse_range(strl)
    for result in results:
        record = my_records[result]
        record_desc = "From %s to %s ixPerson %s ixBug %s" % (record.dtstart.contents[0],record.dtend.contents[0],record.ixperson.contents[0],record.ixbug.contents[0])
        from_time = dateutil.parser.parse(record.dtstart.contents[0])
        to_time = dateutil.parser.parse(record.dtend.contents[0])
        time_interval += (to_time-from_time).total_seconds()
        juche.info("from_time %s to_time %s time_interval %s" % (from_time,to_time,time_interval))
        fbConnection.commentOn(fr,"recharge: A record was removed from this ticket: %s, see case %d" % (record_desc,to))
        fbConnection.commentOn(to,"recharge: A record was added to this ticket: %s, see case %d" % (record_desc, fr))
        fbConnection.createTimeRecord(to,str(record.dtstart.contents[0]),str(record.dtend.contents[0]))
    oldEst = fbConnection.getEstimate(fr) * 60.0 * 60.0
    newEst = (oldEst - time_interval) / 60.0 / 60.0
    if newEst <= 0: newEst = 1/60.0
    juche.info("Setting estimate to",newEst)
    fbConnection.setEstimate(fr,timespan="%f hours" % newEst)
#fbConnection.deleteTimeRecord(record.ixinterval.contents[0])
    if mustOpen: fbConnection.closeCase(to)
Beispiel #20
0
def projectShip():
    if lint_loaded:
        try:
            result = lint.Lint.analyze()
        except Exception as e:
            juche.exception(e)
            print "Lint sunk the ship, but we still have a liferaft!"
        else:
            if not result:
                while True:
                    cont = raw_input("Your code failed lint analyses. Continue anyway? (Y/n)")
                    if cont == "n":
                        exit()
                    if cont == "Y":
                        break

    #create new gitConnect object to talk to git
    gitConnection = GitConnect()
    gitConnection.checkForUnsavedChanges();

    #create new FogBugzConnect object to talk to FBAPI
    fbConnection = FogBugzConnect()

    #check if we're in a git repo
    branch = gitConnection.getBranch();


    # check if branch is the right branch
    caseno = gitConnection.extractCaseFromBranch()
    gitConnection.pushChangesToOriginBranch(branch)
    gitConnection.checkoutMaster()

    #create the pull request
    gitHubConnect = GitHubConnect()

    if not gitHubConnect.pullRequestAlreadyExists("work-%d" % caseno):
        body = "Ticket at %s/default.asp?%d\n%s" % (fbConnection.getFBURL(),caseno,raw_input("Type a note: "))
        list =  gitConnection.getUserRepo()
        integrationBranch = fbConnection.getIntegrationBranch(caseno)
        if integrationBranch=="Undecided":
            raise Exception("Come on now, you've implemented the ticket before you decided what mielstone it was?  You have to decide!")
        pullURL = gitHubConnect.createPullRequest("work-%d" % caseno,body,integrationBranch,"work-%d" % caseno)
        fbConnection.commentOn(caseno,"Pull request at %s\n%s" %(pullURL,body))


    #is there a test case?
    try:
        (parent,child) = fbConnection.getCaseTuple(caseno)
        fbConnection.resolveCase(caseno,isTestCase_CASENO=child)
    except:
        fbConnection.resolveCase(caseno)
        pass
    print """There's about an 80% chance that whatever you just did was work that rightfully belongs to some other (possibly closed) case.  Recharging is a way to signify to EBS that your work should be counted against a different case.

        Ex 1: You're fixing a somewhat-forseeable bug in a feature that was implemented and estimated in another case, but for some reason a new bug has been filed instead of the old feature reactivated.  Recharge to the original feature, as whoever estimated that should have accounted for a relatively bug-free implementation.

        Ex 2: You're implementing a feature that was originally estimated in some other case.  Maybe it was a parent case that was broken down into child cases, or maybe somebody carved out a feature of a larger something for you to implement.

        When there are multiple candidates for a recharge, use your judgment.  Pick the newer case where reasonable.

        DO NOT RECHARGE
        1) Things that are legitimately and substantially new features
        2) Test cases, inquiries, or fake tickets
        3) Build feedback / ship tickets """

    print "recharge to: (case#)",
    TO_CASE = raw_input()
    if TO_CASE:
        to = int(TO_CASE)
        recharge(caseno,to)
Beispiel #21
0
class MockRepo:
    git = None
    dir = None
    fb = None

    def __init__(self, dir=None):
        if not dir:
            dir = "%s/mockrepo/" % tempfile.gettempdir()
            if not os.path.exists(dir):
                os.makedirs(dir)
        self.dir = dir
        self.git = GitConnect(self.dir)
        self.fb = FogBugzConnect()

    def createFile(self, name, contents):
        self.editFile(name, contents)

    def editFile(self, name, contents):
        with open(self.dir+name, "w") as file:
            file.write(contents)

    def readFile(self, name):
        with open(self.dir+name, "r") as file:
            return file.read()

    def gitInit(self):
        self.git.statusOutput("git init", self.dir)

    def gitAdd(self, file):
        self.git.add(file)

    def gitCommit(self, message):
        self.git.commitAll(message)

    def gitPush(self, forceful=False):
        if forceful:
            self.git.statusOutput("git push -u -f")
        else:
            self.git.statusOutput("git push -u")

    def gitPull(self):
        self.git.fetch()
        self.git.pull()
        self.git.submoduleUpdate() #Drew would like to document that this is redundant. Bion would like to document that this redundancy is not clear.

    def gitMerge(self, branch):
        self.git.mergeIn(branch)

    def gitCheckout(self, branch):
        self.git.checkoutExistingBranchRaw(branch)

    #is this name really necessary?
    def wipeRepo__INCREDIBLY__DESTRUCTIVE_COMMAND(self):
        self.git.statusOutputExcept("git push -f --all --delete")
        self.git.statusOutput("rm -Rf .git")
        self.git.statusOutputExcept("git init")
        self.git = GetConnect(self.dir)
        self.git.statusOutputExcept("git remote add origin [email protected]:drewcrawford/SampleProject.git")
        self.createFile("README.txt", "test")
        self.gitAdd("README.txt")
        self.gitCommit("First Commit")
        self.gitPush()

    def ticketReactivate(self, ticket):
        if "Closed" in self.fb.getStatuses(ticket):
            self.fb.reactivate(ticket, 7, "Just testing...")

    def ticketResolve(self, ticket):
        self.fb.resolveCase(ticket)

    def ticketClose(self, ticket):
        self.fb.closeCase(ticket)

    def ticketAssign(self, ticket, ixUser):
        self.fb.assignCase(ticket, ixUser)

    def ticketCreate(self, title):
        return self.fb.createCase(title, 1, 1, 7)

    def ticketSetEstimate(self, ticket, estimate):
        self.fb.setEstimate(ticket, estimate)