Issue

Supported by Redmine starting from version 1.0

Manager

All operations on the Issue resource are provided by its manager. To get access to it you have to call redmine.issue where redmine is a configured redmine object. See the Configuration about how to configure redmine object.

Create methods

create

redminelib.managers.ResourceManager.create(**fields)

Creates new Issue resource with given fields and saves it to the Redmine.

Parameters:
  • project_id (int or string) – (required). Id or identifier of issue’s project.

  • subject (string) – (required). Issue subject.

  • tracker_id (int) – (optional). Issue tracker id.

  • description (string) – (optional). Issue description.

  • status_id (int) – (optional). Issue status id.

  • priority_id (int) – (optional). Issue priority id.

  • category_id (int) – (optional). Issue category id.

  • fixed_version_id (int) – (optional). Issue version id.

  • is_private (bool) – (optional). Whether issue is private.

  • assigned_to_id (int) – (optional). Issue will be assigned to this user id.

  • watcher_user_ids (list) – (optional). User ids watching this issue.

  • parent_issue_id (int) – (optional). Parent issue id.

  • start_date (string or date object) – (optional). Issue start date.

  • due_date (string or date object) – (optional). Issue end date.

  • estimated_hours (int) – (optional). Issue estimated hours.

  • done_ratio (int) – (optional). Issue done ratio.

  • custom_fields (list) – (optional). Custom fields as [{‘id’: 1, ‘value’: ‘foo’}].

  • uploads (list) – (optional). Uploads as [{'': ''}, ...], accepted keys are:

    • path (required). Absolute file path or file-like object that should be uploaded.

    • filename (optional). Name of the file after upload.

    • description (optional). Description of the file.

    • content_type (optional). Content type of the file.

Returns:

Resource object

>>> from io import BytesIO
>>> issue = redmine.issue.create(
...     project_id='vacation',
...     subject='Vacation',
...     tracker_id=8,
...     description='foo',
...     status_id=3,
...     priority_id=7,
...     assigned_to_id=123,
...     watcher_user_ids=[123],
...     parent_issue_id=345,
...     start_date=datetime.date(2014, 1, 1),
...     due_date=datetime.date(2014, 2, 1),
...     estimated_hours=4,
...     done_ratio=40,
...     custom_fields=[{'id': 1, 'value': 'foo'}, {'id': 2, 'value': 'bar'}],
...     uploads=[{'path': '/absolute/path/to/file'}, {'path': BytesIO(b'I am content of file 2')}]
... )
>>> issue
<redminelib.resources.Issue #123 "Vacation">

new

redminelib.managers.ResourceManager.new()

Creates new empty Issue resource but saves it to the Redmine only when save() is called, also calls pre_create() and post_create() methods of the Resource object. Valid attributes are the same as for create() method above.

Returns:

Resource object

>>> issue = redmine.issue.new()
>>> issue.project_id = 'vacation'
>>> issue.subject = 'Vacation'
>>> issue.tracker_id = 8
>>> issue.description = 'foo'
>>> issue.status_id = 3
>>> issue.priority_id = 7
>>> issue.assigned_to_id = 123
>>> issue.watcher_user_ids = [123]
>>> issue.parent_issue_id = 345
>>> issue.start_date = datetime.date(2014, 1, 1)
>>> issue.due_date = datetime.date(2014, 2, 1)
>>> issue.estimated_hours = 4
>>> issue.done_ratio = 40
>>> issue.custom_fields = [{'id': 1, 'value': 'foo'}, {'id': 2, 'value': 'bar'}]
>>> issue.uploads = [{'path': '/absolute/path/to/file'}, {'path': '/absolute/path/to/file2'}]
>>> issue.save()
<redminelib.resources.Issue #123 "Vacation">

Read methods

get

redminelib.managers.ResourceManager.get(resource_id, **params)

Returns single Issue resource from Redmine by its id.

Parameters:
  • resource_id (int) – (required). Id of the issue.

  • include (list) – (optional). Fetches associated data in one call. Accepted values:

    • children

    • attachments

    • relations

    • changesets

    • journals

    • watchers (requires Redmine >= 2.3.0)

    • allowed_statuses (requires Redmine >= 5.0.0)

Returns:

Resource object

>>> issue = redmine.issue.get(34441, include=['children', 'journals', 'watchers'])
>>> issue
<redminelib.resources.Issue #34441 "Vacation">

