~launchpad-pqm/launchpad/devel

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
IFAQTarget Interface
====================

The Launchpad Answer Tracker can be used to track answers to commonly
asked questions. Regular user questions can then be answered with a
referral to the FAQ document.

Pillars that can host a FAQ provides the IFAQTarget.

    >>> from zope.interface.verify import verifyObject
    >>> from zope.security.proxy import removeSecurityProxy
    >>> from lp.answers.interfaces.faqtarget import IFAQTarget

    # NB: this test is called multiple times with different values for
    # 'target', which is setup by the testing framework.  It can be a
    # product or a distribution.

    # removeSecurityProxy() is needed because some attributes are
    # protected.

    >>> verifyObject(IFAQTarget, removeSecurityProxy(target))
    True


newFAQ()
--------

The newFAQ() method is used to create a new IFAQ object on the target.

That method is only available to a user who has 'launchpad.Append' on
the target.

    >>> login('no-priv@canonical.com')
    >>> from lp.services.webapp.authorization import check_permission
    >>> from lp.services.webapp.interfaces import ILaunchBag
    >>> check_permission('launchpad.Append', target)
    False

    >>> no_priv = getUtility(ILaunchBag).user
    >>> #target.newFAQ(no_priv, 'Title', 'Summary', content='Content')
    Traceback (most recent call last):
      ...
    Unauthorized: ...

In practice, this means that only the project's owner (aka maintainer)
or one of its answer contacts is authorized to create a new FAQ.

    >>> old_owner = target.owner

    >>> removeSecurityProxy(target).owner = no_priv
    >>> from lp.services.webapp.authorization import clear_cache
    >>> clear_cache() # Clear authorization cache for check_permission.
    >>> check_permission('launchpad.Append', target)
    True

    >>> removeSecurityProxy(target).owner = old_owner

    # An answer contact must have a preferred language registered.

    >>> from lp.services.worlddata.interfaces.language import ILanguageSet
    >>> no_priv.addLanguage(getUtility(ILanguageSet)['en'])
    >>> target.addAnswerContact(no_priv, no_priv)
    True

    >>> clear_cache() # Clear authorization cache for check_permission.
    >>> check_permission('launchpad.Append', target)
    True

    >>> faq = target.newFAQ(no_priv, 'Title', 'Content')

The returned object provides the IFAQ interface:

    >>> from lp.answers.interfaces.faq import IFAQ
    >>> verifyObject(IFAQ, faq)
    True

The newFAQ() requires an owner, title, and content parameter. It also
accepts an optional date_created attribute (which defaults to the
current time), and an optional keywords parameter used to initialize the
FAQ's keywords.

    >>> from datetime import datetime
    >>> from pytz import UTC
    >>> now = datetime.now(UTC)

    >>> faq = target.newFAQ(
    ...     no_priv, 'How to do something', 'Explain how to do something.',
    ...     keywords='documentation howto', date_created=now)

    >>> print faq.owner.displayname
    No Privileges Person

    >>> print faq.title
    How to do something

    >>> print faq.content
    Explain how to do something.

    >>> print faq.keywords
    documentation howto

    >>> faq.date_created == now
    True

The project where the FAQ was created is available through the target
attribute:

    >>> faq.target == target
    True


getFAQ()
--------

It is possible to retrieve the FAQ from its container when you know the
id of the FAQ by using the get() method.

    >>> target.getFAQ(faq.id) == faq
    True

It returns None when there is FAQ with that ID in the context:

    >>> print target.getFAQ(12345)
    None

It also returns None when asking an ID for a FAQ that isn't in the
requested target:

    # Create a FAQ on Ubuntu.

    >>> from lp.services.webapp.interfaces import ILaunchBag
    >>> from lp.registry.interfaces.distribution import IDistributionSet
    >>> ubuntu = getUtility(IDistributionSet).getByName('ubuntu')
    >>> ubuntu != target
    True

    >>> login('foo.bar@canonical.com')
    >>> foo_bar = getUtility(ILaunchBag).user
    >>> ubuntu_faq = ubuntu.newFAQ(
    ...     foo_bar, 'Ubuntu Installation HowTo',
    ...     'Ubuntu installation procedure can be found at: '
    ...     'https://help.ubuntu.com/community/Installation')

    >>> login('no-priv@canonical.com')
    >>> print target.getFAQ(ubuntu_faq.id)
    None


findSimilarFAQs()
-----------------

The method findSimilarFAQs() can be use to find FAQ document that are
likely to answer a particular question. The question's summary or a
sentence describing the issue should be given in parameter. The FAQ's
title, summary, keywords and content can be the source of the match.

This method uses a "natural language" search algorithm (see
lib/canonical/doc/textsearching.txt for the details) which ignore common
words and stop words.

    # Create more FAQs.

    >>> faq = target.newFAQ(
    ...     no_priv, 'How to answer a question',
    ...     'Description on how to use the Answer Tracker can be found at: '
    ...     'https://help.launchpad.net/AnswerTrackerDocumentation')
    >>> faq = target.newFAQ(
    ...     no_priv, 'How to become a Launchpad king',
    ...     'The secret to achieve uber-karma is to answer questions using '
    ...     'the Launchpad Answer Tracker')
    >>> faq = target.newFAQ(
    ...     no_priv, 'How to use bug mail',
    ...     'The syntax of bug mail commands is described at: '
    ...     'https://help.launchpad.net/BugTrackerEmailInterface')

    >>> for faq in target.findSimilarFAQs('How do I use the Answer Tracker'):
    ...     print faq.title
    How to answer a question
    How to become a Launchpad king

The results are ordered by relevancy. The first document is considered
more relevant because 'Answer Tracker' appeared in the summary (they
appear in the content in the other document).

If there are no similar FAQ, no result should be returned:

    >>> for faq in target.findSimilarFAQs('How do I do this?'):
    ...     print faq.title

Since only common and stop words are in that summary, no similar FAQ
could be found.