Merge lp:~stefanor/ibid/factoid-495700 into lp:~ibid-core/ibid/old-trunk-1.6

Proposed by Stefano Rivera
Status: Merged
Approved by: Jonathan Hitchcock
Approved revision: not available
Merged at revision: 801
Proposed branch: lp:~stefanor/ibid/factoid-495700
Merge into: lp:~ibid-core/ibid/old-trunk-1.6
Diff against target: 317 lines (+117/-60)
3 files modified
ibid/plugins/factoid.py (+104/-54)
ibid/plugins/log.py (+1/-1)
scripts/ibid-factpack (+12/-5)
To merge this branch: bzr merge lp:~stefanor/ibid/factoid-495700
Reviewer Review Type Date Requested Status
Jonathan Hitchcock Approve
Michael Gorven Approve
Review via email: mp+16073@code.launchpad.net
To post a comment you must log in.
lp:~stefanor/ibid/factoid-495700 updated
797. By Stefano Rivera

Remove some redundant code

Revision history for this message
Michael Gorven (mgorven) wrote :

 review approve

review: Approve
lp:~stefanor/ibid/factoid-495700 updated
798. By Stefano Rivera

gettatr doesn't work as expected on events

Revision history for this message
Jonathan Hitchcock (vhata) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ibid/plugins/factoid.py'
2--- ibid/plugins/factoid.py 2009-12-11 22:38:52 +0000
3+++ ibid/plugins/factoid.py 2009-12-14 13:43:11 +0000
4@@ -4,11 +4,13 @@
5 import re
6
7 from dateutil.tz import tzlocal, tzutc
8-from sqlalchemy import Column, Integer, Unicode, DateTime, ForeignKey, UnicodeText, Table, or_
9-from sqlalchemy.orm import relation
10+from sqlalchemy import Column, Table, ForeignKey, or_, Boolean, Integer, \
11+ Unicode, UnicodeText, DateTime
12+from sqlalchemy.orm import relation, synonym
13 from sqlalchemy.sql import func
14
15-from ibid.plugins import Processor, match, handler, authorise, auth_responses, RPC
16+from ibid.plugins import Processor, match, handler, authorise, auth_responses, \
17+ RPC
18 from ibid.config import Option, IntOption, ListOption
19 from ibid.plugins.identity import get_identities
20 from ibid.models import Base, VersionedSchema
21@@ -24,17 +26,27 @@
22 default_interrogatives = ('what', 'wtf', 'where', 'when', 'who', "what's", "who's")
23
24 def strip_name(unstripped):
25+ "Apply to factoid names, as we use unstripped matches"
26 return re.match(r'^\s*(.*?)[?!.]*\s*$', unstripped, re.DOTALL).group(1)
27
28+def escape_name(name):
29+ "Turn a $arg factoid name to _%"
30+ return name.replace('%', '\\%').replace('_', '\\_').replace('$arg', '_%')
31+
32+def unescape_name(name):
33+ "Turn a _% factoid name to $arg"
34+ return name.replace('_%', '$arg').replace('\\%', '%').replace('\\_', '_')
35+
36 class FactoidName(Base):
37 __table__ = Table('factoid_names', Base.metadata,
38 Column('id', Integer, primary_key=True),
39- Column('name', UnicodeText, nullable=False, unique=True, index=True,
40- info={'ibid_mysql_index_length': 32}),
41+ Column('name', UnicodeText, key='_name', nullable=False, unique=True, index=True,
42+ info={'ibid_mysql_index_length': 32}),
43 Column('factoid_id', Integer, ForeignKey('factoids.id'), nullable=False, index=True),
44 Column('identity_id', Integer, ForeignKey('identities.id'), index=True),
45 Column('time', DateTime, nullable=False),
46 Column('factpack', Integer, ForeignKey('factpacks.id'), index=True),
47+ Column('wild', Boolean, nullable=False, default=False, index=True),
48 useexisting=True)
49
50 class FactoidNameSchema(VersionedSchema):
51@@ -48,12 +60,25 @@
52 self.add_index(self.table.c.identity_id)
53 self.add_index(self.table.c.factpack)
54 def upgrade_4_to_5(self):
55- self.alter_column(Column('name', Unicode(64), nullable=False, unique=True, index=True))
56+ self.alter_column(Column('name', Unicode(64), key='_name', nullable=False,
57+ unique=True, index=True))
58 def upgrade_5_to_6(self):
59- self.alter_column(Column('name', UnicodeText, nullable=False,
60- unique=True, index=True, info={'ibid_mysql_index_length': 32}))
61+ self.alter_column(Column('name', UnicodeText, key='_name', nullable=False,
62+ unique=True, index=True,
63+ info={'ibid_mysql_index_length': 32}))
64+ def upgrade_6_to_7(self):
65+ self.add_column(Column('wild', Boolean, nullable=False, index=True,
66+ default=False, server_default='0'))
67+ # http://www.sqlalchemy.org/trac/ticket/1400:
68+ # We can't use .like() in MySQL
69+ for row in self.upgrade_session.query(FactoidName) \
70+ .filter('name LIKE :pattern ESCAPE :escape') \
71+ .params(pattern='%\\_\\%%', escape='\\') \
72+ .all():
73+ row.wild = True
74+ self.upgrade_session.save_or_update(row)
75
76- __table__.versioned_schema = FactoidNameSchema(__table__, 6)
77+ __table__.versioned_schema = FactoidNameSchema(__table__, 7)
78
79 def __init__(self, name, identity_id, factoid_id=None, factpack=None):
80 self.name = name
81@@ -65,6 +90,15 @@
82 def __repr__(self):
83 return u'<FactoidName %s %s>' % (self.name, self.factoid_id)
84
85+ def _get_name(self):
86+ return unescape_name(self._name)
87+
88+ def _set_name(self, name):
89+ self.wild = u'$arg' in name
90+ self._name = escape_name(name)
91+
92+ name = synonym('_name', descriptor=property(_get_name, _set_name))
93+
94 class FactoidValue(Base):
95 __table__ = Table('factoid_values', Base.metadata,
96 Column('id', Integer, primary_key=True),
97@@ -142,13 +176,8 @@
98 reply_re = re.compile(r'^\s*<reply>\s*')
99 escape_like_re = re.compile(r'([%_\\])')
100
101-def escape_name(name):
102- return name.replace('%', '\\%').replace('_', '\\_').replace('$arg', '_%')
103-
104-def unescape_name(name):
105- return name.replace('_%', '$arg').replace('\\%', '%').replace('\\_', '_')
106-
107-def get_factoid(session, name, number, pattern, is_regex, all=False, literal=False):
108+def get_factoid(session, name, number, pattern, is_regex, all=False,
109+ literal=False):
110 """session: SQLAlchemy session
111 name: Factoid name (can contain arguments unless literal query)
112 number: Return factoid[number] (or factoid[number:] for literal queries)
113@@ -157,34 +186,55 @@
114 all: Return a random factoid from the set if False
115 literal: Match factoid name literally (implies all)
116 """
117- factoid = None
118- # Reversed LIKE because factoid name contains SQL wildcards if factoid supports arguments
119- query = session.query(Factoid)\
120- .add_entity(FactoidName).join(Factoid.names)\
121- .add_entity(FactoidValue).join(Factoid.values)
122+ # First pass for exact matches, if necessary again for wildcard matches
123 if literal:
124- query = query.filter(func.lower(FactoidName.name)==escape_name(name).lower())
125- else:
126- query = query.filter(":fact LIKE name ESCAPE :escape").params(fact=name, escape='\\')
127- if pattern:
128- if is_regex:
129- query = query.filter(FactoidValue.value.op('REGEXP')(pattern))
130- else:
131- pattern = "%%%s%%" % escape_like_re.sub(r'\\\1', pattern)
132- # http://www.sqlalchemy.org/trac/ticket/1400: We can't use .like() in MySQL
133- query = query.filter('value LIKE :pattern ESCAPE :escape').params(pattern=pattern, escape='\\')
134- if number:
135- try:
136- if literal:
137- return query.order_by(FactoidValue.id)[int(number) - 1:]
138- else:
139- factoid = query.order_by(FactoidValue.id)[int(number) - 1]
140- except IndexError:
141- return
142- if all or literal:
143- return factoid and [factoid] or query.all()
144- else:
145- return factoid or query.order_by(func.random()).first()
146+ passes = (False,)
147+ else:
148+ passes = (False, True)
149+ for wild in passes:
150+ factoid = None
151+ query = session.query(Factoid)\
152+ .add_entity(FactoidName).join(Factoid.names)\
153+ .add_entity(FactoidValue).join(Factoid.values)
154+ if wild:
155+ # Reversed LIKE because factoid name contains SQL wildcards if
156+ # factoid supports arguments
157+ query = query.filter(':fact LIKE name ESCAPE :escape') \
158+ .params(fact=name, escape='\\')
159+ else:
160+ query = query.filter(func.lower(FactoidName.name) \
161+ == escape_name(name).lower())
162+ # For normal matches, restrict to the subset applicable
163+ if not literal:
164+ query = query.filter(FactoidName.wild == wild)
165+
166+ if pattern:
167+ if is_regex:
168+ query = query.filter(FactoidValue.value.op('REGEXP')(pattern))
169+ else:
170+ pattern = '%%%s%%' % escape_like_re.sub(r'\\\1', pattern)
171+ # http://www.sqlalchemy.org/trac/ticket/1400:
172+ # We can't use .like() in MySQL
173+ query = query.filter('value LIKE :pattern ESCAPE :escape') \
174+ .params(pattern=pattern, escape='\\')
175+
176+ if number:
177+ try:
178+ if literal:
179+ return query.order_by(FactoidValue.id)[int(number) - 1:]
180+ else:
181+ factoid = query.order_by(FactoidValue.id)[int(number) - 1]
182+ except IndexError:
183+ continue
184+ if all or literal:
185+ if factoid is not None:
186+ return [factoid]
187+ else:
188+ factoid = query.all()
189+ else:
190+ factoid = factoid or query.order_by(func.random()).first()
191+ if factoid:
192+ return factoid
193
194 class Utils(Processor):
195 u"""literal <name> [( #<from number> | /<pattern>/[r] )]"""
196@@ -286,7 +336,7 @@
197 event.addresponse(u"I already know stuff about %s", target)
198 return
199
200- name = FactoidName(escape_name(unicode(target)), event.identity)
201+ name = FactoidName(unicode(target), event.identity)
202 factoid.names.append(name)
203 event.session.save_or_update(factoid)
204 event.session.commit()
205@@ -347,7 +397,9 @@
206 matches = [match for match in query[start:start+limit]]
207
208 if matches:
209- event.addresponse(u'; '.join(u'%s [%s]' % (unescape_name(fname.name), len(factoid.values)) for factoid, fname in matches))
210+ event.addresponse(u'; '.join(
211+ u'%s [%s]' % (fname.name, len(factoid.values))
212+ for factoid, fname in matches))
213 else:
214 event.addresponse(u"I couldn't find anything that matched '%s'" % origpattern)
215
216@@ -401,14 +453,12 @@
217 if factoid:
218 (factoid, fname, fvalue) = factoid
219 reply = fvalue.value
220- pattern = re.escape(fname.name).replace(r'\_\%', '(.*)').replace('\\\\\\%', '%').replace('\\\\\\_', '_')
221+ oname = fname.name
222+ pattern = re.escape(fname.name).replace(r'\$arg', '(.*)')
223
224- position = 1
225- for capture in re.match(pattern, name, re.I).groups():
226- if capture.startswith('$arg'):
227- return
228- reply = reply.replace('$%s' % position, capture)
229- position = position + 1
230+ for i, capture in enumerate(re.match(pattern, name, re.I).groups()):
231+ reply = reply.replace('$%s' % (i + 1), capture)
232+ oname = oname.replace('$arg', capture, 1)
233
234 reply = _interpolate(reply, event)
235
236@@ -420,7 +470,7 @@
237 if count:
238 return {'address': False, 'reply': reply}
239
240- reply = u'%s %s' % (unescape_name(fname.name), reply)
241+ reply = u'%s %s' % (oname, reply)
242 return reply
243
244 class Set(Processor):
245@@ -473,7 +523,7 @@
246 return
247 else:
248 factoid = Factoid()
249- fname = FactoidName(escape_name(unicode(name)), event.identity)
250+ fname = FactoidName(unicode(name), event.identity)
251 factoid.names.append(fname)
252 event.session.save_or_update(factoid)
253 event.session.flush()
254
255=== modified file 'ibid/plugins/log.py'
256--- ibid/plugins/log.py 2009-12-11 14:26:39 +0000
257+++ ibid/plugins/log.py 2009-12-14 13:43:11 +0000
258@@ -64,7 +64,7 @@
259
260 file = open(filename, 'a')
261 self.logs[filename] = file
262- if getattr(event, 'public', True):
263+ if event.get('public', True):
264 chmod(filename, int(self.public_mode, 8))
265 else:
266 chmod(filename, int(self.private_mode, 8))
267
268=== modified file 'scripts/ibid-factpack'
269--- scripts/ibid-factpack 2009-10-19 15:07:40 +0000
270+++ scripts/ibid-factpack 2009-12-14 13:43:11 +0000
271@@ -9,7 +9,8 @@
272 import ibid
273 from ibid.compat import json
274 from ibid.config import FileConfig
275-from ibid.plugins.factoid import Factoid, FactoidName, FactoidValue, Factpack, escape_name
276+from ibid.plugins.factoid import Factoid, FactoidName, FactoidValue, Factpack, \
277+ escape_name
278
279 parser = OptionParser(usage=u'%prog <factpack>')
280 parser.add_option('-r', '--remove', action='store_true', help='Remove the named factpack from the database')
281@@ -45,7 +46,8 @@
282 session.delete(factoid)
283
284 if extras and not options.force:
285- print >> stderr, u'The following factoid entries have been added. Use -f to force removal.'
286+ print >> stderr, u'The following factoid entries have been added. ' \
287+ u'Use -f to force removal.'
288 for extra in extras:
289 print extra
290 exit(6)
291@@ -80,10 +82,13 @@
292 for names, values in facts:
293 factoid = Factoid(factpack.id)
294 for name in names:
295- if session.query(FactoidName).filter(func.lower(FactoidName.name)==escape_name(name).lower()).first():
296+ if session.query(FactoidName) \
297+ .filter(func.lower(FactoidName.name)
298+ == escape_name(name).lower()) \
299+ .first():
300 existing.append(name)
301 continue
302- fname = FactoidName(escape_name(unicode(name)), None, factpack=factpack.id)
303+ fname = FactoidName(unicode(name), None, factpack=factpack.id)
304 factoid.names.append(fname)
305 for value in values:
306 fvalue = FactoidValue(unicode(value), None, factpack=factpack.id)
307@@ -92,7 +97,9 @@
308 session.save(factoid)
309
310 if existing and not options.skip:
311- print >> stderr, u'The following factoids already exist in the database. Please remove them before importing this factpack, or use -s to skip them'
312+ print >> stderr, u'The following factoids already exist in the database. ' \
313+ u'Please remove them before importing this factpack, ' \
314+ u'or use -s to skip them'
315 for name in existing:
316 print >> stderr, name
317 session.rollback()

Subscribers

People subscribed via source and target branches