1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 __doc__ = \
21 """
22 pywurfl search algorithms
23 """
24
25 import re
26
27 from pywurfl.exceptions import DeviceNotFound
28 from pywurfl.algorithms.wurfl.handlers import handlers
29
30 import Levenshtein
31
32
33 __author__ = "Armand Lynch <lyncha@users.sourceforge.net>"
34 __copyright__ = "Copyright 2006-2010, Armand Lynch"
35 __license__ = "LGPL"
36 __url__ = "http://celljam.net/"
37
38
40 """
41 Base class for all pywurfl search algorithms
42 """
44 """
45 Every pywurfl algorithm class must define a __call__ method.
46
47 @param ua: The user agent
48 @type ua: string
49 @param devices: The devices object to search
50 @type devices: Devices
51 @rtype: Device
52 """
53 raise NotImplementedError
54
55
57 """
58 Jaro-Winkler Search Algorithm
59 """
60
61 - def __init__(self, accuracy=1.0, weight=0.05, use_normalized_ua=True):
62 """
63 @param accuracy: The tolerance that the Jaro-Winkler algorithm will
64 use to determine if a user agent matches
65 0.0 <= accuracy <= 1.0
66 @type accuracy: float
67 @param weight: The prefix weight is inverse value of common prefix
68 length sufficient to consider the strings
69 'identical' (excerpt from the Levenshtein module
70 documentation).
71 @type weight: float
72 @param use_normalized_ua: Specify whether or not that this algorithm
73 requires a normalized user agent for search.
74 @type use_normalized_ua: bool
75 """
76
77 self.accuracy = accuracy
78 self.weight = weight
79 self.use_normalized_ua = use_normalized_ua
80
82 """
83 @param ua: The user agent
84 @type ua: string
85 @param devices: The devices object to search
86 @type devices: Devices
87 @rtype: Device
88 @raises pywurfl.DeviceNotFound
89 """
90 match = max((Levenshtein.jaro_winkler(x, ua, self.weight), x) for
91 x in devices.devuas)
92 if match[0] >= self.accuracy:
93 return devices.devuas[match[1]]
94 else:
95 raise DeviceNotFound(ua)
96
97
99 """
100 Levenshtein distance Search Algorithm
101 """
102
103 - def __init__(self, use_normalized_ua=True):
104 """
105 @param use_normalized_ua: Specify whether or not that this algorithm
106 requires a normalized user agent for search.
107 @type use_normalized_ua: bool
108 """
109
110 self.use_normalized_ua = use_normalized_ua
111
113 """
114 @param ua: The user agent
115 @type ua: string
116 @param devices: The devices object to search
117 @type devices: Devices
118 @rtype: Device
119 """
120
121 match = min((Levenshtein.distance(ua, x), x) for x in
122 devices.devuas)
123 return devices.devuas[match[1]]
124
125
127 """
128 Tokenizer Search Algorithm
129 """
130 tokenize_chars = ('/', '.', ',', ';', '-', '_', ' ', '(', ')')
131 base_regex = '[\\'+'\\'.join(tokenize_chars)+']*'
132
133 - def __init__(self, devwindow=30, use_normalized_ua=True):
134 """
135 @param devwindow: If more than devwindow user agents match,
136 return empty device.
137 @type devwindow: integer
138 @param use_normalized_ua: Specify whether or not that this algorithm
139 requires a normalized user agent for search.
140 @type use_normalized_ua: bool
141 """
142 self.devwindow = devwindow
143 self.use_normalized_ua = use_normalized_ua
144
146 """
147 @param s: The user agent to tokenize
148 @type s: string
149 """
150 for d in self.tokenize_chars:
151 s = s.replace(d, ' ')
152 return [re.escape(x) for x in s.split()]
153
155 """
156 @param ua: The user agent
157 @type ua: string
158 @param devices: The devices object to search
159 @type devices: Devices
160 @rtype: Device
161 """
162 uas = devices.devuas.keys()
163 tokens = self._tokenize(ua)
164 regex = ''
165 for t in tokens:
166 if regex:
167 regex += self.base_regex + t
168 else:
169 regex += t
170
171 regex2 = regex + '.*'
172
173 uare = re.compile(regex2, re.I)
174 uas2 = [x for x in uas if uare.match(x)]
175
176
177
178
179
180 if len(uas2) == 0 and len(uas) > self.devwindow:
181 return devices.devids['generic']
182 elif len(uas2) == 0 and len(uas) <= self.devwindow:
183
184 return devices.devuas[uas[0]]
185
186
187 if len(uas2) == 1:
188
189 return devices.devuas[uas2[0]]
190
191
192 uas = uas2
193
194
195
196
197
198 if len(uas2) > self.devwindow:
199 return devices.devids['generic']
200 else:
201
202 return devices.devuas[uas2[0]]
203
204
206 """
207 WURFL Two Step Analysis algorithm based on the Java API implementation
208 """
209 - def __init__(self, devices, use_normalized_ua=False):
210 """
211 @param use_normalized_ua: Specify whether or not that this algorithm
212 requires a normalized user agent for search.
213 @type use_normalized_ua: bool
214 """
215 self.use_normalized_ua = use_normalized_ua
216 for ua in devices.uas:
217 for h in handlers:
218 if h.can_handle(ua):
219 h.add(ua, devices.devuas[ua].devid)
220 break
221
227
229 handler = self._determine_handler(ua)
230 match = handler(ua)
231 try:
232 return devices.devids[match]
233 except KeyError:
234
235
236
237
238 return devices.devids[u"generic"]
239