Welcome to fogbugz-orm’s documentation!

fogbugz-orm - FogBugz ORM wrapper around the FogBugz XML API

This is a python interface to the FogBugz XML API FogBugz (http://www.fogcreek.com/fogbugz/) issue tracker. It wraps the FogBugzPy python interface, which provides non-typed python binding via BeautifulSoup. As well as type conversion, this ORM interface provides direct JSON serialization of the FogBugz data using the jsontree module.

Warning

It is HIGHLY recommended that you use an SSL connection to the FogBugz server for secure authentication.

Project Links:

External Links:

Quick Start

Comparing FogBugzPy to FogBugz-ORM

Example code from FogBugzPy documentation:

resp = fb.search(cols="ixBug,sTitle")

for case in resp.cases.findAll('case'):
    print "%s: %s" % (case.ixbug.string,
                      case.stitle.string.encode('UTF-8'))

Equivalent FogBugz-ORM code:

cases = fb.search(cols="ixBug,sTitle")

for case in cases:
    print "%d: %s" % (case.ixBug, case.sTitle)
  • You can access the array of cases directly as a list.
  • The column names are referred to in their proper mixed case matching the API as they must be for the cols argument; ixBug, sTitle.
  • The data is extracted and properly converted; ixBug is an integer, and sTitle is a UTF-8 converted string.

Get all the cases from the ‘To Be Closed’ filter and close them.

import fborm

### login form 1
fbo = fborm.FogBugzORM('https://hostname/', secret_token)

### Find the 'To Be Closed' filter
filters = fbo.listFilters()
for filt in filters:
    if filt.sName == 'To Be Closed':
        break

### Set it as the current filter
fbo.setCurrentFilter(filt)

### Get all cases in that filter
cases = fbo.search()

### Make sure they are closed
for case in cases:
    if not case.fOpen:
        continue
    if 'Active' in case.sStatus:
        fbo.resolve(ixBug=case.ixBug)
    fbo.close(ixBug=case.ixBug)

Create a new case

import fborm
import jsontree

### login form 2
fbo = fborm.FogBugzORM('https://hostname/', username=u, password=p)

bug = jsontree.jsontree()
bug.sCategory = 'Bug'
bug.sProject = 'My Project'
bug.sArea = 'Some Area'
bug.sTitle = 'The title of the bug'
bug.tags = ['tag1', 'tag2', 'tag3']
bug.sEvent = """
    Some nice long comment for the change being made
"""

ixBug = fbo.new(bug)

List some data

import fborm

fbo = fborm.FogBugzORM('https://hostname/')
### login form 3
fbo.logon(username=u, password=p)

people = fbo.listPeople()
projects = fbo.listProjects()
areas = fbo.listAreas()
areas_in_proj = fbo.listProjects(ixProject=projects[0].ixProject)

### if you are using the CustomFields plugin
custom_field_names = fbo.listCustomFieldNames()

CustomFields Plugin Data

The CustomFields plugin allows you to add yor own elements to cases in FogBugz. These elements are added to the FogBugz XML API with a prefix and a unique magic string suffix. Also, any punctuation is transformed, so you will need to look up what your custom field is with fborm.FogBugzORM.listCustomFieldNames(). Once you know that, you can simplify your code by setting a namemap for the returned data. This means that if you have multiple servers with the same CustomFields, they will have different names in the API. Having a per-server namemap greatly simplifies your code.

import fborm

### Mapping of code name to what it is in the FogBugz XML API.
custom_field_map = dict(
    sBranch = 'plugin_customfields_at_fogcreek_com_branchg83'
)

### fborm type mapping between the FogBugz XML API element to python type
### only list the items you want returned.
fbBugType = dict(
    ixBug = fborm.fbint,
    sTitle = fborm.fbstring,
    sBranch = fborm.fbstring,
    dtOpened = fborm.fbdatatime,
)

### supply a ``namemap`` for mapping custom fields to more friendly
### in code names.
fbo = fborm.FogBugzORM('https://hostname/', namemap=custom_field_map)
### login form 4
fbo.token = secret_token

### All the cases in the last week
bugs = fbo.search(q='opened:"This Week"', casetype=fbBugType)
for bug in bugs:
    print bug.ixBug, bug.sBranch, bug.dtOpened.isoformat(), bug.sTitle

    ### if it is for the 'feature_x' branch, set it to be 'feature_xy'
    if sBranch == 'feature_x':
        bug.sBranch = 'feature_xy'
        bug.sEvent = "The 'feature_x' branch was merged into 'feature_xy'"
        del bug['dtOpened'] # only admins can set this.
        fbo.edit(bug, fbBugType)

Problems with the FogBugz XML API

For the most part, the FogBugz XML API is very clean, clean, and easy to use. However there are some odd corner cases in which the inderlying implementation is showing through and causing odd inconsistencies which can be problimatic.

#. BeautifulSoup lowercasing all tags and attributes

Because the FogBugz API Python interface is just a thin wrapper around the BeautifulSoup HTML/XML parser, the resulting objects returned have all the elements lower cased. This can be a problem because for bugs and other objects, these names need to be supplied to the cols argument with the mixed case preserved. It also makes it harder to read the resulting python code, and match it up to the FogBugz XML API documentation.

import fogbugz

fb = fogbugz.FogBugz("https://hostname/", secret_token)
result = fb.search(q="123", cols="sTitle, sPersonAssignedTo, ixArea")
print result.cases.case.stitle.text
print result.cases.case.spersonassignedto.text
ixArea = int(result.cases.case.ixarea.text, 10)
if ixArea > 10:
    print ixArea

The FogBugzORM preserved the mixed case, and will manage the mixed case and the cols argument for you, if not supplied.

import fborm

fbBugType = dict(
    sTitle = fborm.fbstring,
    sPersonAssignedTo = fborm.fbstring,
    ixArea = fborm.fbint,
)

fbo = fborm.FogBugzORM("https://hostname/", secret_token)
result = fbo.search(q='123', casetype=fbBugType)
print result[0].sTitle
print result[0].sPersonAssignedTo
if result[0].ixArea > 10:
    print result[0].ixArea

While more time is required to describe the elements you want returned and their types. You end up with a more natureal python feel to the interface. The interface does have a default casetype argument which is fborm.objects.fbBug. Be default it will return and parse all the elements ddescribed. You can also supply the cols argument yourself to return a subset that way.

import fborm

fbo = fborm.FogBugzORM("https://hostname/", secret_token)
result = fbo.search(q='123', cols="sTitle,sPersonAssignedTo,ixArea")
print result[0].sTitle
print result[0].sPersonAssignedTo
if result[0].ixArea > 10:
    print result[0].ixArea

Understanding the Componenets

XML Element Types

XML Object Type Mapping

Parsing XML Data

FogBugz API Command Wrappers

FogBugz API Instance Wrapper

License

Copyright (c) 2013, Douglas Napoleone. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of Django nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Indices and tables