Hint

Issue resource object provides you with on demand includes. On demand includes are the other resource objects wrapped in a ResourceSet which are associated with an Issue resource object. Keep in mind that on demand includes are retrieved in a separate request, that means that if the speed is important it is recommended to use get() method with include keyword argument. On demand includes provided by the Issue resource object are the same as in the get() method above:

>>> issue = redmine.issue.get(34441)
>>> issue.journals
<redminelib.resultsets.ResourceSet object with IssueJournal resources>

Hint

Issue resource object provides you with some relations. Relations are the other resource objects wrapped in a ResourceSet which are somehow related to an Issue resource object. The relations provided by the Issue resource object are:

  • relations

  • time_entries

  • checklists (requires the RedmineUP Checklists plugin, see Checklist)

>>> issue = redmine.issue.get(34441)
>>> issue.time_entries
<redminelib.resultsets.ResourceSet object with TimeEntry resources>

all

redminelib.managers.ResourceManager.all(**params)

Returns all Issue resources (both open and closed) from Redmine, to return issues with specific status from Redmine use filter() method below.

Parameters:
  • sort (string) – (optional). Column to sort. Append :desc to invert the order.

  • limit (int) – (optional). How much resources to return.

  • offset (int) – (optional). Starting from what resource to return the other resources.

  • include (list) – (optional). Fetches associated data in one call. Accepted values:

    • relations

    • attachments (requires Redmine >= 3.4.0)

Returns:

ResourceSet object

>>> issues = redmine.issue.all(sort='category:desc', include=['relations', 'attachments'])
>>> issues
<redminelib.resultsets.ResourceSet object with Issue resources>

filter

redminelib.managers.ResourceManager.filter(**filters)

Returns Issue resources that match the given lookup parameters.

Parameters:
  • issue_id (int or string) – (optional). Find issue or issues by id (separated by ,)

  • parent_id (int) – (optional). Get issues whose parent issue is given id.

  • project_id (int or string) – (optional). Id or identifier of issue’s project.

  • subproject_id (int or string) – (optional). Get issues from the subproject with the given id. You can use project_id=X, subproject_id=!* to get only the issues of a given project and none of its subprojects.

  • tracker_id (int) – (optional). Get issues from the tracker with given id.

  • query_id (int) – (optional). Get issues for the given query id if the project_id is given.

  • status_id (int or string) – (optional). Get issues with given status id. One of:

    • open - open issues

    • closed - closed issues

    • * - all issues

    • id - status id

  • author_id (int) – (optional). Get issues which are authored by the given user id.

  • assigned_to_id (int) – (optional). Get issues which are assigned to the given user id. To get the issues assigned to the user whose credentials were used to access the API pass me as a string.

  • cf_x (string) – (optional). Get issues with given value for custom field with an ID of x. The ~ sign can be used before the value to find issues containing a string in a custom field.

  • sort (string) – (optional). Column to sort. Append :desc to invert the order.

  • limit (int) – (optional). How much resources to return.

  • offset (int) – (optional). Starting from what resource to return the other resources.

  • include (list) – (optional). Fetches associated data in one call. Accepted values:

    • relations

    • attachments (requires Redmine >= 3.4.0)

Returns:

ResourceSet object

>>> issues = redmine.issue.filter(
...     project_id='vacation',
...     subproject_id='!*',
...     created_on='><2012-03-01|2012-03-07',
...     cf_22='~foo',
...     sort='category:desc'
... )
>>> issues
<redminelib.resultsets.ResourceSet object with Issue resources>
>>> issues = redmine.issue.filter(
...     project_id='vacation',
...     query_id=326
... )
>>> issues
<redminelib.resultsets.ResourceSet object with Issue resources>

Hint

You can also get issues from a Project, Tracker, IssueStatus or User resource objects directly using issues relation:

>>> project = redmine.project.get('vacation')
>>> project.issues
<redminelib.resultsets.ResourceSet object with Issue resources>

Added in version 2.5.0.

Apart from issues relation a User resource object provides issues_assigned which is an alias to issues relation and issues_authored relation which returns Issue objects authored by a user:

>>> user = redmine.user.get(9)
>>> user.issues
<redminelib.resultsets.ResourceSet object with Issue resources>
>>> user.issues_assigned
<redminelib.resultsets.ResourceSet object with Issue resources>
>>> user.issues_authored
<redminelib.resultsets.ResourceSet object with Issue resources>

