Python Redmine
Python Redmine is a Python library that communicates with Redmine.
It has the following features.
・ 100% support for Redmine API function (You can even create a project) -Python 2.7, 3.4-3.7
The details are described below. https://python-redmine.com/
It can be installed using pip or easy_install.
$ pip install python-redmine
Or
$ easy_install python-redmine
As a setting on the Redmine side, check "Enable Web service by REST API" on the API tab on the "Administration"-> "Settings" screen.
If you enable the API here, you can check the API key on the personal settings screen.
A sample is described below. This sample is confirmed by Python3.7 + python redmine2.2.1 + Redmine 4.0.3.stable on Windows.
You can connect to redmine by specifying a user name and password.
from redminelib import Redmine
redmine = Redmine('http://localhost/redmine', username='admin', password='admin')
Alternatively, you can connect using the API key as follows.
from redminelib import Redmine
redmine = Redmine('http://localhost/redmine', key='e4a413a3b7a3c238102b7393c035bbc5f5eb6409')
See below for ticket operations https://python-redmine.com/resources/issue.html
You can create a ticket by creating a ticket object with redmine.issue.new (), setting properties there, and saving it.
import datetime
from redminelib import Redmine
redmine = Redmine('http://192.168.0.200/', key='60076966cebf71506ae3f2391da649235a2b1d46')
issue = redmine.issue.new()
issue.project_id = 'Test1'
issue.subject = 'Subject'
issue.tracker_id = 1 #Tracker
issue.description = 'Show the contents of the ticket.\n Line breaks are also possible.'
issue.status_id = 1 #status
issue.priority_id = 1 #priority
issue.assigned_to_id = 1 #Person in charge ID
issue.watcher_user_ids = [1] #ID of the user to watch
issue.parent_issue_id = 12 #Parent ticket ID
issue.start_date = datetime.date(2014, 1, 1) #start date
issue.due_date = datetime.date(2014, 2, 1) #Deadline
issue.estimated_hours = 4 #Expected man-hours
issue.done_ratio = 40
issue.custom_fields = [{'id': 1, 'value': 'foo'}]
issue.uploads = [{'path': 'C:\\dev\\python3\\redmine\\test.txt'}]
issue.custom_fields = [{'id': 1, 'value': 'foo'}]
issue.save()
Specify the ticket ID with redmine.issue.get (). If it does not exist redmine.exceptions.ResourceNotFoundError Exception occurs.
import datetime
from redminelib import Redmine
from redminelib.exceptions import ResourceNotFoundError
redmine = Redmine('http://192.168.0.200/', key='60076966cebf71506ae3f2391da649235a2b1d46')
try:
issue = redmine.issue.get(60)
print (dir(issue))
print ('id:%d' % issue.id)
print ('project:%s' % issue.project.name)
print ('project_id:%d' % issue.project.id)
print ('subject:%s' % issue.subject)
print ('tracker:%s' % issue.tracker.name)
print ('tracker_id:%d' % issue.tracker.id)
print ('description:%s' % issue.description)
print ('status:%s' % issue.status.name)
print ('status:%d' % issue.status.id)
print ('author:%s' % issue.author.name)
print ('author_id:%d' % issue.author.id)
if hasattr(issue, 'assigned'):
print ('assigned:%s' % issue.assigned_to.name)
print ('assigned_id:%d' % issue.assigned_to.id)
print ('watcher--------')
for u in issue.watchers:
print (' %d:%s' % (u.id, u.name))
print ('Created date:%s' % issue.created_on)
print ('Update date:%s' % issue.updated_on)
if hasattr(issue, 'start_date'):
print ('start_date:%s' % issue.start_date)
if hasattr(issue, 'due_date'):
print ('issue_date:%s' % issue.due_date)
if hasattr(issue, 'issue.estimated_hours'):
print ('estimated_hours:%d' % issue.estimated_hours)
print ('Working hours:%d' % issue.spent_hours)
print ('Record of working time----------')
for t in issue.time_entries:
print(' ID:%d' % t.id)
print('Activities:%s' % t.activity)
print('comment:%s' % str(t.comments))
print('Created date:%s' % t.created_on)
print('time:%s' %t.hours)
print('Ticket ID:%s' % t.issue)
print('Project ID:%s' % t.project)
print('date:%s' % t.spent_on)
print('Update date:%s' % t.updated_on)
print(' user:%d %s' % (t.user.id,t.user.name))
print ('done_ratio:%d' % issue.done_ratio)
print ('priority:%s' % issue.priority.name)
print ('priority_id:%d' % issue.priority.id)
print ('custom_fields----')
for c in issue.custom_fields:
print (' %d:%s = %s' % (c.id, c.name, c.value))
print ('attachements---')
for f in issue.attachments:
print (' id:%d' % (f.id))
print (' author:%s' % (f.author))
print (' content_url:%s' % (f.content_url))
print (' created_on:%s' % (f.created_on))
print (' description:%s' % (f.description))
print (' filename:%s' % (f.filename))
print (' filesize:%d' % (f.filesize))
print (' ---------------')
print ('changeset---')
for c in issue.changesets:
#Commit log stored as dictionary type
print (' %s' % c)
if hasattr(issue, 'parent'):
print ('parent:%s' % issue.parent)
print ('children----------')
for c in issue.children:
print (' %s:%s' % (c.id, c.subject))
print ('relation----------')
for r in issue.relations:
print (' %d:%d->%d(%s)' % (r.id, r.issue_id, r.issue_to_id, r.relation_type))
except (ResourceNotFoundError):
print ('Not found')
The property itself does not exist for the item for which input is omitted. Therefore, you have to check the existence with hasattr.
The user information of watchers, the person in charge, and the creator contains only the minimum necessary information. If you want to get the login name etc., get the user details from the user ID as follows.
user = redmine.user.get(u.id)
You can get it with redmine.issue.all (). The parameters that can be omitted are as follows.
sort (string): sort order limit (integer): Upper limit of the number of acquisitions offset (integer): Acquisition start position
import datetime
from redminelib import Redmine
redmine = Redmine('http://192.168.0.200/', key='60076966cebf71506ae3f2391da649235a2b1d46')
issues = redmine.issue.all(sort='category:desc')
for issue in issues:
print ('%d:%s' % (issue.id, issue.subject))
Tickets with specific conditions can be extracted using redmine.issue.filter. In the following, the person in charge is extracting his / her ticket.
import datetime
from redminelib import Redmine
redmine = Redmine('http://192.168.0.200/', key='60076966cebf71506ae3f2391da649235a2b1d46')
issues = redmine.issue.filter(assigned_to_id='me')
for issue in issues:
print ('%d:%s' % (issue.id, issue.subject))
You can also search by registered query by using query_id.
After all, you're just reading the REST API, so you can use it with Redmine-supported operators. http://www.redmine.org/projects/redmine/wiki/Rest_Issues
In the above document, examples such as "! \ " ">" "<" "" "~" Are introduced.
So, for example, to get a task that is not assigned to anyone, the implementation is as follows.
import datetime
from redminelib import Redmine
redmine = Redmine('http://192.168.0.200/', key='60076966cebf71506ae3f2391da649235a2b1d46')
issues = redmine.issue.filter(assigned_to_id='!*')
for issue in issues:
print ('%d:%s' % (issue.id, issue.subject))
Also, if you want to get the task of a person in charge other than yourself, it will be as follows.
import datetime
from redminelib import Redmine
redmine = Redmine('http://192.168.0.200/', key='60076966cebf71506ae3f2391da649235a2b1d46')
issues = redmine.issue.filter(assigned_to_id='!me')
for issue in issues:
print ('%d:%s' % (issue.id, issue.subject))
We can see that various conditioning is possible by using operators such as negation and range specification, but the problem is that the REST API documentation does not tell us what kind of operator there is.
Therefore, it is necessary to refer to the actual code.
redmine/app/models/query.rb https://github.com/redmine/redmine/blob/9746ab7e5be2db5e2d233ee37365cf21ba4b893a/app/models/query.rb#L254
If you look at this implementation, you can see what operators are defined. However, not all items can be used by all operators. For example, in subject, "\ *: all", "! \ *: None" and "~: include" can be used, but "^: starts with" cannot be used. Whether or not this can be used is the same as the conditions that can be done on the filter setting screen below.
Tickets obtained with redmine.issue.get can be changed and saved. The following example is an example of changing the ticket status.
# -*- coding: utf-8 -*-
import datetime
from redmine import Redmine
redmine = Redmine('http://localhost/redmine', key='e4a413a3b7a3c238102b7393c035bbc5f5eb6409')
issue = redmine.issue.get(51)
issue.status_id = 2
issue.save()
See below for working time operations https://python-redmine.com/resources/time_entry.html
The following is an example of enumerating working hours.
import datetime
from redminelib import Redmine
redmine = Redmine('http://192.168.0.200/', key='60076966cebf71506ae3f2391da649235a2b1d46')
time_entries = redmine.time_entry.all()
for t in time_entries:
print(' ID:%d' % t.id)
print('Activities:%s' % t.activity)
print('comment:%s' % str(t.comments))
print('Created date:%s' % t.created_on)
print('time:%s' %t.hours)
print('Ticket ID:%s' % t.issue)
print('Project ID:%s' % t.project)
print('date:%s' % t.spent_on)
print('Update date:%s' % t.updated_on)
print(' user:%d %s' % (t.user.id,t.user.name))
print('Work classification%d %s' % (t.activity.id, t.activity.name))
An example of recording the working time is shown below.
import datetime
from redminelib import Redmine
redmine = Redmine('http://192.168.0.200/', key='60076966cebf71506ae3f2391da649235a2b1d46')
time_entry = redmine.time_entry.new()
time_entry.issue_id = 60
time_entry.spent_on = datetime.date(2014, 1, 14)
time_entry.hours = 3
time_entry.activity_id = 4
time_entry.comments = 'hello'
time_entry.activity_id = 8
time_entry.save()
This time, only a part of the provided API was verified, but it was confirmed that the operations that can be performed on the Web screen are covered.
By using Python Redmine, you can expect to be able to operate Redmine freely with Python. It can be expected that the following can be realized.
-Issue a ticket from the program when the automatic test fails. ・ Automatic conversion of design documents written in Excel to Wiki -Aggregate the work time and notify another system.
that's all
Recommended Posts