/* 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 $ */