Update methods

update

redminelib.managers.ResourceManager.update(resource_id, **fields)

Updates values of given fields of an Issue resource and saves them to the Redmine.

Parameters:
  • resource_id (int) – (required). Issue id.

  • project_id (int) – (optional). Issue project id.

  • subject (string) – (optional). Issue subject.

  • tracker_id (int) – (optional). Issue tracker id.

  • description (string) – (optional). Issue description.

  • notes (string) – (optional). Issue journal note.

  • private_notes (bool) – (optional). Whether notes are private.

  • status_id (int) – (optional). Issue status id.

  • priority_id (int) – (optional). Issue priority id.

  • category_id (int) – (optional). Issue category id.

  • fixed_version_id (int) – (optional). Issue version id.

  • is_private (bool) – (optional). Whether issue is private.

  • assigned_to_id (int) – (optional). Issue will be assigned to this user id.

  • parent_issue_id (int) – (optional). Parent issue id.

  • start_date (string or date object) – (optional). Issue start date.

  • due_date (string or date object) – (optional). Issue end date.

  • estimated_hours (int) – (optional). Issue estimated hours.

  • done_ratio (int) – (optional). Issue done ratio.

  • custom_fields (list) – (optional). Custom fields as [{‘id’: 1, ‘value’: ‘foo’}].

  • uploads (list) – (optional). Uploads as [{'': ''}, ...], accepted keys are:

    • path (required). Absolute file path or file-like object that should be uploaded.

    • filename (optional). Name of the file after upload.

    • description (optional). Description of the file.

    • content_type (optional). Content type of the file.

Returns:

True

>>> from io import BytesIO
>>> redmine.issue.update(
...     1,
...     project_id=1,
...     subject='Vacation',
...     tracker_id=8,
...     description='foo',
...     notes='A journal note',
...     private_notes=True,
...     status_id=3,
...     priority_id=7,
...     assigned_to_id=123,
...     parent_issue_id=345,
...     start_date=datetime.date(2014, 1, 1),
...     due_date=datetime.date(2014, 2, 1),
...     estimated_hours=4,
...     done_ratio=40,
...     custom_fields=[{'id': 1, 'value': 'foo'}, {'id': 2, 'value': 'bar'}],
...     uploads=[{'path': '/absolute/path/to/file'}, {'path': BytesIO(b'I am content of file 2')}]
... )
True

save

redminelib.resources.Issue.save(**attrs)

Saves the current state of an Issue resource to the Redmine. Attrs that can be changed are the same as for update() method above.

Returns:

Resource object

>>> issue = redmine.issue.get(1)
>>> issue.project_id = 1
>>> issue.subject = 'Vacation'
>>> issue.tracker_id = 8
>>> issue.description = 'foo'
>>> issue.notes = 'A journal note'
>>> issue.private_notes = True
>>> issue.status_id = 3
>>> issue.priority_id = 7
>>> issue.assigned_to_id = 123
>>> issue.parent_issue_id = 345
>>> issue.start_date = datetime.date(2014, 1, 1)
>>> issue.due_date = datetime.date(2014, 2, 1)
>>> issue.estimated_hours = 4
>>> issue.done_ratio = 40
>>> issue.custom_fields = [{'id': 1, 'value': 'foo'}, {'id': 2, 'value': 'bar'}]
>>> issue.uploads = [{'path': '/absolute/path/to/file'}, {'path': '/absolute/path/to/file2'}]
>>> issue.save()
<redminelib.resources.Issue #1 "Vacation">

Added in version 2.1.0: Alternative syntax was introduced.

>>> issue = redmine.issue.get(1).save(
...     project_id=1,
...     subject='Vacation',
...     tracker_id=8,
...     description='foo',
...     notes='A journal note',
...     private_notes=True,
...     status_id=3,
...     priority_id=7,
...     assigned_to_id=123,
...     parent_issue_id=345,
...     start_date=datetime.date(2014, 1, 1),
...     due_date=datetime.date(2014, 2, 1),
...     estimated_hours=4,
...     done_ratio=40,
...     custom_fields=[{'id': 1, 'value': 'foo'}, {'id': 2, 'value': 'bar'}],
...     uploads=[{'path': '/absolute/path/to/file'}, {'path': '/absolute/path/to/file2'}]
... )
>>> issue
<redminelib.resources.Issue #1 "Vacation">

