REST1

Rt - Python interface to Request Tracker API.

Description of Request Tracker REST API: http://requesttracker.wikia.com/wiki/REST

Provided functionality:

  • login to RT

  • logout

  • getting, creating and editing tickets

  • getting attachments

  • getting history of ticket

  • replying to ticket requestors

  • adding comments

  • getting and editing ticket links

  • searching

  • providing lists of last updated tickets

  • providing tickets with new correspondence

  • merging tickets

  • take tickets

  • steal tickets

  • untake tickets

rt.rest1.DEFAULT_QUEUE = 'General'

Default queue used.

class rt.rest1.Rt(url: str, default_login: str | None = None, default_password: str | None = None, cookies: dict | None = None, proxy: str | None = None, default_queue: str = 'General', skip_login: bool = False, verify_cert: str | bool | None = True, http_auth: AuthBase | None = None)[source]

API for Request Tracker according to http://requesttracker.wikia.com/wiki/REST.

Interface is based on REST architecture, which is based on HTTP/1.1 protocol. This module is therefore mainly sending and parsing special HTTP messages.

Note

Use only ASCII LF as newline (\\n). Time is returned in UTC. All strings returned are encoded in UTF-8 and the same is expected as input for string values.

RE_PATTERNS: Dict[str, Pattern] = {'attachments_list_pattern': re.compile('[^0-9]*(\\d+): (.+) \\((.+) / (.+)\\),?$'), 'attachments_pattern': re.compile('Attachments:'), 'bad_request_pattern': re.compile('.* 400 Bad Request$'), 'content_pattern': re.compile('Content:'), 'content_pattern_bytes': re.compile(b'Content:'), 'created_link_pattern': re.compile('.* Created link '), 'credentials_required_pattern': re.compile('.* 401 Credentials required$'), 'deleted_link_pattern': re.compile('.* Deleted link '), 'does_not_exist_pattern': re.compile('^# (?:Queue|User|Ticket) \\w* does not exist\\.$'), 'does_not_exist_pattern_bytes': re.compile(b'^# (?:Queue|User|Ticket) \\w* does not exist\\.$'), 'headers_pattern_bytes': re.compile(b'Headers:'), 'invalid_attachment_pattern_bytes': re.compile(b'^# Invalid attachment id: \\d+$'), 'links_updated_pattern': re.compile('^# Links for ticket [0-9]+ updated.$'), 'merge_successful_pattern': re.compile('^# Merge completed.|^Merge Successful$'), 'not_allowed_pattern': re.compile('^# You are not allowed to'), 'not_related_pattern': re.compile('^# Transaction \\d+ is not related to Ticket \\d+'), 'queue_pattern': re.compile('^# Queue (\\w*) (?:updated|created)\\.$'), 'requestors_pattern': re.compile('Requestors:'), 'status_pattern': re.compile('^\\S+ (\\d{3}) '), 'syntax_error_pattern': re.compile('.* 409 Syntax Error$'), 'ticket_created_pattern': re.compile('^# Ticket ([0-9]+) created\\.$'), 'update_pattern': re.compile('^# Ticket [0-9]+ updated.$'), 'user_pattern': re.compile('^# User ([0-9]*) (?:updated|created)\\.$')}
__init__(url: str, default_login: str | None = None, default_password: str | None = None, cookies: dict | None = None, proxy: str | None = None, default_queue: str = 'General', skip_login: bool = False, verify_cert: str | bool | None = True, http_auth: AuthBase | None = None) None[source]

API initialization.

