Welcome | Get started | Dive into Lino | Contribute | Reference
tickets
(Ticket management in Noi)¶
The lino_noi.lib.tickets
plugin extends lino_xl.lib.tickets
to
make it collaborate with lino_noi.lib.working
.
In Lino Noi the site of a ticket also indicates "who is going to pay" for our work. Lino Noi uses this information when generating a service report.
This is a tested document. The following instructions are used for initialization:
>>> from lino import startup
>>> startup('lino_book.projects.noi1e.settings.demo')
>>> from lino.api.doctest import *
Tickets¶
Here is a textual description of the fields and their layout used in the detail window of a ticket.
>>> from lino.utils.diag import py2rst
>>> print(py2rst(tickets.AllTickets.detail_layout, True))
...
(main) [visible for all]:
- **General** (general_tab_1):
- **None** (overview)
- (general2):
- (general2_1): **Subscription** (order), **End user** (end_user), **Ticket type** (ticket_type)
- (triager_panel_1) [visible for developer admin]: **Project** (site) [visible for all], **Assign to** (quick_assign_to) [visible for all], **Confidential** (private) [visible for all]
- (general2_3): **Priority** (priority), **Planned time** (planned_time), **Deadline** (deadline)
- (general2_4): **Regular** (regular_hours), **Extra** (extra_hours), **Free** (free_hours)
- **Sessions** (working.SessionsByTicket) [visible for contributor developer admin]
- (general3): **Workflow** (workflow_buttons), **My comment** (comment), **Comments** (comments.CommentsByRFC)
- **More** (more_tab_1):
- (more1):
- (more1_1): **ID** (id), **Reference** (ref)
- **Summary** (summary)
- **Description** (description)
- (more2): **Resolution** (upgrade_notes), **Upload files** (uploads.UploadsByController) [visible for customer contributor developer admin]
- (more3):
- **State** (state)
- **Assigned to** (assigned_to)
- (more3_3): **Author** (user), **Created** (created)
- (more3_4): **Modified** (modified), **Fixed since** (fixed_since)
- **Duplicate of** (duplicate_of)
- **Duplicates** (DuplicatesByTicket)
- **Links** (links_1):
- (links_left):
- **Parent** (parent)
- **Tickets** (TicketsByParent) [visible for customer contributor developer admin]
- **Mentioned in** (comments.CommentsByMentioned)
Screenshots¶

