2013-01-08 43 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

相关问题