/* Perforce Defect Tracking Integration Project
TEAMTRACK-SERVER.CPP -- PYTHON WRAPPER ROUND TSSERVER CLASS
Gareth Rees, Ravenbrook Limited, 2000-08-08
1. INTRODUCTION
This file implements a Python interface to TSServer objects
(connections to TeamTrack servers). Each TSServer object is
represented in Python by an object belonging to the teamtrack_server
type.
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 "teamtrack-record.h"
#include "TSField.C"
/* teamtrack_server_new(s, delete_p) returns a Python wrapper for the TSServer
object s, or NULL if there is an error. Iff delete_p is true, then the
TSServer object will be deleted when the Python wrapper is deleted. */
PyObject *
teamtrack_server_new(TSServer *s, int delete_p) {
teamtrack_server *self =
PyObject_NEW(teamtrack_server, &teamtrack_server_type);
if (self == NULL) {
return NULL;
}
self->s = s;
self->delete_p = delete_p;
/* Try to work out which table contains the cases; see [GDR
2000-08-08]. If this doesn't work we will assume the table id is
given by TS_TBLID_CASES and we'll leave the name as NULL, meaning
"TS_CASES". */
self->case_table_id = TS_TBLID_CASES;
self->case_table_name = NULL;
TSRecord r(TS_TBLID_TABLES, s);
if (s->ReadRecord(&r, TS_TBLID_CASES) != TS_OK
&& s->ReadRecordWithWhere(&r, "TS_SNAME LIKE 'Issue'") == TS_OK) {
TSField *f = r.fieldList.FindFieldByName("ID", r.fieldType);
TSField *g = r.fieldList.FindFieldByName("DBNAME", r.fieldType);
if (f && f->dataType == TS_DATATYPE_INTEGER
&& g && g->dataType == TS_DATATYPE_STRING) {
self->case_table_id = f->intValue;
self->case_table_name = new TSString(g->charValue);
}
}
return (PyObject *)self;
}
/* teamtrack_record_list_to_python_list(tts, ts_records) returns a Python list
containing a teamtrack_record object for each TSRecord object in the
ts_records argument. The tts argument is the teamtrack_server object to
which the record list belongs. */
static PyObject *
teamtrack_record_list_to_python_list(teamtrack_server *tts, TSRecordList *ts_records) {
int n_records = ts_records->Length();
PyObject *py_records = PyList_New(n_records);
if (py_records == NULL) {
return NULL;
}
int i; /* Position in Python list */
TSPosition *p; /* Position in TSRecordList */
for (i = 0, p = ts_records->GetFirst();
i < n_records && p;
++i, p = ts_records->GetNext(p)) {
TSRecord *ts_record = ts_records->GetAt(p);
if (ts_record == NULL) {
/* Can't just 'raise' here, since py_records would leak. */
teamtrack_set_error(tts->s);
break;
}
/* I believe it's safe to pass ownership of the TSRecord objects to Python,
because the TSRecordList class doesn't actually delete the records in it
unless you call TSList::EmptyAndDestroyList. See TSList.C. */
PyObject *py_record = teamtrack_record_new(ts_record, tts, 1);
if (py_record == NULL) {
break;
}
PyList_SetItem(py_records, i, py_record);
/* No need to decref py_record since PyList_SetItem doesn't incref it */
}
if (PyErr_Occurred()) {
Py_DECREF(py_records); /* Delete py_records. */
return NULL;
}
return py_records;
}
/* Instance methods */
static PyObject *
teamtrack_server_case_table_id(PyObject *self, PyObject *args) {
teamtrack_check_type(teamtrack_server, self, NULL);
teamtrack_server *tts = (teamtrack_server *)self;
return PyInt_FromLong((long)tts->case_table_id);
}
static PyObject *
teamtrack_server_case_table_name(PyObject *self, PyObject *args) {
teamtrack_check_type(teamtrack_server, self, NULL);
teamtrack_server *tts = (teamtrack_server *)self;
if (tts->case_table_name != NULL) {
return PyString_FromString(tts->case_table_name->GetBuffer());
} else {
return PyString_FromString("TS_CASES");
}
}
static PyObject *
teamtrack_server_delete_record(PyObject *self, PyObject *args) {
teamtrack_check_type(teamtrack_server, self, NULL);
teamtrack_server *tts = (teamtrack_server *)self;
int table_id, record_id;
if (!PyArg_ParseTuple(args, "ii", &table_id, &record_id)) {
return NULL;
}
int rc = tts->s->DeleteRecord(table_id, record_id);
teamtrack_try(rc, tts->s, NULL);
Py_INCREF(Py_None); /* It will be on Python's stack when returned. */
return Py_None;
}
static PyObject *
teamtrack_server_new_record(PyObject *self, PyObject *args) {
teamtrack_check_type(teamtrack_server, self, NULL);
teamtrack_server *tts = (teamtrack_server *)self;
int table_id;
if (!PyArg_ParseTuple(args, "i", &table_id)) {
return NULL;
}
TSRecord *r = new TSRecord(table_id, tts->s);
teamtrack_assert(r, NULL);
return teamtrack_record_new(r, tts, 1);
}
static PyObject *
teamtrack_server_query(PyObject *self, PyObject *args) {
teamtrack_check_type(teamtrack_server, self, NULL);
teamtrack_server *tts = (teamtrack_server *)self;
int table_id;
char *where_clause;
if (!PyArg_ParseTuple(args, "is", &table_id, &where_clause)) {
return NULL;
}
TSRecordList ts_records;
int rc = tts->s->ReadRecordListWithWhere(&ts_records, table_id,
where_clause);
teamtrack_try(rc, tts->s, NULL);
return teamtrack_record_list_to_python_list(tts, &ts_records);
}
static PyObject *
teamtrack_server_read_record(PyObject *self, PyObject *args) {
teamtrack_check_type(teamtrack_server, self, NULL);
teamtrack_server *tts = (teamtrack_server *)self;
int table_id, record_id;
if (!PyArg_ParseTuple(args, "ii", &table_id, &record_id)) {
return NULL;
}
TSRecord *r = new TSRecord(table_id, tts->s);
teamtrack_assert(r, NULL);
if (tts->s->ReadRecord(r, record_id) != TS_OK) {
teamtrack_set_error(tts->s);
delete r;
return NULL;
}
return teamtrack_record_new(r, tts, 1);
}
static PyObject *
teamtrack_server_read_state_list(PyObject *self, PyObject *args) {
teamtrack_check_type(teamtrack_server, self, NULL);
teamtrack_server *tts = (teamtrack_server *)self;
int workflow_id;
int include_parent = 0;
if (!PyArg_ParseTuple(args, "i|i", &workflow_id, &include_parent)) {
return NULL;
}
TSRecordList ts_records;
int rc = tts->s->ReadStateList(&ts_records, workflow_id, include_parent);
teamtrack_try(rc, tts->s, NULL);
return teamtrack_record_list_to_python_list(tts, &ts_records);
}
static PyObject *
teamtrack_server_read_transition_list(PyObject *self, PyObject *args) {
teamtrack_check_type(teamtrack_server, self, NULL);
teamtrack_server *tts = (teamtrack_server *)self;
int project_id;
if (!PyArg_ParseTuple(args, "i", &project_id)) {
return NULL;
}
TSRecordList ts_records;
int rc = tts->s->ReadTransitionList(&ts_records, project_id);
teamtrack_try(rc, tts->s, NULL);
return teamtrack_record_list_to_python_list(tts, &ts_records);
}
static struct PyMethodDef teamtrack_server_methods[] = {
{ "case_table_id", teamtrack_server_case_table_id, 1 },
{ "case_table_name", teamtrack_server_case_table_name, 1 },
{ "delete_record", teamtrack_server_delete_record, 1 },
{ "new_record", teamtrack_server_new_record, 1 },
{ "query", teamtrack_server_query, 1 },
{ "read_record", teamtrack_server_read_record, 1 },
{ "read_state_list", teamtrack_server_read_state_list, 1 },
{ "read_transition_list", teamtrack_server_read_transition_list, 1 },
{ NULL, NULL, 0 } /* End of methods */
};
/* Basic Python operators */
static void
teamtrack_server_dealloc(PyObject *self) {
if (!teamtrack_server_p(self)) {
Py_FatalError("teamtrack_server_dealloc called on object that's not "
"a teamtrack_server.");
}
teamtrack_server *tts = (teamtrack_server *)self;
if (tts->case_table_name != NULL) {
delete tts->case_table_name;
}
if (tts->delete_p) {
delete tts->s;
}
PyMem_DEL(self);
}
static PyObject *
teamtrack_server_repr(PyObject *self) {
teamtrack_check_type(teamtrack_server, self, NULL);
teamtrack_server *tts = (teamtrack_server *)self;
return PyString_FromString("");
}
static PyObject *
teamtrack_server_getattr(PyObject *self, char *name) {
teamtrack_check_type(teamtrack_server, self, NULL);
return Py_FindMethod(teamtrack_server_methods, self, name);
}
/* Create the teamtrack.server class */
PyTypeObject teamtrack_server_type = {
/* Type header */
PyObject_HEAD_INIT(&PyType_Type)
0, /* ob_size */
"server", /* tp_name */
sizeof(teamtrack_server), /* tp_basicsize */
0, /* tp_itemsize */
/* Basic methods */
teamtrack_server_dealloc,
NULL, /* print */
teamtrack_server_getattr,
NULL, /* setatts */
NULL, /* compare */
teamtrack_server_repr,
/* Type categories */
NULL, /* number operators */
NULL, /* sequence operators */
NULL, /* mapping operators */
/* Other methods are NULL: see Python's object.h for details */
};
/* A. REFERENCES
[GDR 2000-08-08] "Python interface to TeamTrack: design"; Gareth
Rees; Ravenbrook Limited; 2000-08-08;
.
B. DOCUMENT HISTORY
2000-08-08 GDR Created.
2000-08-29 GDR Changed "tTrack" to "TeamTrack" throughout.
1976 2000-08-31 GDR Removed obsolete read_case method from the server class.
Added new_record method to the server class.
2000-08-31 GDR Added delete_record method to server class.
2000-09-06 GDR Added add_field method to teamtrack server class.
2000-09-07 GDR Moved add_field() method implementation from server class to
record class.
2000-10-13 GDR Added read_state_list and read_transition_list to the
teamtrack server object. The former returns a list of available states in a
workflow. The latter returns a list of available transitions in a project.
It is more maintainable (and more likely to be correct) to use these
functions to query TeamTrack's workflow model than to try to duplicate
TeamTrack's logic using only the schema documentation.
2001-06-26 GDR Added methods case_table_name() and case_table_id().
2001-06-28 GDR The case table id and name are now worked out by the
procedure documented in [GDR 2000-08-08].
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-server.cpp#3 $ */