The life cycle of a ticket¶
In Lino Noi we use the default tickets workflow defined in
lino_xl.lib.tickets.TicketStates
.
Projects¶
The list of projects in our demo database depends on who is looking at it. Anonymous users can see only public projects:
>>> rt.show(tickets.Sites)
=========== ============= ======== ================ ======== ============== ====
Reference Designation Client Contact person Remark Workflow ID
----------- ------------- -------- ---------------- -------- -------------- ----
admin admin **⚒ Active** 6
bugs bugs **⚒ Active** 3
cust cust **⚒ Active** 5
docs docs **⚒ Active** 2
pypi pypi **⚒ Active** 1
=========== ============= ======== ================ ======== ============== ====
>>> rt.login("marc").show(tickets.Sites)
=========== ============= ======== ================ ======== ================================ ====
Reference Designation Client Contact person Remark Workflow ID
----------- ------------- -------- ---------------- -------- -------------------------------- ----
admin admin **⚒ Active** → [⚹] [☉] [☾] [☑] 6
bugs bugs **⚒ Active** → [⚹] [☉] [☾] [☑] 3
cust cust **⚒ Active** 5
docs docs **⚒ Active** 2
pypi pypi **⚒ Active** 1
=========== ============= ======== ================ ======== ================================ ====
List of sites to which Jean is "subscribed" (i.e. that are assigned to a team where Jean is member):
>>> rt.login("jean").show(tickets.MySites)
======================= ============= ================================
Project Description Workflow
----------------------- ------------- --------------------------------
`pypi <Detail>`__ **⚒ Active** → [⚹] [☉] [☾] [☑]
`security <Detail>`__ **⚒ Active** → [⚹] [☉] [☾] [☑]
======================= ============= ================================
List of tickets that have not yet been assigned to a site:
>>> pv = dict(has_site=dd.YesNo.no)
>>> rt.login("robin").show(tickets.AllTickets, param_values=pv)
...
===== ============================================== ========== =========================================== =========
ID Summary Priority Workflow Project
----- ---------------------------------------------- ---------- ------------------------------------------- ---------
116 Foo never bars 30 [✋] [▶] **☒ Refused**
114 Default account in invoices per partner 30 [✋] [▶] **☐ Ready** → [☒]
112 How can I see where bar? 30 [✋] [▶] **⚒ Working** → [☾] [☐] [☒] [⧖]
110 Why is foo so bar 30 [✋] [▶] **☎ Talk** → [☾] [☉] [☐] [☒] [⧖]
108 No more foo when bar is gone 30 [✋] [▶] **⧖ Waiting**
106 'NoneType' object has no attribute 'isocode' 30 [✋] [▶] **☑ Closed** → [☉]
104 Misc optimizations in Baz 30 [✋] [▶] **☾ Sleeping** → [⧖]
102 Irritating message when bar 30 [✋] [▶] **☉ Open** → [☾] [☐] [☒] [⧖]
100 Cannot delete foo 30 [✋] [▶] **⚹ New** → [☾] [☉] [☐] [⧖]
98 Foo never bars 30 [✋] [▶] **☒ Refused**
96 Default account in invoices per partner 30 [✋] [▶] **☐ Ready** → [☒]
94 How can I see where bar? 30 [✋] [▶] **⚒ Working** → [☾] [☐] [☒] [⧖]
92 Why is foo so bar 30 [✋] [▶] **☎ Talk** → [☾] [☉] [☐] [☒] [⧖]
90 No more foo when bar is gone 30 [✋] [▶] **⧖ Waiting**
88 'NoneType' object has no attribute 'isocode' 30 [✋] [▶] **☑ Closed** → [☉]
86 Misc optimizations in Baz 30 [✋] [▶] **☾ Sleeping** → [⧖]
84 Irritating message when bar 30 [✋] [▶] **☉ Open** → [☾] [☐] [☒] [⧖]
82 Cannot delete foo 30 [✋] [▶] **⚹ New** → [☾] [☉] [☐] [⧖]
80 Foo never bars 30 [✋] [▶] **☒ Refused**
78 Default account in invoices per partner 30 [✋] [▶] **☐ Ready** → [☒]
76 How can I see where bar? 30 [✋] [▶] **⚒ Working** → [☾] [☐] [☒] [⧖]
74 Why is foo so bar 30 [✋] [▶] **☎ Talk** → [☾] [☉] [☐] [☒] [⧖]
72 No more foo when bar is gone 30 [✋] [▶] **⧖ Waiting**
70 'NoneType' object has no attribute 'isocode' 30 [✋] [▶] **☑ Closed** → [☉]
68 Misc optimizations in Baz 30 [✋] [▶] **☾ Sleeping** → [⧖]
66 Irritating message when bar 30 [✋] [▶] **☉ Open** → [☾] [☐] [☒] [⧖]
64 Cannot delete foo 30 [✋] [▶] **⚹ New** → [☾] [☉] [☐] [⧖]
62 Foo never bars 30 [✋] [▶] **☒ Refused**
60 Default account in invoices per partner 30 [✋] [▶] **☐ Ready** → [☒]
58 How can I see where bar? 30 [✋] [▶] **⚒ Working** → [☾] [☐] [☒] [⧖]
56 Why is foo so bar 30 [✋] [▶] **☎ Talk** → [☾] [☉] [☐] [☒] [⧖]
54 No more foo when bar is gone 30 [✋] [▶] **⧖ Waiting**
52 'NoneType' object has no attribute 'isocode' 30 [✋] [▶] **☑ Closed** → [☉]
50 Misc optimizations in Baz 30 [✋] [▶] **☾ Sleeping** → [⧖]
48 Irritating message when bar 30 [✋] [▶] **☉ Open** → [☾] [☐] [☒] [⧖]
46 Cannot delete foo 30 [✋] [▶] **⚹ New** → [☾] [☉] [☐] [⧖]
44 Foo never bars 30 [✋] [▶] **☒ Refused**
42 Default account in invoices per partner 30 [✋] [▶] **☐ Ready** → [☒]
40 How can I see where bar? 30 [✋] [▶] **⚒ Working** → [☾] [☐] [☒] [⧖]
38 Why is foo so bar 30 [✋] [▶] **☎ Talk** → [☾] [☉] [☐] [☒] [⧖]
36 No more foo when bar is gone 30 [✋] [▶] **⧖ Waiting**
34 'NoneType' object has no attribute 'isocode' 30 [✋] [▶] **☑ Closed** → [☉]
32 Misc optimizations in Baz 30 [✋] [▶] **☾ Sleeping** → [⧖]
30 Irritating message when bar 30 [✋] [▶] **☉ Open** → [☾] [☐] [☒] [⧖]
28 Cannot delete foo 30 [✋] [▶] **⚹ New** → [☾] [☉] [☐] [⧖]
26 Foo never bars 30 [✋] [▶] **☒ Refused**
24 Default account in invoices per partner 30 [✋] [▶] **☐ Ready** → [☒]
22 How can I see where bar? 30 [✋] [▶] **⚒ Working** → [☾] [☐] [☒] [⧖]
20 Why is foo so bar 30 [✋] [▶] **☎ Talk** → [☾] [☉] [☐] [☒] [⧖]
18 No more foo when bar is gone 30 [✋] [▶] **⧖ Waiting**
16 How to get bar from foo 30 [✋] [▶] **☑ Closed** → [☉]
14 Bar cannot baz 30 [✋] [▶] **☾ Sleeping** → [⧖]
12 Foo cannot bar 30 [✋] [▶] **☉ Open** → [☾] [☐] [☒] [⧖]
10 Where can I find a Foo when bazing Bazes? 30 [✋] [▶] **⚹ New** → [☾] [☉] [☐] [⧖]
8 Is there any Bar in Foo? 30 [✋] [▶] **☒ Refused**
6 Sell bar in baz 30 [✋] [▶] **☐ Ready** → [☒]
4 Foo and bar don't baz 30 [✋] [▶] **⚒ Working** → [☾] [☐] [☒] [⧖]
2 Bar is not always baz 30 [✋] [▶] **☎ Talk** → [☾] [☉] [☐] [☒] [⧖]
===== ============================================== ========== =========================================== =========
Ticket types¶
The demo
fixture defines the following ticket types.
>>> rt.show(tickets.TicketTypes)
============= ================== ================== ================
Designation Designation (de) Designation (fr) Reporting type
------------- ------------------ ------------------ ----------------
Bugfix Bugfix Bugfix Regular
Enhancement Enhancement Enhancement Extra
Upgrade Upgrade Upgrade Regular
============= ================== ================== ================
Deciding what to do next¶
Show all active tickets reported by me.
>>> rt.login('marc').show(tickets.MyTicketsToWork)
...
========== ======================================= ========================================== =========
Priority Ticket Workflow Project
---------- --------------------------------------- ------------------------------------------ ---------
30 `#101 (Why is foo so bar) <Detail>`__ **☎ Talk** → [☾] [☉] [⚒] [☐] [☑] [☒] [⧖] bugs
30 `#83 (Why is foo so bar) <Detail>`__ **☎ Talk** admin
========== ======================================= ========================================== =========
>>> rt.login('jean').show(tickets.MyTickets)
...
========== ============================================================ ================= ============== ===================================================
Priority Ticket Assigned to Planned time Workflow
---------- ------------------------------------------------------------ ----------------- -------------- ---------------------------------------------------
30 `#92 (Why is foo so bar) <Detail>`__ [✋] [▶] **☎ Talk** → [☾] [☉] [☐] [☒] [⧖]
30 `#85 (How can I see where bar?) <Detail>`__ Mathieu [▶] **⚒ Working** → [☾] [☎] [☐] [☑] [☒] [⧖]
30 `#78 (Default account in invoices per partner) <Detail>`__ [✋] [▶] **☐ Ready** → [☒]
30 `#64 (Cannot delete foo) <Detail>`__ [✋] [▶] **⚹ New** → [☾] [☉] [☐] [⧖]
30 `#57 (Irritating message when bar) <Detail>`__ [✋] [▶] **☉ Open** → [☾] [☎] [⚒] [☐] [☑] [☒] [⧖]
30 `#29 (Why is foo so bar) <Detail>`__ Rolf Rompen [▶] **☎ Talk** → [☾] [☉] [⚒] [☐] [☑] [☒] [⧖]
30 `#22 (How can I see where bar?) <Detail>`__ [✋] [▶] **⚒ Working** → [☾] [☐] [☒] [⧖]
30 `#15 (Bars have no foo) <Detail>`__ Romain Raffault [▶] **☐ Ready** → [☎] [☑] [☒]
30 `#1 (Föö fails to bar when baz) <Detail>`__ [✋] [▶] **⚹ New** → [☾] [☎] [☉] [⚒] [☐] [☑] [⧖]
========== ============================================================ ================= ============== ===================================================
The backlog¶
The TicketsBySite
panel shows all the tickets for a given project. It
is a scrum backlog.
>>> pypi = tickets.Site.objects.get(ref="pypi")
>>> rt.login("robin").show(tickets.TicketsBySite, pypi)
...
==================== ============================================= ============== ============ ======= ============ ==================================================
Priority Ticket Planned time Regular Extra Free Workflow
-------------------- --------------------------------------------- -------------- ------------ ------- ------------ --------------------------------------------------
30 `#109 (Cannot delete foo) <Detail>`__ 124:17 [▶] **⚹ New** → [☾] [☎] [☉] [⚒] [☐] [☑] [⧖]
30 `#85 (How can I see where bar?) <Detail>`__ 122:33 [▶] **⚒ Working** → [☾] [☎] [☐] [☑] [☒] [⧖]
30 `#73 (Cannot delete foo) <Detail>`__ 126:53 [✋] [▶] **⚹ New** → [☾] [☎] [☉] [⚒] [☐] [☑] [⧖]
30 `#49 (How can I see where bar?) <Detail>`__ 127:09 [✋] [▶] **⚒ Working** → [☾] [☎] [☐] [☑] [☒] [⧖]
30 `#37 (Cannot delete foo) <Detail>`__ 124:41 174:42 [▶] **⚹ New** → [☾] [☎] [☉] [⚒] [☐] [☑] [⧖]
30 `#13 (Bar cannot foo) <Detail>`__ 125:40 174:46 [▶] **⚒ Working** → [☾] [☎] [☐] [☑] [☒] [⧖]
30 `#1 (Föö fails to bar when baz) <Detail>`__ 125:31 168:21 [✋] [▶] **⚹ New** → [☾] [☎] [☉] [⚒] [☐] [☑] [⧖]
**Total (7 rows)** **876:44** **517:49**
==================== ============================================= ============== ============ ======= ============ ==================================================
Anonymous cannot see tickets of a non-public project.
>>> security = tickets.Site.objects.get(ref="security")
>>> rt.show(tickets.TicketsBySite, security)
...
No data to display
Links between tickets¶
>>> obj = tickets.Ticket.objects.get(id=20)
>>> rt.show(tickets.TicketsByParent, obj)
...
========== ==== ============================= =================
Priority ID Summary Assign to
---------- ---- ----------------------------- -----------------
30 21 Irritating message when bar luc, **romain**
========== ==== ============================= =================
Filtering tickets¶
Lino Noi modifies the list of the parameters you can use for filterings
tickets be setting a custom params_layout
.
>>> show_fields(tickets.AllTickets, all=True)
...
- Author (user) : The author or reporter of this ticket. The user who reported this
ticket to the database and is responsible for managing it.
- End user (end_user) : Only rows concerning this end user.
- Assigned_to (assigned_to) : Only tickets with this user assigned.
- Not assigned to (not_assigned_to) : Only that this user is not assigned to.
- Interesting for (interesting_for) : Only tickets interesting for this partner.
- Project (site) : Show only tickets within this project.
- Has site (has_site) : Show only (or hide) tickets which have a site assigned.
- State (state) : Only rows having this state.
- Assigned (show_assigned) : Show only (or hide) tickets that are assigned to somebody.
- Active (show_active) : Show only (or hide) tickets which are active (i.e. state is Talk
or ToDo).
- To do (show_todo) : Show only (or hide) tickets that are todo (i.e. state is New
or ToDo).
- Private (show_private) : Show only (or hide) tickets that are marked private.
- Date from (start_date) : Start of observed date range
- until (end_date) : End of observed date range
- Observed event (observed_event) :
- Has reference (has_ref) :
- Commented Last (last_commenter) : Only tickets that have this use commenting last.
- Not Commented Last (not_last_commenter) : Only tickets where this use is not the last commenter.
- Site Subscriber (subscriber) : Limit tickets to tickets that have a site this user is subscribed to.
Change observers¶
Don't read this.
>>> ar = rt.login('robin')
>>> list(tickets.Site.objects.get(ref="docs").get_change_observers())
...
[(User #6 ('Luc'), <notify.MailModes.often:often>), (User #3 ('Romain
Raffault'), <notify.MailModes.often:often>)]
>>> list(comments.Comment.objects.get(pk=1).get_change_observers())
...
[(User #7 ('Jean'), <notify.MailModes.often:often>), (User #5 ('Mathieu'),
<notify.MailModes.often:often>), (User #1 ('Robin Rood'),
<notify.MailModes.often:often>)]
>>> list(comments.Comment.objects.get(pk=155).get_change_observers())
...
[(User #6 ('Luc'), <notify.MailModes.often:often>), (User #3 ('Romain
Raffault'), <notify.MailModes.often:often>)]