Tuesday, April 10th, 2012

Integrating Product Studio with Python

Not sure if any Python developers have used Microsoft's Team Foundation Server, it includes an application called Product Studio for managing bugs in your companies software. My job uses it for escalations to server teams and such. Recently, I found out about the SDK, which uses the Windows COM system, and Python has a module for this, win32com. I wrote this very handle class to query Product Studio for bugs, and also create new bugs. The class is very basic, but gets the job done.

import win32com.client

class ProductStudio(object):
    def __init__(self, product='Service Delivery Escalation'):
        self.psdir = win32com.client.Dispatch("ProductStudio.Directory")
        self.psdir.Connect("domain.com")
        self.prod = self.psdir.GetProductByName(product)
        self.ds = self.prod.Connect()
    def query(self, psq):
        q = win32com.client.Dispatch("ProductStudio.Query")
        q.CountOnly = False
        q.DatastoreItemType = -100
        lst = win32com.client.Dispatch("ProductStudio.DataStoreItemList")
        lst.Query = q
        lst.Datastore = self.ds
        q.SelectionCriteria = psq
        fields = self.ds.FieldDefinitions
        q.QueryFields.Clear()
        q.QueryFields.Add(fields("Title"))
        q.QuerySortFields.Add(fields("ID"), 0)
        lst.Execute()
        items = []
        for item in lst.DatastoreItems:
            items.append({'bugid':item.Fields("ID")(),
                          'title':item.Fields("Title")(),
                          'opened_by':item.Fields("Opened By")(),
                          'status':item.Fields("Status")(),
                          'created_on':item.Fields("Opened Date")()})
        return items
    def _equals(self, equals):
        psq = ""
        for column in equals:
            psq += "<Expression Column='%s' Operator='equals'>" % column
            psq += "<String>%s</String>" % equals[column]
            psq += "</Expression>"
        return psq
    def make_query(self, equals={}):
        psq = "<Query>"
        if not isinstance(equals, dict):
            psq += "<Group GroupOperator='or'>"
            for row in equals:
                psq += self._equals(row)
            psq += "</Group>"
        else:
            psq += self._equals(equals)
        psq += "</Query>"
        return psq

class Bug(object):
    def __init__(self, datastore, path):
        self.lst = win32com.client.Dispatch("ProductStudio.DataStoreItemList")
        self.lst.Datastore = datastore
        self.lst.CreateBlank(-100)
        self.item = self.lst.DatastoreItems.Add()
        self.fields = self.item.Fields
        treeid = self._get_treeid(datastore.RootNode, path)
        self.fields["TreeID"].Value = treeid
    def __setitem__(self, key, value):
        self.fields[key].Value = value
    def __getitem__(self, key):
        return self.fields[key].Value
    def is_valid(self):
        self.errors = []
        for field in self.fields:
            if field.Validity != 0:
                self.errors.append(field.Name)
        if self.errors != []:
            return False
        return True
    def save(self):
        if self.is_valid():
            self.item.Save()
            return self.fields["ID"].Value
        return 0
    def _get_treeid(self, node, path):
        if len(path) == 0:
            return node.ID
        name = path.split("\\")[0]
        for subnode in node.Nodes:
            if subnode.Name == name:
                try:
                    subsub = path.split("\\", 1)[1]
                except IndexError:
                    return subnode.ID
                return self._get_treeid(subnode, subsub)

I originally created the querying code for a reporting tool I am working on, which is why it returns a specific set of fields from the database. The bug data model came next, and I sub-class this model to set up a specific template for a bug report. Hopefully someone else finds this helpful.

Comment #1: Posted 2 years, 8 months ago by Svetlana

Hi, guys! Thank you for a good article. We`ve just posted "Getting Started With Python" on our blog: http://www.learncomputer.com/getting-started-with-python/
Might be useful for your readrs too.
Thank you.

About Me

My Photo
Names Kevin, hugely into UNIX technologies, not just Linux. I've dabbled with the demons, played with the Sun, and now with the Penguins.




Kevin Veroneau Consulting Services
Do you require the services of a Django contractor? Do you need both a website and hosting services? Perhaps I can help.

If you like what you read, please consider donating to help with hosting costs, and to fund future books to review.

Python Powered | © 2012-2014 Kevin Veroneau