Parameters:
  • url – Base URL for Request Tracker API. E.g.: http://tracker.example.com/REST/1.0/

  • default_login – Default RT login used by self.login if no other credentials are provided

  • default_password – Default RT password

  • proxy – Proxy server (string with http://user:password@host/ syntax)

  • default_queue – Default RT queue

  • skip_login – Set this option True when HTTP Basic authentication credentials for RT are in .netrc file. You do not need to call login, because it is managed by requests library instantly.

  • http_auth – Specify a http authentication instance, e.g. HTTPBasicAuth(), HTTPDigestAuth(), etc. to be used for authenticating to RT

comment(ticket_id: str | int, text: str = '', cc: str = '', bcc: str = '', content_type: str = 'text/plain', files: List[Tuple[str, IO, str | None]] | None = None) bool[source]

Adds comment to the given ticket.

Form of message according to documentation:

id: <ticket-id>
Action: comment
Text: the text comment
      second line starts with the same indentation as first
Attachment: an attachment filename/path

Example:

>>> tracker = Rt('http://tracker.example.com/REST/1.0/', 'rt-username', 'top-secret')
>>> attachment_name = sys.argv[1]
>>> message_text = ' '.join(sys.argv[2:])
>>> ret = tracker.comment(ticket_id, text=message_text,
... files=[(attachment_name, open(attachment_name, 'rb'))])
>>> if not ret:
...     print('Error: could not send attachment', file=sys.stderr)
...     exit(1)
Parameters:
  • ticket_id – ID of ticket to which comment belongs

  • text – Content of comment

  • content_type – Content type of comment, default to text/plain

  • files – Files to attach as multipart/form-data List of 2/3 tuples: (filename, file-like object, [content type])

Returns:

True

Operation was successful

False

Sending failed (status code != 200)

Raises:

BadRequestError – When ticket does not exist

create_queue(Name: str, **kwargs: Any) int[source]

Create queue (undocumented API feature).

Parameters:
  • Name – Queue name (required)

  • kwargs – Optional fields to set (see edit_queue)

Returns:

ID of new queue or False when create fails

Raises:
create_ticket(Queue: str | object | None = None, files: List[Tuple[str, IO, str | None]] | None = None, **kwargs: Any) int[source]

Create new ticket and set given parameters.

Example of message sended to http://tracker.example.com/REST/1.0/ticket/new:

content=id: ticket/new
Queue: General
Owner: Nobody
Requestor: somebody@example.com
Subject: Ticket created through REST API
Text: Lorem Ipsum

In case of success returned message has this form:

RT/3.8.7 200 Ok

# Ticket 123456 created.
# Ticket 123456 updated.

Otherwise:

RT/3.8.7 200 Ok

# Required: id, Queue
  • list of some key, value pairs, probably default values.

Parameters:
  • Queue – Queue where to create ticket

  • files – Files to attach as multipart/form-data List of 2/3 tuples: (filename, file-like object, [content type])

  • kwargs

    Other arguments possible to set:

    Requestor, Subject, Cc, AdminCc, Owner, Status, Priority, InitialPriority, FinalPriority, TimeEstimated, Starts, Due, Text,… (according to RT fields)

    Custom fields CF.{<CustomFieldName>} could be set with keywords CF_CustomFieldName.

Returns:

ID of new ticket or -1, if creating failed

create_user(Name: str, EmailAddress: str, **kwargs: Any) int | bool[source]

Create user (undocumented API feature).

Parameters:
  • Name – User name (login for privileged, required)

  • EmailAddress – Email address (required)

  • kwargs – Optional fields to set (see edit_user)

Returns:

ID of new user or False when create fails

Raises:

Creates or deletes a link between the specified tickets (undocumented API feature).

Parameters:
  • ticket_id – ID of ticket to edit

  • link_name – Name of link to edit (DependsOn, DependedOnBy, RefersTo, ReferredToBy, HasMember or MemberOf)

  • link_value – Either ticker ID or external link.

  • delete – if True the link is deleted instead of created

Returns:

True

Operation was successful

False

Ticket with given ID does not exist or link to delete is not found

Raises:

InvalidUseError – When none or more then one links are specified. Also when wrong link name is used.

edit_queue(queue_id: str | int, **kwargs: Any) str | bool[source]

Edit queue (undocumented API feature).

Parameters:
  • queue_id – Identification of queue by name (str) or ID (int)

  • kwargs

    Other fields to edit from the following list:

    • Name

    • Description

    • CorrespondAddress

    • CommentAddress

    • InitialPriority

    • FinalPriority

    • DefaultDueIn

Returns:

ID or name of edited queue or False when edit fails

Raises:
edit_ticket(ticket_id: str | int, **kwargs: Any) bool[source]

Edit ticket values.

Parameters:
  • ticket_id – ID of ticket to edit

  • kwargs

    Other arguments possible to set:

    Requestors, Subject, Cc, AdminCc, Owner, Status, Priority, InitialPriority, FinalPriority, TimeEstimated, Starts, Due, Text,… (according to RT fields)

    Custom fields CF.{<CustomFieldName>} could be set with keywords CF_CustomFieldName.

Returns:

True

Operation was successful

False

Ticket with given ID does not exist or unknown parameter was set (in this case all other valid fields are changed)

Edit ticket links.

Warning

This method is deprecated in favour of edit_link method, because there exists bug in RT 3.8 REST API causing mapping created links to ticket/1. The only drawback is that edit_link cannot process multiple links all at once.

Parameters:
  • ticket_id – ID of ticket to edit

  • kwargs – Other arguments possible to set: DependsOn, DependedOnBy, RefersTo, ReferredToBy, Members, MemberOf. Each value should be either ticker ID or external link. Int types are converted. Use empty string as value to delete existing link.

Returns:

True

Operation was successful

False

Ticket with given ID does not exist or unknown parameter was set (in this case all other valid fields are changed)

edit_user(user_id: str | int, **kwargs: Any) int | bool[source]

Edit user profile (undocumented API feature).

Parameters:
  • user_id – Identification of user by username (str) or user ID (int)

  • kwargs

    Other fields to edit from the following list:

    • Name

    • Password

    • EmailAddress

    • RealName

    • NickName

    • Gecos

    • Organization

    • Address1

    • Address2

    • City

    • State

    • Zip

    • Country

    • HomePhone

    • WorkPhone

    • MobilePhone

    • PagerPhone

    • ContactInfo

    • Comments

    • Signature

    • Lang

    • EmailEncoding

    • WebEncoding

    • ExternalContactInfoId

    • ContactInfoSystem

    • ExternalAuthId

    • AuthSystem

    • Privileged

    • Disabled

Returns:

ID of edited user or False when edit fails

Raises:
get_attachment(ticket_id: str | int, attachment_id: str | int) dict | None[source]

Get attachment.

Parameters:
  • ticket_id – ID of ticket

  • attachment_id – ID of attachment for obtain

Returns:

Attachment as dictionary with these keys:

  • Transaction

  • ContentType

  • Parent

  • Creator

  • Created

  • Filename

  • Content (bytes type)

  • Headers

  • MessageId

  • ContentEncoding

  • id

  • Subject

All these fields are strings, just ‘Headers’ holds another dictionary with attachment headers as strings e.g.:

  • Delivered-To

  • From

  • Return-Path

  • Content-Length

  • To

  • X-Seznam-User

  • X-QM-Mark

  • Domainkey-Signature

  • RT-Message-ID

  • X-RT-Incoming-Encryption

  • X-Original-To

  • Message-ID

  • X-Spam-Status

  • In-Reply-To

  • Date

  • Received

  • X-Country

  • X-Spam-Checker-Version

  • X-Abuse

  • MIME-Version

  • Content-Type

  • Subject

Warning

Content-Length parameter is set after opening ticket in web interface!

Set of headers available depends on mailservers sending emails not on Request Tracker!

Returns None if ticket or attachment does not exist.

Raises:

UnexpectedMessageFormatError – Unexpected format of returned message.

get_attachment_content(ticket_id: str | int, attachment_id: str | int) bytes | None[source]

Get content of attachment without headers.

This function is necessary to use for binary attachment, as it can contain \\n chars, which would disrupt parsing of message if get_attachment() is used.

Format of message:

RT/3.8.7 200 Ok\n\nStart of the content...End of the content\n\n\n
Parameters:
  • ticket_id – ID of ticket

  • attachment_id – ID of attachment

Returns: Bytes with content of attachment or None if ticket or

attachment does not exist.

get_attachments(ticket_id: str | int) List[Tuple[str, str, str, str]] | None[source]

Get attachment list for a given ticket.

Parameters:

ticket_id – ID of ticket

Returns:

List of tuples for attachments belonging to given ticket. Tuple format: (id, name, content_type, size) Returns None if ticket does not exist.

get_attachments_ids(ticket_id: str | int) List[int] | None[source]

Get IDs of attachments for given ticket.

Parameters:

ticket_id – ID of ticket

Returns:

List of IDs (type int) of attachments belonging to given ticket. Returns None if ticket does not exist.

get_history(ticket_id: str | int, transaction_id: str | int | None = None) List[dict] | None[source]

Get set of history items.

Parameters:
  • ticket_id – ID of ticket

  • transaction_id – If set to None, all history items are returned, if set to ID of valid transaction just one history item is returned

Returns:

List of history items ordered increasingly by time of event. Each history item is dictionary with following keys:

Description, Creator, Data, Created, TimeTaken, NewValue, Content, Field, OldValue, Ticket, Type, id, Attachments

All these fields are strings, just ‘Attachments’ holds list of pairs (attachment_id,filename_with_size).

Returns None if ticket or transaction does not exist.

Raises:

UnexpectedMessageFormatError – Unexpected format of returned message.

Gets the ticket links for a single ticket.

Parameters:

ticket_id – ticket ID

Returns:

Links as lists of strings in dictionary with these keys (just those which are defined):

  • id

  • Members

  • MemberOf

  • RefersTo

  • ReferredToBy

  • DependsOn

  • DependedOnBy

None is returned if ticket does not exist.

Raises:

UnexpectedMessageFormatError – In case that returned status code is not 200

get_queue(queue_id: str | int) Dict[str, str] | None[source]

Get queue details.

Parameters:

queue_id – Identification of queue by name (str) or queue ID (int)

Returns:

Queue details as strings in dictionary with these keys if queue exists (otherwise None):

  • id

  • Name

  • Description

  • CorrespondAddress

  • CommentAddress

  • InitialPriority

  • FinalPriority

  • DefaultDueIn

Raises:

UnexpectedMessageFormatError – In case that returned status code is not 200

get_short_history(ticket_id: str | int) List[Tuple[int, str]] | None[source]

Get set of short history items.

Parameters:

ticket_id – ID of ticket

Returns:

List of history items ordered increasingly by time of event. Each history item is a tuple containing (id, Description). Returns None if ticket does not exist.

get_ticket(ticket_id: str | int) dict | None[source]

Fetch ticket by its ID.

Parameters:

ticket_id – ID of demanded ticket

Returns:

Dictionary with key, value pairs for ticket with ticket_id or None if ticket does not exist. List of keys:

  • id

  • numerical_id

  • Queue

  • Owner

  • Creator

  • Subject

  • Status

  • Priority

  • InitialPriority

  • FinalPriority

  • Requestors

  • Cc

  • AdminCc

  • Created

  • Starts

  • Started

  • Due

  • Resolved

  • Told

  • TimeEstimated

  • TimeWorked

  • TimeLeft

Raises:

UnexpectedMessageFormatError – Unexpected format of returned message.

get_user(user_id: str | int) Dict[str, str] | None[source]

Get user details.

Parameters:

user_id – Identification of user by username (str) or user ID (int)

Returns:

User details as strings in dictionary with these keys for RT users:

  • Lang

  • RealName

  • Privileged

  • Disabled

  • Gecos

  • EmailAddress

  • Password

  • id

  • Name

Or these keys for external users (e.g. Requestors replying to email from RT:

  • RealName

  • Disabled

  • EmailAddress

  • Password

  • id

  • Name

None is returned if user does not exist.

Raises:

UnexpectedMessageFormatError – In case that returned status code is not 200

last_updated(since: str, queue: str | object | None = None) List[dict][source]

Obtains tickets changed after given date.

Parameters:
  • since – Date as string in form ‘2011-02-24’

  • queue – Queue where to search

Returns:

List of tickets with LastUpdated parameter later than since ordered in decreasing order by LastUpdated. Each tickets is dictionary, the same as in get_ticket().

login(login: str | None = None, password: str | None = None) bool[source]

Login with default or supplied credentials.

Note

Calling this method is not necessary when HTTP basic or HTTP digest_auth authentication is used and RT accepts it as external authentication method, because the login in this case is done transparently by requests module. Anyway this method can be useful to check whether given credentials are valid or not.

Parameters:
  • login – Username used for RT, if not supplied together with password default_login and default_password are used instead

  • password – Similarly as login

Returns:

True

Successful login

False

Otherwise

Raises:

AuthorizationError – In case that credentials are not supplied neither during inicialization or call of this method.

logout() bool[source]

Logout of user.

Returns:

True

Successful logout

False

Logout failed (mainly because user was not login)

merge_ticket(ticket_id: str | int, into_id: str | int) bool[source]

Merge ticket into another (undocumented API feature).

Parameters:
  • ticket_id – ID of ticket to be merged

  • into_id – ID of destination ticket

Returns:

True

Operation was successful

False

Either origin or destination ticket does not exist or user does not have ModifyTicket permission.

new_correspondence(queue: str | object | None = None) List[dict][source]

Obtains tickets changed by other users than the system one.

Parameters:

queue – Queue where to search

Returns:

List of tickets which were last updated by other user than the system one ordered in decreasing order by LastUpdated. Each ticket is dictionary, the same as in get_ticket().

reply(ticket_id: str | int, text: str = '', cc: str = '', bcc: str = '', content_type: str = 'text/plain', files: List[Tuple[str, IO, str | None]] | None = None) bool[source]

Sends email message to the contacts in Requestors field of given ticket with subject as is set in Subject field.

Form of message according to documentation:

id: <ticket-id>
Action: correspond
Text: the text comment
      second line starts with the same indentation as first
Cc: <...>
Bcc: <...>
TimeWorked: <...>
Attachment: an attachment filename/path
Parameters:
  • ticket_id – ID of ticket to which message belongs

  • text – Content of email message

  • content_type – Content type of email message, default to text/plain

  • cc – Carbon copy just for this reply

  • bcc – Blind carbon copy just for this reply

  • files – Files to attach as multipart/form-data List of 2/3 tuples: (filename, file-like object, [content type])

Returns:

True

Operation was successful

False

Sending failed (status code != 200)

Raises:

BadRequestError – When ticket does not exist

search(Queue: str | object | None = None, order: str | None = None, raw_query: str | None = None, Format: str = 'l', Fields: List[str] | None = None, **kwargs: Any) List[dict][source]

Search arbitrary needles in given fields and queue.

Example:

>>> tracker = Rt('http://tracker.example.com/REST/1.0/', 'rt-username', 'top-secret')
>>> tracker.login()
>>> tickets = tracker.search(CF_Domain='example.com', Subject__like='warning')
>>> tickets = tracker.search(Queue='General', order='Status', raw_query="id='1'+OR+id='2'+OR+id='3'")
Parameters:
  • Queue – Queue where to search. If you wish to search across all of your queues, pass the ALL_QUEUES object as the argument.

  • order – Name of field sorting result list, for descending order put - before the field name. E.g. -Created will put the newest tickets at the beginning

  • raw_query – A raw query to provide to RT if you know what you are doing. You may still pass Queue and order kwargs, so use these instead of including them in the raw query. You can refer to the RT query builder. If passing raw_query, all other \**kwargs will be ignored.

  • Format

    Format of the query:

    • i: only id fields are populated

    • s: only id and subject fields are populated

    • l: multi-line format, all fields are populated

  • kwargs

    Other arguments possible to set if not passing raw_query:

    Requestors, Subject, Cc, AdminCc, Owner, Status, Priority, InitialPriority, FinalPriority, TimeEstimated, Starts, Due, Text,… (according to RT fields)

    Custom fields CF.{<CustomFieldName>} could be set with keywords CF_CustomFieldName.

    To alter lookup operators you can append one of the following endings to each keyword:

    __exact    for operator = (default)
    __notexact for operator !=
    __gt       for operator >
    __lt       for operator <
    __like     for operator LIKE
    __notlike  for operator NOT LIKE
    __is       for operator IS
    __isnot    for operator IS NOT
    

    Setting values to keywords constrain search result to the tickets satisfying all of them.

Returns:

List of matching tickets. Each ticket is the same dictionary as in get_ticket()\.

Raises:

UnexpectedMessageFormatError: Unexpected format of returned message. InvalidQueryError: If raw query is malformed

static split_header(line: str) Sequence[str][source]

Split a header line into field name and field value.

Note that custom fields may contain colons inside the curly braces, so we need a special test for them.

Parameters:

line – A message line to be split.

Returns:

(Field name, field value) tuple.

steal(ticket_id: str | int) bool[source]

Steal ticket.

Parameters:

ticket_id – ID of ticket to be merged

Returns:

True

Operation was successful

False

Either the ticket does not exist or user does not have StealTicket permission.

take(ticket_id: str | int) bool[source]

Take ticket.

Parameters:

ticket_id – ID of ticket to be merged

Returns:

True

Operation was successful

False

Either the ticket does not exist or user does not have TakeTicket permission.

untake(ticket_id: str | int) bool[source]

Untake ticket.

Parameters:

ticket_id – ID of ticket to be merged

Returns:

True

Operation was successful

False

Either the ticket does not exist or user does not own the ticket.