forked from ianepperson/pyredminews
-
Notifications
You must be signed in to change notification settings - Fork 0
/
setup.py
509 lines (337 loc) · 14.6 KB
/
setup.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
#from distutils.core import setup
from setuptools import setup
def readme():
# with open('README.rst') as f:
# return f.read()
return '''
pyredminews
===========
Python Redmine Web Services Library
This python library facilitates creating, reading, updating and deleting content from a Redmine_ installation through the REST API.
Communications are performed via HTTP/S, freeing up the need for the Python script to run on the same machine as the Redmine installation.
This library was originally published at http://code.google.com/p/pyredminews/
.. _Redmine: http://www.redmine.org/
Installation
------------
PyPI
++++
The easiest way to install. The module is available on the Python Package Index (PyPI) and is available for the
easy_install or pip commands. easy_install is available by default on OSX, either command may be available for whatever
flavor of Unix you are using, and must be installed on Windows.
::
> easy_install pyredmine
::
> pip install pyredmine
GitHub
++++++
To get the latest code updates or to get a different version of the library, you can clone this Github repository
and run the setup script manually.
::
> git clone https://github.com/ianepperson/pyredminews.git
> cd pyredminews
> python setup.py
How to use it
-------------
Set up Redmine
++++++++++++++
Within Redmine, you must enable the REST web service before most of the library operations will work.
In the Administration -> Settings page on the Authentication tab, check the box for
"Enable REST web service" and click the Save button. If the REST interface is not enabled, the users
will not see an "API key" in their user information page.
Many of the read-only actions provided by this library will still work, but the write/create operations will fail.
Documentation
+++++++++++++
The library, like most good Python libraries, is self documenting. In the console, import the library then type ''help(redmine)''
for all the details.
Step by Step
------------
Start Python, Import the Module
+++++++++++++++++++++++++++++++
Open a Python terminal window.
::
$ python
Python 2.6.1 (r261:67515, Feb 11 2010, 00:51:29)
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
Now, import the Redmine class
::
>>> from redmine import Redmine
>>>
View the documentation
++++++++++++++++++++++
::
>>> help(Redmine)
Help on class Redmine in module redmine.redmine:
class Redmine
| Class to interoperate with a Redmine installation using the REST web services.
| instance = Redmine(url, [key=strKey], [username=strName, password=strPass] )
|
| url is the base url of the Redmine install ( http://my.server/redmine )
(type q to quit)
etc.
Locate a Redmine installation
+++++++++++++++++++++++++++++
If you don't have Redmine installed somewhere that you can play with you can use the public demo server.
With your web browser, go take a look at http://demo.redmine.org. Find or make a project and note its identifier
(not its pretty name, but the path it ends up in such as "testproject" in ``http://demo.redmine.org/projects/testproject``).
Make a bug and note its number. The remaining examples will assume you've done this.
Currently the Redmine instalation running at demo.redmine.org *does not* have the REST interface enabled.
Set up the Connection
+++++++++++++++++++++
Make an instance that represents the server you want to connect to.
::
>>> demo_anon = Redmine('http://demo.redmine.org')
>>>
Authentication
++++++++++++++
You can perform most of the view actions with this anonymous access, but for the cool stuff,
you should register an account and set up that access:
::
>>> demo = Redmine('http://demo.redmine.org', username='pyredmine', password='password')
>>>
Since leaving around your password in a script really sucks, Redmine allows you to use a user API key instead.
Once logged into Redmine, on the My Account page, click on the Show button under the API Access Key on the right.
That will reveal a key that can be used instead of a username/password. If you do not see any reference to the
API key in the right-hand panel, the REST interface probably hasn't been enabled - see `Set up Redmine`_ above.
::
>>> demo = Redmine('http://demo.redmine.org', key='701c0aec4330fb2f1db944f1808e1e987050c7f5')
>>>
Define the Redmine Version
++++++++++++++++++++++++++
Different versions of Redmine can use improved security, and have different items available through the REST interface.
In order for the module to correctly represent the data and use available security features, you should tell
the object what version your Redmine server is using.
::
>>> demo = Redmine('http://demo.redmine.org', username='pyredmine', password='password', version=2.1)
>>>
View Project Data
+++++++++++++++++
Although you can use this library to look up a large list of projects, the easiest helper functions are designed
to work with a single project of a given identifier (testproject, in our example above). The projects parameter on
the Redmine object can be used to return a single Project object.
::
>>> project = demo.projects['demoproject']
>>>
Now with that shiny new project object, you can take a look at the data available:
::
>>> project.id
393
>>> project.identifier
u'demoproject'
>>>
The fields available on the project will differ depending on the version of Redmine.
To see the full list of items available for the project, try:
::
>>> dir(project)
(politely ignore anything staring with a _)
u'created_on',
u'description',
u'homepage',
u'id',
u'identifier',
'issues',
u'name',
'parent',
'refresh',
'save',
'time_entries',
u'updated_on']
If you happen to know the numeric ID of your project, that can also be used to look it up. If demoproject is in fact
id 393, then the following will return the same project:
::
>>> project = demo.projects[393]
Change the Project
++++++++++++++++++
Changing the fields of the project are as easy as changing the objects parameters and invoking the save method.
::
>>> project.homepage = 'http://www.dead-parrot.com'
>>> project.name = 'Dead Parrot Society'
>>> project.save()
If you try and set a parameter for a read-only field, you'll get an attribute error.
::
>>> project.updated_on = 'today'
AttributeError: Can't set attribute updated_on.
If your Redmine instance has custom fields set for projects, those fields and their values will be returned and can be changed
in the same manner:
::
>>> project.custom_fields['Customer'] = 'John Cleese'
>>> project.save()
Examine All Projects
++++++++++++++++++++
The projects member can be iterated over to retrieve information about all projects. Be default, Redmine will return 25 items
at a time, so the following query might take some time to complete.
::
>>> for proj in demo.projects:
... print "%s : %s" % (proj.name, proj.homepage)
(truncated)
Test Project LS II : None
Test Project Only : None
Test Project Rizal : None
Create home page : None
Test Project SES : None
Test project SGO : None
Test Project Trial : None
Test Project Tutorial ABCDE : None
Get All Issues for a Project
++++++++++++++++++++++++++++
The issues associated for a project can be retreived by iterating over the 'issues' method in a project.
::
>>> for issue in project.issues:
... print issue
<Redmine issue #3903, "mary was here too">
<Redmine issue #3902, "Johny was there">
<Redmine issue #3870, "Demo Feature">
(truncated)
(You may get an Unicode error if any of the issues has unicode in the subject. If you do, instead use: print "%s" % issue )
If you want to exclude issues from any subprojects, you can add query parameters to the iterator:
::
>>> for issue in project.issues(subproject_id='!*'):
... print issue
Other parameters are:
* tracker_id: get issues from the tracker with the given id
* status_id: get issues with the given status id only. Possible values: open, closed, * to get open and closed issues, status id
* assigned_to_id: get issues which are assigned to the given user id
* cf_x: get issues with the given value for custom field with an ID of x. (Custom field must have 'used as a filter' checked.)
Create a New Issue
++++++++++++++++++
You can use the project object to create a new issue for that project:
::
>>> issue = project.issues.new(subject="Test from Python", description="That rabbit is dynamite!")
>>> issue.id
35178
>>> issue.created_on
datetime.datetime(2013, 2, 7, 1, 0, 28, tzinfo=tzutc())
>>>
Note that the new command returned an Issue object containing all (or, on older Redmine versions, most) of the new issue's data.
You can now go to http://demo.redmin.org/projects/demoproject/issues to see your new issue. Any date/time information is returned
as a `Python datetime object <http://docs.python.org/2/library/datetime.html#datetime-objects>`_.
(Note the issue ID, you'll need that for the next steps)
View an Issue
+++++++++++++
You can view any issue by its ID:
::
>>> issue = demo.issues[35178]
>>> issue.status
<Redmine status #1 - New>
>>> issue.subject
u'That rabbit is dynamite!'
Like the issues.new command above, it's returning an object with all of the issue data.
Note that this command is not running from the Project object but from the Redmine object.
If you examine your issue object, you'll see that it contains an author parameter, which is itself another object:
::
>>> issue.author
<Redmine user #5 - Ian Epperson>
However, this user object is incomplete:
::
>>> issue.author.last_login
>>>
pyRedmine created the object with the data it had on hand, and since it doesn't have the last_login data (it wasn't
in the issue information) it isn't shown here. There are two ways to flesh out that data, one is to use the refresh
method for the author (works on Redmine 1.1 and later where this data is available).
::
>>> issue.author.refresh()
>>> issue.author.last_login
datetime.datetime(2013, 2, 7, 1, 0, 28, tzinfo=tzutc())
The other is to simply request that user id from the server
::
>>> demo.users[5]
<Redmine user #5 - Ian Epperson>
>>> issue.author.last_login
datetime.datetime(2013, 2, 7, 1, 0, 28, tzinfo=tzutc())
pyRedmine caches all objects it sees and sets up all cross references. Updating a Redmine object attached to one object
will update them all. Also, this allows you to directly compare objects if needed:
::
>>> issue.author == issue.assigned_to
True
Change an Issue's Status
++++++++++++++++++++++++
You can move an issue through the workflow as well. You must set an issue status based on the status ID,
which is can only be discovered in Redmine version 2.2 and later (but not yet available via this library).
By default, the library uses the status ID for Resolved and Closed from a default Redmine installation,
but if you've changed them in the Administration page, you'll have to change these each time as well.
::
>>> demo.ISSUE_STATUS_ID_RESOLVED
3
>>> demo.ISSUE_STATUS_ID_CLOSED
5
The closed and resolved methods are available on the issue itself, with an optional comment:
::
>>> issue.close('Closed the issue from Python!')
>>> issue.resolve('Resolved the issue from Python!')
Some versions of Redmine will not return an error if this operation fails, so be careful of false hopes.
If you need to set another status, you'll need to find the requisite status ID, then use the set_status method
(again, with optional comment):
::
>>> issue.set_status(8, 'Setting the status from Python!')
If you need to close an issue and don't need to get an issue object, you can set the issue status directly
using the Redmine server object with a single operation:
::
>>> demo.issues.update(35178, status_id=5)
Change an Issue
+++++++++++++++
Just like with projects, to change a field on an issue simply change the parameter on the issue object
then invoke the save method.
::
>>> issue.description = "The parrot doesn't seem to be alive."
>>> issue.save()
If your installation of Redmine has custom fields on issues, those fields can be inspected and set.
::
>>> issue.custom_fields['Inform the client']
u'0'
>>> issue.custom_fields['Inform the client'] = 1
>>> issue.save()
If you want to change the project this issue is assigned to, you can set it directly to either a project object
or a numeric project ID, then save it.
::
>>> issue.project = 12
>>> issue.save()
>>> issue.project = demo.projects['test']
>>> issue.save()
Delete an Issue
+++++++++++++++
There is also an issue delete command that you should use with care. In a real production environment,
you normally would never delete an issue - just leave it closed. Deleting it will remove history, time worked,
and almost every trace of it. So, be careful! On the demo server, you don't have permission to delete, so go ahead and try:
::
>>> demo.issues.delete(35178)
(whole lot of response, including)
urllib2.HTTPError: HTTP Error 403: Forbidden
>>>
Different versions of Redmine are inconsistent about when they returns 403 and when they just doesn't work. You can't rely on the lack of an
HTTPError to guarantee success.
Other Objects
+++++++++++++
Depending on what Redmine version you have, you can use these same commands to get/update/delete different Redmine items:
* users
* news
* time_entries
Not every item supports every method. For instance, no current version of Redmine allows creating a news item, thus:
::
>>> demo.news.new(title='does this work?', description='Nope', author_id=4)
AttributeError: new is not available for News
'''
setup(
name = "pyredmine",
packages = ["redmine"],
install_requires = ["python-dateutil"],
version = "0.2.4",
description = "Python Redmine Web Services Library",
long_description = readme(),
author = "Ian Epperson",
author_email = "[email protected]",
url = "https://github.com/ianepperson/pyredminews",
keywords = ["redmine", "server"],
classifiers = [
"Programming Language :: Python",
"Development Status :: 3 - Alpha",
"Environment :: Other Environment",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
"Operating System :: OS Independent",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Office/Business :: Groupware",
"Topic :: Utilities",
"Topic :: Internet :: WWW/HTTP :: Site Management",
],
)