/* Perforce Defect Tracking Integration Project
TEAMTRACK-MODULE.CPP -- PYTHON MODULES INTERFACING TO TEAMTRACK
Gareth Rees, Ravenbrook Limited, 2000-08-02
1. INTRODUCTION
This file implements the "teamtrack45" and "teamtrack50" Python
modules which interface to TeamTrack 4.5 and TeamTrack 5.0
respectively. See [GDR 2000-08-08] for the design.
The intended readership is project developers.
This document is not confidential. */
#include "teamtrack-module.h"
#include "teamtrack-server.h"
#include "TSServer.h"
/* Objects representing Python exceptions. They are assigned in
initteamtrack(). */
/* teamtrack_error represents errors that represents errors generated by this
interface module. In Python it's called teamtrack.error. */
PyObject *teamtrack_error;
/* teamtrack_tsapi_error represents errors generated by the TeamShare API. In
Python it's called teamtrack.tsapi_error. */
static PyObject *teamtrack_tsapi_error;
/* teamtrack_error is a map from TeamTrack error code to a name for that
error. */
typedef struct {
int id;
char *name;
} int_to_string_map;
int_to_string_map teamtrack_error_code[] = {
/* These error codes are used in TeamTrack 4.5. */
{ TS_ERROR, "ERROR" },
{ TS_INVALID_DATATYPE, "INVALID_DATATYPE" },
{ TS_INVALID_USER, "INVALID_USER" },
{ TS_INVALID_VERSION, "INVALID_VERSION" },
{ TS_MEMORY_ERROR, "MEMORY_ERROR" },
{ TS_NO_PERMISSION, "NO_PERMISSION" },
{ TS_NO_RESULTS, "NO_RESULTS" },
{ TS_NO_SUCH_FIELD, "NO_SUCH_FIELD" },
{ TS_SERVER_ERROR, "SERVER_ERROR" },
{ TS_SOCKET_CONNECT_FAILED, "SOCKET_CONNECT_FAILED" },
{ TS_SOCKET_CREATE_FAILED, "SOCKET_CREATE_FAILED" },
{ TS_SOCKET_READ_ERROR, "SOCKET_READ_ERROR" },
{ TS_SOCKET_WRITE_ERROR, "SOCKET_WRITE_ERROR" },
/* These error codes were added in TeamTrack 5.0. */
#if TS_APIVERSION >= 3
{ TS_ADD_TABLE_ERROR, "ADD_TABLE_ERROR" },
{ TS_CONCURRENT_LICENSE_UNAVAILABLE, "CONCURRENT_LICENSE_UNAVAILABLE" },
{ TS_FILE_NOT_FOUND, "FILE_NOT_FOUND" },
{ TS_FILE_READ_ERROR, "FILE_READ_ERROR" },
{ TS_FILE_SEEK_ERROR, "FILE_SEEK_ERROR" },
{ TS_INCORRECT_MODE, "INCORRECT_MODE" },
{ TS_INVALID_FIELDS, "INVALID_FIELDS" },
{ TS_INVALID_PARAMETERS, "INVALID_PARAMETERS" },
{ TS_INVALID_PREFIX, "INVALID_PREFIX" },
{ TS_ITEM_NOT_FOUND, "ITEM_NOT_FOUND" },
{ TS_LICENSING_VIOLATION, "LICENSING_VIOLATION" },
{ TS_MISSING_PARAMETER, "MISSING_PARAMETER" },
{ TS_NOT_UPDATEABLE, "NOT_UPDATEABLE" },
{ TS_NO_IP_ADDRESS, "NO_IP_ADDRESS" },
{ TS_PARAMETER_NOT_UNIQUE, "PARAMETER_NOT_UNIQUE" },
{ TS_RECORD_LOCKED, "RECORD_LOCKED" },
{ TS_RECORD_LOCK_BROKEN, "RECORD_LOCK_BROKEN" },
{ TS_STRING_TOO_LONG, "STRING_TOO_LONG" },
{ TS_TRANSITION_INVALID, "TRANSITION_INVALID" },
#endif /* TS_APIVERSION >= 3 */
};
/* teamtrack_set_error(server) sets the current Python error to the most recent
TeamTrack error for the given TeamShare server. */
void teamtrack_set_error(TSServer *server) {
char *error_name = "(unknown)";
int error_id = TSGetLastError();
for (size_t i = 0; i < sizeof(teamtrack_error_code) / sizeof(teamtrack_error_code[0]); ++i) {
if (error_id == teamtrack_error_code[i].id) {
error_name = teamtrack_error_code[i].name;
break;
}
}
PyObject *separator = PyString_FromString(": ");
const char *ts_message = server->GetLastErrorMessage();
PyObject *error_message;
if (ts_message != NULL) {
error_message = PyString_FromString(ts_message);
} else {
error_message = PyString_FromString("(no message from the TeamShare API)");
}
PyObject *message = PyString_FromString(error_name);
PyString_ConcatAndDel(&message, separator);
PyString_ConcatAndDel(&message, error_message);
PyErr_SetObject(teamtrack_tsapi_error, message);
Py_XDECREF(message); /* Owned by Python's exception handler. */
}
/* teamtrack.connect(user,password,hostname) connects to a TeamShare server on
hostname with the given user and password and returns an object representing
that server. */
static PyObject *
teamtrack_connect(PyObject *self, PyObject *args) {
char *user;
char *password;
char *hostname;
if (!PyArg_ParseTuple(args, "sss", &user, &password, &hostname)) {
return NULL;
}
TSServer *s = new TSServer;
if (s == NULL) {
return NULL;
}
if (s->Connect(user, password, hostname) != TS_OK) {
teamtrack_set_error(s);
delete s;
return NULL;
}
return teamtrack_server_new(s, 1);
}
static PyMethodDef teamtrack_methods[] = {
{ "connect", teamtrack_connect, METH_VARARGS },
{ NULL, NULL } /* End of methods. */
};
typedef struct {
char *name;
int id;
} string_to_int_map;
/* teamtrack_make_map(c_map, size, name, dict) turns the C map c_map (with size
items) into a Python map, and installs it with the given name in the given
dictionary. */
void teamtrack_make_map(string_to_int_map c_map[], size_t size,
char *name, PyObject *dict) {
PyObject *py_map = PyDict_New();
PyDict_SetItemString(dict, name, py_map);
for (size_t i = 0; i < size; ++i) {
PyObject *id = PyInt_FromLong((long)c_map[i].id);
PyDict_SetItemString(py_map, c_map[i].name, id);
Py_XDECREF(id); /* Owned by py_map. */
}
Py_XDECREF(py_map); /* Owned by dict. */
}
/* teamtrack_table[] is used to initialize the teamtrack.table dictionary that
maps table name to table id. The table identifiers appear in TSDef.h. */
string_to_int_map teamtrack_table[] = {
{ "ATTACHMENTS", TS_TBLID_ATTACHMENTS },
{ "CASES", TS_TBLID_CASES },
{ "CHANGES", TS_TBLID_CHANGES },
{ "COMPANIES", TS_TBLID_COMPANIES },
{ "CONTACTS", TS_TBLID_CONTACTS },
{ "FIELDORDERINGS", TS_TBLID_FIELDORDERINGS },
{ "FIELDS", TS_TBLID_FIELDS },
{ "FOLDERCOLUMNS", TS_TBLID_FOLDERCOLUMNS },
{ "FOLDERITEMS", TS_TBLID_FOLDERITEMS },
{ "FOLDERS", TS_TBLID_FOLDERS },
{ "GROUPS", TS_TBLID_GROUPS },
{ "INCIDENTS", TS_TBLID_INCIDENTS },
{ "KEYWORDS", TS_TBLID_KEYWORDS },
{ "KEYWORDUSAGES", TS_TBLID_KEYWORDUSAGES },
{ "LICENSES", TS_TBLID_LICENSES },
{ "MACROS", TS_TBLID_MACROS },
{ "MEMBERS", TS_TBLID_MEMBERS },
{ "MERCHANDISE", TS_TBLID_MERCHANDISE },
{ "NOTIFICATIONCONDITIONS", TS_TBLID_NOTIFICATIONCONDITIONS },
{ "NOTIFICATIONEVENTS", TS_TBLID_NOTIFICATIONEVENTS },
{ "NOTIFICATIONFIELDS", TS_TBLID_NOTIFICATIONFIELDS },
{ "NOTIFICATIONMESSAGES", TS_TBLID_NOTIFICATIONMESSAGES },
{ "NOTIFICATIONPERMISSIONS", TS_TBLID_NOTIFICATIONPERMISSIONS },
{ "NOTIFICATIONRULES", TS_TBLID_NOTIFICATIONRULES },
{ "NOTIFICATIONS", TS_TBLID_NOTIFICATIONS },
{ "NOTIFICATIONSUBSCRIPTIONS", TS_TBLID_NOTIFICATIONSUBSCRIPTIONS },
{ "PRIVILEGES", TS_TBLID_PRIVILEGES },
{ "PROBLEMS", TS_TBLID_PROBLEMS },
{ "PRODUCTS", TS_TBLID_PRODUCTS },
{ "PRODUCTUSAGES", TS_TBLID_PRODUCTUSAGES },
{ "PROJECTS", TS_TBLID_PROJECTS },
{ "PROJECTSELECTIONS", TS_TBLID_PROJECTSELECTIONS },
{ "PROJECTTRANSITIONS", TS_TBLID_PROJECTTRANSITIONS },
{ "PROPERTIES", TS_TBLID_PROPERTIES },
{ "REPORTS", TS_TBLID_REPORTS },
{ "RESOLUTIONS", TS_TBLID_RESOLUTIONS },
{ "SELECTIONS", TS_TBLID_SELECTIONS },
{ "SERVICEAGREEMENTS", TS_TBLID_SERVICEAGREEMENTS },
{ "STATES", TS_TBLID_STATES },
{ "SYSTEMINFO", TS_TBLID_SYSTEMINFO },
{ "TABLES", TS_TBLID_TABLES },
{ "TRANSISSUETYPES", TS_TBLID_TRANSISSUETYPES },
{ "TRANSITIONS", TS_TBLID_TRANSITIONS },
{ "TRANSTRIGGERS", TS_TBLID_TRANSTRIGGERS },
{ "TRANSTRIGGERSTATES", TS_TBLID_TRANSTRIGGERSTATES },
{ "TRANSTRIGGERTRANSITIONS", TS_TBLID_TRANSTRIGGERTRANSITIONS },
{ "USERS", TS_TBLID_USERS },
{ "VCACTIONS", TS_TBLID_VCACTIONS },
{ "WORKFLOWS", TS_TBLID_WORKFLOWS },
/* These tables were added in TeamTrack 5.0. */
#if TS_APIVERSION >= 3
{ "ADMINGROUPS", TS_TBLID_ADMINGROUPS },
{ "ADMINLOCKS", TS_TBLID_ADMINLOCKS },
{ "ADMINTABLES", TS_TBLID_ADMINTABLES },
{ "BLOBS", TS_TBLID_BLOBS },
{ "BROWSERS", TS_TBLID_BROWSERS },
{ "GWECOMMENTS", TS_TBLID_GWECOMMENTS },
{ "GWESTATES", TS_TBLID_GWESTATES },
{ "GWETRANSITIONS", TS_TBLID_GWETRANSITIONS },
{ "ITEMNOTIFICATIONS", TS_TBLID_ITEMNOTIFICATIONS },
{ "LASTID", TS_TBLID_LASTID },
{ "MAILBOX", TS_TBLID_MAILBOX },
{ "MAILHEADERFIELDS", TS_TBLID_MAILHEADERFIELDS },
{ "MAILMAPPINGS", TS_TBLID_MAILMAPPINGS },
{ "MSSELECTION", TS_TBLID_MSSELECTION },
{ "RECORDLOCKS", TS_TBLID_RECORDLOCKS },
{ "RESOURCES", TS_TBLID_RESOURCES },
{ "SECTIONS", TS_TBLID_SECTIONS },
{ "SELFREGFIELDS", TS_TBLID_SELFREGFIELDS },
{ "SOLUTIONS", TS_TBLID_SOLUTIONS },
{ "SUBTASKS", TS_TBLID_SUBTASKS },
{ "SYSTEMSETTINGS", TS_TBLID_SYSTEMSETTINGS },
{ "USAGES", TS_TBLID_USAGES },
#endif /* TS_APIVERSION >= 3 */
};
/* teamtrack_field_type[] is used to initialize the teamtrack.field_type
dictionary that maps field type name to field type id. The field type
identifiers appear in TSDef.h. */
string_to_int_map teamtrack_field_type[] = {
{ "NUMERIC", TS_FLDTYPE_NUMERIC },
{ "TEXT", TS_FLDTYPE_TEXT },
{ "MEMO", TS_FLDTYPE_MEMO },
{ "DATETIME", TS_FLDTYPE_DATETIME },
{ "SELECTION", TS_FLDTYPE_SELECTION },
{ "BINARY", TS_FLDTYPE_BINARY },
{ "STATE", TS_FLDTYPE_STATE },
{ "USER", TS_FLDTYPE_USER },
{ "PROJECT", TS_FLDTYPE_PROJECT },
{ "SUMMATION", TS_FLDTYPE_SUMMATION },
{ "MULTIPLE_SELECTION", TS_FLDTYPE_MULTIPLE_SELECTION },
{ "CONTACT", TS_FLDTYPE_CONTACT },
{ "COMPANY", TS_FLDTYPE_COMPANY },
{ "INCIDENT", TS_FLDTYPE_INCIDENT },
{ "PRODUCT", TS_FLDTYPE_PRODUCT },
{ "SERVICEAGREEMENT", TS_FLDTYPE_SERVICEAGREEMENT },
{ "FOLDER", TS_FLDTYPE_FOLDER },
{ "KEYWORDLIST", TS_FLDTYPE_KEYWORDLIST },
{ "PRODUCTLIST", TS_FLDTYPE_PRODUCTLIST },
{ "PROBLEM", TS_FLDTYPE_PROBLEM },
{ "RESOLUTION", TS_FLDTYPE_RESOLUTION },
{ "MERCHANDISE", TS_FLDTYPE_MERCHANDISE },
};
/* teamtrack_feature[] is used to initialize the teamtrack.feature
dictionary that maps feature name to 1 if the feature is supported.
The feature identifiers appear in teamtrack-module.h. */
string_to_int_map teamtrack_feature[] = {
{ "default", 1 },
#ifdef TEAMTRACK_FEATURE_SUBMIT
{ "submit", 1 },
#endif /* defined(TEAMTRACK_FEATURE_SUBMIT) */
};
void teamtrack_init(PyObject *m) {
PyObject *d = PyModule_GetDict(m);
/* Create an object to represent errors in the Python interface to TeamTrack
(a string with the value "TeamTrack interface error") and give it the
Python name "teamtrack.error". */
teamtrack_error = PyString_FromString("TeamTrack interface error");
PyDict_SetItemString(d, "error", teamtrack_error);
/* Don't decref teamtrack_error since we continue to own it through a global
variable. */
/* Create an object to represent TeamTrack errors (a string with the value
"TeamTrack error") and give it the Python name "teamtrack.error". */
teamtrack_tsapi_error = PyString_FromString("TeamShare API error");
PyDict_SetItemString(d, "tsapi_error", teamtrack_tsapi_error);
/* Don't decref teamtrack_tsapi_error since we continue to own it through a
global variable. */
/* Make teamtrack.table (a mapping from table name to table id). */
teamtrack_make_map(teamtrack_table, sizeof(teamtrack_table)
/ sizeof(teamtrack_table[0]), "table", d);
/* Make teamtrack.field_type (a mapping from field type to table id). */
teamtrack_make_map(teamtrack_field_type, sizeof(teamtrack_field_type)
/ sizeof(teamtrack_field_type[0]),
"field_type", d);
/* Make teamtrack.feature (a mapping from feature name to 1). */
teamtrack_make_map(teamtrack_feature, sizeof(teamtrack_feature)
/ sizeof(teamtrack_feature[0]),
"feature", d);
/* Check for errors that occurred during module initialization. */
if (PyErr_Occurred()) {
Py_FatalError("Can't initialise teamtrack module.");
}
#if defined(WIN32)
if (TSInitializeWinsock() != TS_OK) {
Py_FatalError("Can't initialise Winsock.");
}
#endif /* defined(WIN32) */
}
/* Two versions of this Python module will be built: one to support
TeamTrack 4.5 and the other to support TeamTrack 5.0. Each requires
an appropriately named initialization function. */
extern "C" void TEAMTRACK_EXPORTED
initteamtrack45() {
/* Create the "teamtrack45" module. */
PyObject *m = Py_InitModule("teamtrack45", teamtrack_methods);
teamtrack_init(m);
}
extern "C" void TEAMTRACK_EXPORTED
initteamtrack50() {
/* Create the "teamtrack50" module. */
PyObject *m = Py_InitModule("teamtrack50", teamtrack_methods);
teamtrack_init(m);
}
/* A. REFERENCES
[GDR 2000-08-08] "Python interface to TeamTrack: design"; Gareth
Rees; Ravenbrook Limited; 2000-08-08;
.
B. DOCUMENT HISTORY
2000-08-02 GDR Created.
2000-08-07 GDR Function ttrack.connect() passes arguments correctly to
TSServer::Connect. New function ttrack.read_case() for reading a case as a
string. Module initialization calls TSInitializeWinsock().
2000-08-08 GDR New dictionary ttrack.tables, which maps TeamShare table name
to table id. Call to TSInitializeWinsock in ttrackmodule.cpp wrapped with
#ifdef WIN32.
2000-08-23 GDR ttrack_init isn't so paranoid about its error checking --
Python API functions returns 0 if any of their arguments are 0 so you can
generally do a bunch of Python API calls without checking all the return
values, and then call PyErr_Occurred to see if anything went wrong. This
makes the code more readable at the cost of having to check the
justification given above.
2000-09-07 GDR Added teamtrack.field_type map. Wrote function
teamtrack_make_map to build these maps.
2001-06-12 GDR Made it work with TeamTrack 5 by not using the
deprecated error code TS_INVALID_DATA_TYPE.
2001-07-02 GDR Added initialization functions initteamtrack45
and initteamtrack50 to support multiple TeamTrack versions.
2001-10-29 GDR Added names for error codes from TeamTrack 5.0.
2002-01-08 GDR Added names for tables from TeamTrack 5.0.
C. COPYRIGHT AND LICENCE
This file is copyright (c) 2001 Perforce Software, Inc. 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.
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
HOLDERS AND 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.
$Id: //info.ravenbrook.com/project/p4dti/branch/2001-11-22/bugzilla-parameters/code/python-teamtrack-interface/teamtrack-module.cpp#4 $ */