2013-01-08 21 views
1

我希望能夠查詢Rally是否存在現有缺陷,然後複製該缺陷只更改幾個字段,同時保留所有附件。有沒有簡單的方法來做到這一點?我嘗試調用rally.create並傳遞現有的缺陷對象,但未能將所有成員序列化爲JSON。最終,如果將pyral擴展到包含這種功能,那將會很好。在Python中使用Rally REST API複製缺陷的首選方法

相反,我寫了一些代碼來複制現有缺陷的每個python-native屬性,然後使用.ref作爲其他所有內容。它似乎工作得很好。我已經利用Mark W的代碼來複制附件,這也很有效。剩下的一個挫折是複製迭代不起作用。當我打電話.REF的迭代屬性,我得到這個:

>>> s 
<pyral.entity.Defect object at 0x029A74F0> 
>>> s.Iteration 
<pyral.entity.Iteration object at 0x029A7710> 
>>> s.Iteration.ref 
No classFor item for |UserIterationCapacity| 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "c:\python27\lib\site-packages\pyral\entity.py", line 119, in __getattr__ 
    hydrateAnInstance(self._context, item, existingInstance=self) 
    File "c:\python27\lib\site-packages\pyral\restapi.py", line 77, in hydrateAnInstance 
    return hydrator.hydrateInstance(item, existingInstance=existingInstance) 
    File "c:\python27\lib\site-packages\pyral\hydrate.py", line 62, in hydrateInstance 
    self._setAppropriateAttrValueForType(instance, attrName, attrValue, 1) 
    File "c:\python27\lib\site-packages\pyral\hydrate.py", line 128, in _setAppropriateAttrValueForType 
    elements = [self._unravel(element) for element in attrValue] 
    File "c:\python27\lib\site-packages\pyral\hydrate.py", line 162, in _unravel 
    return self._basicInstance(thing) 
    File "c:\python27\lib\site-packages\pyral\hydrate.py", line 110, in _basicInstance 
    raise KeyError(itemType) 
KeyError: u'UserIterationCapacity' 
>>> 

這是否看起來像拉力賽或或許還有一個自定義字段的一個問題一個問題,我們的項目管理員可能會造成的?我能夠通過構建來自oid的ref來解決它:

newArtifact["Iteration"] = { "_ref": "iteration/" + currentArtifact.Iteration.oid } 

雖然這讓我感覺很不舒服。

回答

0

最終解決方案,包括馬克·W公司用於複製附件

def getDataCopy(data): 

    """ Given a piece of data, figure out how to copy it. If it's a native python type 
     like a string or numeric, just return the value. If it's a rally object, return 
     the ref to it. If it's a list, iterate and call ourself recursively for the 
     list members. """ 

    if isinstance(data, types.ListType): 
     copyData = [] 
     for entry in data: 
      copyData.append(getDataCopy(entry)) 

    elif hasattr(data, "ref"): 
     copyData = { "_ref": data.ref } 

    else: 
     copyData = data 

    return copyData 

def getArtifactCopy(artifact): 

    """ Build a dictionary based on the values in the specified artifact. This dictionary 
     can then be passed to a rallyConn.put() call to actually create the new entry in 
     Rally. Attachments and Tasks must be copied seperately, since they require creation 
     of additional artifacts """ 

    newArtifact = {} 

    for attrName in artifact.attributes(): 

     # Skip the attributes that we can't or shouldn't handle directly 
     if attrName.startswith("_") or attrName == "oid" or attrName == "Iteration" or attrName == "Attachments": 
      continue 

     attrValue = getattr(artifact, attrName) 
     newArtifact[attrName] = getDataCopy(attrValue) 

    if getattr(artifact, "Iteration", None) != None: 
     newArtifact["Iteration"] = { "_ref": "iteration/" + artifact.Iteration.oid } 

    return newArtifact 