Delete methods

delete

redminelib.managers.ResourceManager.delete(resource_id)

Deletes single Issue resource from Redmine by its id.

Parameters:

resource_id (int) – (required). Issue id.

Returns:

True

>>> redmine.issue.delete(1)
True
redminelib.resources.Issue.delete()

Deletes current Issue resource object from Redmine.

Returns:

True

>>> issue = redmine.issue.get(1)
>>> issue.delete()
True

Copying

Added in version 2.5.0.

redminelib.managers.IssueManager.copy(resource_id, project_id=None, link_original=True, include=('subtasks', 'attachments'), **fields)

Copies single Issue resource by its id using the same API as Redmine’s GUI copying which means copying is done the most efficient way possible. By default links original to a copy via relations and copies both subtasks and attachments.

Parameters:
  • resource_id (int) – (required). Issue id.

  • project_id (int or string) – (required). Id or identifier of issue’s project.

  • link_original (bool) – (optional). Whether to link the original issue to a copy via relations.

  • include (list) – (optional). Additional data to copy or None. Accepted values:

    • subtasks

    • attachments

  • fields (dict) – (optional). Accepts the same fields as Issue’s create() method to add or modify original values.

Returns:

Resource object

>>> copy = redmine.issue.copy(1, project_id='vacation', link_original=False, include=['attachments'])
>>> copy
<redminelib.resources.Issue #124 "Vacation">
redminelib.resources.Issue.copy(link_original=True, include=('subtasks', 'attachments'), **fields)

Copies current Issue resource object.

Returns:

Resource object

>>> issue = redmine.issue.get(123)
>>> copy = issue.copy(subject='this is a copy')
>>> copy
<redminelib.resources.Issue #124 "this is a copy">

Filters added in newer Redmine versions

All issue, time entry and project filters of the Redmine web interface can be used with filter(). Filters whose names contain a dot have to be passed as a dict:

>>> redmine.issue.filter(project_id='vacation', **{'author.group': 5, 'author.role': 3})
>>> redmine.time_entry.filter(**{'user.group': 5})
>>> redmine.project.filter(updated_on='>t-7')

Redmine 6.0+ adds author.group and author.role for issues, user.group and user.role for time entries and updated_on for projects. estimated_hours, spent_time and the time entry hours filters accept the time formats Redmine accepts elsewhere, e.g. 1:30.

RedmineUP plugin extensions

Added in version 3.1.0.

The RedmineUP plugins extend the core issue API:

  • Checklists: create() and update() accept checklists with a list of {'subject': ..., 'is_done': ..., 'is_section': ...} dicts. Python-Redmine sends them as the index keyed checklists_attributes hash the plugin needs on update. New items are added, pass id to change an item and '_destroy': '1' to remove one. Filters: checklists_status (1 done, 0 undone) and checklists_item (subject).

    >>> redmine.issue.create(project_id='vacation', subject='Packing', checklists=[
    ...     {'subject': 'Passport', 'is_done': True},
    ...     {'subject': 'Sunscreen'},
    ... ])
    
  • Agile: agile_data_attributes with story_points and agile_sprint_id, see Agile Data. Filter: agile_sprints (sprint id).

  • Helpdesk: create() accepts helpdesk_ticket_attributes (from_address, to_address, cc_address, ticket_date, source, …), customer_id, customer_address and helpdesk_send_as (1 auto answer, 2 initial message). Python-Redmine sends the last three next to the issue data as the plugin expects. Filters: customer, customer_company, ticket_source, ticket_reaction_time, ticket_first_response_time, ticket_resolve_time and vote.

  • CRM: links to contacts and deals are issue custom fields of the contact, company or deal format and are read and written through custom_fields with the contact or deal ids as values.

Expert plugin extensions

Added in version 3.0.0.