def copyAttachments(rallyConn, oldArtifact, newArtifact): 

    """ For each attachment in the old artifact, create new attachments and attach them to the new artifact""" 

    # Copy Attachments 
    source_attachments = rallyConn.getAttachments(oldArtifact) 

    for source_attachment in source_attachments: 

     # First copy the content 
     source_attachment_content = source_attachment.Content 
     target_attachment_content_fields = { "Content": base64.encodestring(source_attachment_content) } 

     try: 
      target_attachment_content = rallyConn.put('AttachmentContent', target_attachment_content_fields) 
      print "\t===> Copied AttachmentContent: %s" % target_attachment_content.ref 
     except pyral.RallyRESTAPIError, details: 
      sys.stderr.write('ERROR: %s \n' % details) 
      sys.exit(2) 

     # Next copy the attachment object 
     target_attachment_fields = { 
      "Name": source_attachment.Name, 
      "Description": source_attachment.Description, 
      "Content": target_attachment_content.ref, 
      "ContentType": source_attachment.ContentType, 
      "Size": source_attachment.Size, 
      "User": source_attachment.User.ref 
     } 

     # Attach it to the new artifact 
     target_attachment_fields["Artifact"] = newArtifact.ref 

     try: 
      target_attachment = rallyConn.put(source_attachment._type, target_attachment_fields) 
      print "\t===> Copied Attachment: '%s'" % target_attachment.Name 
     except pyral.RallyRESTAPIError, details: 
      sys.stderr.write('ERROR: %s \n' % details) 
      sys.exit(2) 

def copyTasks(rallyConn, oldArtifact, newArtifact): 

    """ Iterate over the old artifacts tasks and create new ones, attaching them to the new artifact """ 

    for currentTask in oldArtifact.Tasks: 

     newTask = getArtifactCopy(currentTask) 

     # Assign the new task to the new artifact 
     newTask["WorkProduct"] = newArtifact.ref 

     # Push the new task into rally 
     newTaskObj = rallyConn.put(currentTask._type, newTask) 

     # Copy any attachments the task had 
     copyAttachments(rallyConn, currentTask, newTaskObj) 

def copyDefect(rallyConn, currentDefect, addlUpdates = {}): 

    """ Copy a defect including its attachments and tasks. Add the new defect as a 
     duplicate to the original """ 

    newArtifact = getArtifactCopy(currentDefect) 

    # Add the current defect as a duplicate for the new one 
    newArtifact["Duplicates"].append({ "_ref": currentDefect.ref }) 

    # Copy in any updates that might be needed 
    for (attrName, attrValue) in addlUpdates.items(): 
     newArtifact[attrName] = attrValue 

    print "Copying %s: %s..." % (currentDefect.Project.Name, currentDefect.FormattedID), 
    newDefect = rallyConn.create(currentDefect._type, newArtifact) 

    print "done, new item", newDefect.FormattedID 

    print "\tCopying attachments" 
    copyAttachments(rallyConn, currentDefect, newDefect) 

    print "\tCopying tasks" 
    copyTasks(rallyConn, currentDefect, newDefect) 
0

退房Python的答案(有2個答案吧,另外一個是Ruby)這個問題:

Rally APIs: How to copy Test Folder and member Test Cases

答案包含Python腳本,副本測試用例,附件。儘管該腳本適用於測試用例,但邏輯應該很容易適應缺陷,因爲這些操作基本上是相同的 - 只有字段屬性會有所不同。該腳本複製附件,標籤等,幾乎是整個神器。

+0

是的,我確實看到了這個早期代碼。我唯一的預留是在腳本中調用要複製的字段。任何時候,我們的管理員添加一個新的自定義字段(他已知這樣做),腳本將需要更新。我想我可以保留一份不要複製的列表,但是我依賴於工件的拉力定義不會改變。 – svvitale

+0

您可以在TypeDefinitions/AttributeDefinitions for Defects上進行額外的查詢,並枚舉缺陷工件上的自定義字段。然後,您需要修改代碼以循環訪問這些代碼,並將它們添加到要複製的源/目標字段列表中。 – 2013-01-08 18:34:23

+0

今天我花了一些時間與這個戰鬥沒有太大的進展。我如何獲得AttributeDefinitions?我能夠爲缺陷本身獲得TypeDefinition,但是沒有任何字段信息。我正在考慮只複製每個具有本機Python類型(字符串,數字等)的屬性,然後爲需要它的字段(迭代,用戶,附件等)添加特殊處理的替代方法。你看到這種方法的缺點嗎? – svvitale

相關問題