The redmine_expert_helpdesk and redmine_expert_agile plugins extend the core issue API:

  • create() accepts helpdesk_init to turn a new issue into a helpdesk ticket and optionally send the initial mail (requires the send_helpdesk_reply permission). Python-Redmine sends it next to the issue data as the plugin expects and converts send_mail booleans to the expected '1'/'0':

    >>> redmine.issue.create(
    ...     project_id='support',
    ...     subject='Printer on fire',
    ...     helpdesk_init={
    ...         'contact_email': 'jane@example.com',
    ...         'contact_name': 'Jane Doe',
    ...         'mailbox_id': 7,
    ...         'cc': 'boss@example.com',
    ...         'send_mail': True,
    ...     }
    ... )
    
  • create() and update() accept expert_agile_data_attributes with story_points and sprint_id, see Expert Agile Data (on create() requires redmine_expert_agile >= 0.6.1).

  • filter() supports the additional filters helpdesk_kunde (customer name or email), helpdesk_sla_reaction and helpdesk_sla_solution (met, breached_done, running, warning, breached), helpdesk_awaiting_agent (1/0) and story_points:

    >>> redmine.issue.filter(project_id='support', helpdesk_kunde='~acme', helpdesk_awaiting_agent=1)
    >>> redmine.issue.filter(project_id='scrum', story_points='>=3')
    

Export

Added in version 2.0.0.

redminelib.resources.Issue.export(fmt, savepath=None, filename=None)

Exports Issue resource in one of the following formats: atom, pdf

Parameters:
  • fmt (string) – (required). Format to use for export.

  • savepath (string) – (optional). Path where to save the file.

  • filename (string) – (optional). Name that will be used for the file.

Returns:

String or Object

>>> issue = redmine.issue.get(123)
>>> issue.export('pdf', savepath='/home/jsmith')
'/home/jsmith/123.pdf'
redminelib.resultsets.ResourceSet.export(fmt, savepath=None, filename=None, columns=None, encoding='UTF-8')

Exports a resource set of Issue resources in one of the following formats: atom, pdf, csv

Parameters:
  • fmt (string) – (required). Format to use for export.

  • savepath (string) – (optional). Path where to save the file.

  • filename (string) – (optional). Name that will be used for the file.

  • columns (iterable or string) – (optional). Iterable of column names or “all” string for all available columns or “all_gui” string for GUI like behaviour or iterable of elements with “all_gui” string and additional columns to export.

  • encoding – (optional). Encoding that will be used for the result file.

Returns:

String or Object

>>> issues = redmine.issue.all()
>>> issues.export('csv', savepath='/home/jsmith', filename='issues.csv', columns='all')
'/home/jsmith/issues.csv'

Journals

The history of an issue is represented as a ResourceSet of IssueJournal resources. Since Redmine 6.0 a journal whose notes were edited also contains updated_on and updated_by (a User resource). Currently the following operations are possible:

create

To create a new record in issue history, i.e. new journal:

redmine.issue.update(1, notes='new note')
True

read

Recommended way to access issue journals is through associated data includes:

>>> issue = redmine.issue.get(1, include=['journals'])
>>> issue.journals
<redminelib.resultsets.ResourceSet object with IssueJournal resources>

But they can also be accessed through on demand includes:

>>> issue = redmine.issue.get(1)
>>> issue.journals
<redminelib.resultsets.ResourceSet object with IssueJournal resources>

After that they can be used as usual:

>>> for journal in issue.journals:
...     print(journal.id, journal.notes)
...
1 foobar
2 lalala
3 hohoho

update

Added in version 2.4.0.

To update notes attribute (the only attribute that can be updated) of a journal:

>>> issue = redmine.issue.get(1, include=['journals'])
>>> for journal in issue.journals:
...     journal.save(notes='setting notes to a new value')
...

Or if you know the id beforehand:

>>> redmine.issue_journal.update(1, notes='setting notes to a new value')
True

delete

Added in version 2.4.0.

To delete a journal set its notes attribute to empty string:

>>> issue = redmine.issue.get(1, include=['journals'])
>>> for journal in issue.journals:
...     journal.save(notes='')
...

Or if you know the id beforehand:

>>> redmine.issue_journal.update(1, notes='')
True

Note

You can only delete a journal that doesn’t have the associated details attribute.

Watchers

Python-Redmine provides 2 methods to work with issue watchers:

add

redminelib.resources.Issue.Watcher.add(user_id)

Adds a user to issue watchers list by its id.

Parameters:

user_id (int) – (required). User id.

Returns:

True

>>> issue = redmine.issue.get(1)
>>> issue.watcher.add(1)
True

remove

redminelib.resources.Issue.Watcher.remove(user_id)

Removes a user from issue watchers list by its id.

Parameters:

user_id (int) – (required). User id.

Returns:

True

>>> issue = redmine.issue.get(1)
>>> issue.watcher.remove(1)
True