1
2 """GNUmed person searching code."""
3
4 __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
5 __license__ = "GPL"
6
7
8 import sys, logging, re as regex
9
10
11
12 if __name__ == '__main__':
13 sys.path.insert(0, '../../')
14 from Gnumed.pycommon import gmPG2, gmI18N, gmTools, gmDateTime
15 from Gnumed.business import gmPerson
16
17
18 _log = logging.getLogger('gm.person')
19
21 """UI independant i18n aware patient searcher."""
23 self._generate_queries = self._generate_queries_de
24
25 self.conn = gmPG2.get_connection()
26 self.curs = self.conn.cursor()
27
29 try:
30 self.curs.close()
31 except: pass
32 try:
33 self.conn.close()
34 except: pass
35
36
37
38 - def get_patients(self, search_term = None, a_locale = None, dto = None):
39 identities = self.get_identities(search_term, a_locale, dto)
40 if identities is None:
41 return None
42 return [ gmPerson.cPatient(aPK_obj=ident['pk_identity']) for ident in identities ]
43
44 - def get_identities(self, search_term = None, a_locale = None, dto = None):
45 """Get patient identity objects for given parameters.
46
47 - either search term or search dict
48 - dto contains structured data that doesn't need to be parsed (cDTO_person)
49 - dto takes precedence over search_term
50 """
51 parse_search_term = (dto is None)
52
53 if not parse_search_term:
54 queries = self._generate_queries_from_dto(dto)
55 if queries is None:
56 parse_search_term = True
57 if len(queries) == 0:
58 parse_search_term = True
59
60 if parse_search_term:
61
62 if a_locale is not None:
63 print "temporary change of locale on patient search not implemented"
64 _log.warning("temporary change of locale on patient search not implemented")
65
66 if search_term is None:
67 raise ValueError('need search term (dto AND search_term are None)')
68
69 queries = self._generate_queries(search_term)
70
71
72 if len(queries) == 0:
73 _log.error('query tree empty')
74 _log.error('[%s] [%s] [%s]' % (search_term, a_locale, str(dto)))
75 return None
76
77
78 identities = []
79
80 for query in queries:
81 _log.debug("running %s" % query)
82 try:
83 rows, idx = gmPG2.run_ro_queries(queries = [query], get_col_idx=True)
84 except:
85 _log.exception('error running query')
86 continue
87 if len(rows) == 0:
88 continue
89 identities.extend (
90 [ gmPerson.cIdentity(row = {'pk_field': 'pk_identity', 'data': row, 'idx': idx}) for row in rows ]
91 )
92
93 pks = []
94 unique_identities = []
95 for identity in identities:
96 if identity['pk_identity'] in pks:
97 continue
98 pks.append(identity['pk_identity'])
99 unique_identities.append(identity)
100
101 return unique_identities
102
103
104
106 """Transform some characters into a regex."""
107 if aString.strip() == u'':
108 return aString
109
110
111 normalized = aString.replace(u'Ä', u'(Ä|AE|Ae|A|E)')
112 normalized = normalized.replace(u'Ö', u'(Ö|OE|Oe|O)')
113 normalized = normalized.replace(u'Ü', u'(Ü|UE|Ue|U)')
114 normalized = normalized.replace(u'ä', u'(ä|ae|e|a)')
115 normalized = normalized.replace(u'ö', u'(ö|oe|o)')
116 normalized = normalized.replace(u'ü', u'(ü|ue|u|y)')
117 normalized = normalized.replace(u'ß', u'(ß|sz|ss|s)')
118
119
120
121 normalized = normalized.replace(u'é', u'***DUMMY***')
122 normalized = normalized.replace(u'è', u'***DUMMY***')
123 normalized = normalized.replace(u'***DUMMY***', u'(é|e|è|ä|ae)')
124
125
126 normalized = normalized.replace(u'v', u'***DUMMY***')
127 normalized = normalized.replace(u'f', u'***DUMMY***')
128 normalized = normalized.replace(u'ph', u'***DUMMY***')
129 normalized = normalized.replace(u'***DUMMY***', u'(v|f|ph)')
130
131
132 normalized = normalized.replace(u'Th',u'***DUMMY***')
133 normalized = normalized.replace(u'T', u'***DUMMY***')
134 normalized = normalized.replace(u'***DUMMY***', u'(Th|T)')
135 normalized = normalized.replace(u'th', u'***DUMMY***')
136 normalized = normalized.replace(u't', u'***DUMMY***')
137 normalized = normalized.replace(u'***DUMMY***', u'(th|t)')
138
139
140 normalized = normalized.replace(u'"', u'***DUMMY***')
141 normalized = normalized.replace(u"'", u'***DUMMY***')
142 normalized = normalized.replace(u'`', u'***DUMMY***')
143 normalized = normalized.replace(u'***DUMMY***', u"""("|'|`|***DUMMY***|\s)*""")
144 normalized = normalized.replace(u'-', u"""(-|\s)*""")
145 normalized = normalized.replace(u'|***DUMMY***|', u'|-|')
146
147 if aggressive:
148 pass
149
150
151 _log.debug('[%s] -> [%s]' % (aString, normalized))
152
153 return normalized
154
155
156
157
158
159
160
162 """Compose queries if search term seems unambigous."""
163 queries = []
164
165 raw = raw.rstrip(u',').rstrip(u';')
166
167
168 if regex.match(u"^(\s|\t)*\d+(\s|\t)*$", raw, flags = regex.LOCALE | regex.UNICODE):
169 _log.debug("[%s]: a PK or DOB" % raw)
170 tmp = raw.strip()
171 queries.append ({
172 'cmd': u"SELECT *, %s::text AS match_type FROM dem.v_basic_person WHERE pk_identity = %s ORDER BY lastnames, firstnames, dob",
173 'args': [_('internal patient ID'), tmp]
174 })
175 queries.append ({
176 'cmd': u"SELECT *, %s::text AS match_type FROM dem.v_basic_person WHERE dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone) ORDER BY lastnames, firstnames, dob",
177 'args': [_('date of birth'), tmp.replace(',', '.')]
178 })
179 queries.append ({
180 'cmd': u"""
181 SELECT vba.*, %s::text AS match_type FROM dem.lnk_identity2ext_id li2ext_id, dem.v_basic_person vba
182 WHERE vba.pk_identity = li2ext_id.id_identity and lower(li2ext_id.external_id) ~* lower(%s)
183 ORDER BY lastnames, firstnames, dob""",
184 'args': [_('external patient ID'), tmp]
185 })
186 return queries
187
188
189 if regex.match(u"^(\d|\s|\t)+$", raw, flags = regex.LOCALE | regex.UNICODE):
190 _log.debug("[%s]: a DOB or PK" % raw)
191 queries.append ({
192 'cmd': u"SELECT *, %s::text AS match_type FROM dem.v_basic_person WHERE dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone) ORDER BY lastnames, firstnames, dob",
193 'args': [_('date of birth'), raw.replace(',', '.')]
194 })
195 tmp = raw.replace(u' ', u'')
196 tmp = tmp.replace(u'\t', u'')
197 queries.append ({
198 'cmd': u"SELECT *, %s::text AS match_type FROM dem.v_basic_person WHERE pk_identity LIKE %s%%",
199 'args': [_('internal patient ID'), tmp]
200 })
201 return queries
202
203
204 if regex.match(u"^(\s|\t)*#(\d|\s|\t)+$", raw, flags = regex.LOCALE | regex.UNICODE):
205 _log.debug("[%s]: a PK or external ID" % raw)
206 tmp = raw.replace(u'#', u'')
207 tmp = tmp.strip()
208 tmp = tmp.replace(u' ', u'')
209 tmp = tmp.replace(u'\t', u'')
210
211 queries.append ({
212 'cmd': u"SELECT *, %s::text AS match_type FROM dem.v_basic_person WHERE pk_identity = %s ORDER BY lastnames, firstnames, dob",
213 'args': [_('internal patient ID'), tmp]
214 })
215
216 tmp = raw.replace(u'#', u'')
217 tmp = tmp.strip()
218 tmp = tmp.replace(u' ', u'***DUMMY***')
219 tmp = tmp.replace(u'\t', u'***DUMMY***')
220 tmp = tmp.replace(u'***DUMMY***', u'(\s|\t|-|/)*')
221 queries.append ({
222 'cmd': u"""
223 SELECT vba.*, %s::text AS match_type FROM dem.lnk_identity2ext_id li2ext_id, dem.v_basic_person vba
224 WHERE vba.pk_identity = li2ext_id.id_identity and lower(li2ext_id.external_id) ~* lower(%s)
225 ORDER BY lastnames, firstnames, dob""",
226 'args': [_('external patient ID'), tmp]
227 })
228 return queries
229
230
231 if regex.match(u"^(\s|\t)*#.+$", raw, flags = regex.LOCALE | regex.UNICODE):
232 _log.debug("[%s]: an external ID" % raw)
233 tmp = raw.replace(u'#', u'')
234 tmp = tmp.strip()
235 tmp = tmp.replace(u' ', u'***DUMMY***')
236 tmp = tmp.replace(u'\t', u'***DUMMY***')
237 tmp = tmp.replace(u'-', u'***DUMMY***')
238 tmp = tmp.replace(u'/', u'***DUMMY***')
239 tmp = tmp.replace(u'***DUMMY***', u'(\s|\t|-|/)*')
240 queries.append ({
241 'cmd': u"""
242 SELECT
243 vba.*,
244 %s::text AS match_type
245 FROM
246 dem.lnk_identity2ext_id li2ext_id,
247 dem.v_basic_person vba
248 WHERE
249 vba.pk_identity = li2ext_id.id_identity
250 AND
251 lower(li2ext_id.external_id) ~* lower(%s)
252 ORDER BY
253 lastnames, firstnames, dob""",
254 'args': [_('external patient ID'), tmp]
255 })
256 return queries
257
258
259 if regex.match(u"^(\s|\t)*\d+(\s|\t|\.|\-|/)*\d+(\s|\t|\.|\-|/)*\d+(\s|\t|\.)*$", raw, flags = regex.LOCALE | regex.UNICODE):
260 _log.debug("[%s]: a DOB" % raw)
261 tmp = raw.strip()
262 while u'\t\t' in tmp: tmp = tmp.replace(u'\t\t', u' ')
263 while u' ' in tmp: tmp = tmp.replace(u' ', u' ')
264
265
266
267 queries.append ({
268 'cmd': u"SELECT *, %s AS match_type FROM dem.v_basic_person WHERE dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone) ORDER BY lastnames, firstnames, dob",
269 'args': [_('date of birth'), tmp.replace(',', '.')]
270 })
271 return queries
272
273
274 if regex.match(u"^(\s|\t)*,(\s|\t)*([^0-9])+(\s|\t)*$", raw, flags = regex.LOCALE | regex.UNICODE):
275 _log.debug("[%s]: a firstname" % raw)
276 tmp = self._normalize_soundalikes(raw[1:].strip())
277 cmd = u"""
278 SELECT DISTINCT ON (pk_identity) * FROM (
279 SELECT *, %s AS match_type FROM ((
280 SELECT vbp.*
281 FROM dem.names, dem.v_basic_person vbp
282 WHERE dem.names.firstnames ~ %s and vbp.pk_identity = dem.names.id_identity
283 ) union all (
284 SELECT vbp.*
285 FROM dem.names, dem.v_basic_person vbp
286 WHERE dem.names.firstnames ~ %s and vbp.pk_identity = dem.names.id_identity
287 )) AS super_list ORDER BY lastnames, firstnames, dob
288 ) AS sorted_list"""
289 queries.append ({
290 'cmd': cmd,
291 'args': [_('first name'), '^' + gmTools.capitalize(tmp, mode=gmTools.CAPS_NAMES), '^' + tmp]
292 })
293 return queries
294
295
296 if regex.match(u"^(\s|\t)*(\*|\$).+$", raw, flags = regex.LOCALE | regex.UNICODE):
297 _log.debug("[%s]: a DOB" % raw)
298 tmp = raw.replace(u'*', u'')
299 tmp = tmp.replace(u'$', u'')
300 queries.append ({
301 'cmd': u"SELECT *, %s AS match_type FROM dem.v_basic_person WHERE dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone) ORDER BY lastnames, firstnames, dob",
302 'args': [_('date of birth'), tmp.replace(u',', u'.')]
303 })
304 return queries
305
306 return queries
307
308
309
311 """Generate generic queries.
312
313 - not locale dependant
314 - data -> firstnames, lastnames, dob, gender
315 """
316 _log.debug(u'_generate_queries_from_dto("%s")' % dto)
317
318 if not isinstance(dto, gmPerson.cDTO_person):
319 return None
320
321 vals = [_('name, gender, date of birth')]
322 where_snippets = []
323
324 vals.append(dto.firstnames)
325 where_snippets.append(u'firstnames=%s')
326 vals.append(dto.lastnames)
327 where_snippets.append(u'lastnames=%s')
328
329 if dto.dob is not None:
330 vals.append(dto.dob)
331
332 where_snippets.append(u"dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s)")
333
334 if dto.gender is not None:
335 vals.append(dto.gender)
336 where_snippets.append('gender=%s')
337
338
339 if len(where_snippets) == 0:
340 _log.error('invalid search dict structure')
341 _log.debug(data)
342 return None
343
344 cmd = u"""
345 SELECT *, %%s AS match_type FROM dem.v_basic_person
346 WHERE pk_identity in (
347 SELECT id_identity FROM dem.names WHERE %s
348 ) ORDER BY lastnames, firstnames, dob""" % ' and '.join(where_snippets)
349
350 queries = [
351 {'cmd': cmd, 'args': vals}
352 ]
353
354
355
356 return queries
357
358
359
361
362 if search_term is None:
363 return []
364
365
366 queries = self._generate_simple_query(search_term)
367 if len(queries) > 0:
368 return queries
369
370
371 _log.debug('[%s]: not a search term with a "suggestive" structure' % search_term)
372
373 search_term = search_term.strip(u',').strip(u';')
374 normalized = self._normalize_soundalikes(search_term)
375
376 queries = []
377
378
379
380 if regex.match(u"^(\s|\t)*[a-zäöüßéáúóçøA-ZÄÖÜÇØ]+(\s|\t)*$", search_term, flags = regex.LOCALE | regex.UNICODE):
381
382 cmd = u"""
383 SELECT DISTINCT ON (pk_identity) * FROM (
384 SELECT * FROM ((
385 SELECT vbp.*, %s::text AS match_type FROM dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and lower(n.lastnames) ~* lower(%s)
386 ) union all (
387 -- first name
388 SELECT vbp.*, %s::text AS match_type FROM dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and lower(n.firstnames) ~* lower(%s)
389 ) union all (
390 -- anywhere in name
391 SELECT vbp.*, %s::text AS match_type FROM dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and lower(n.firstnames || n.lastnames || coalesce(n.preferred, '')) ~* lower(%s)
392 )) AS super_list ORDER BY lastnames, firstnames, dob
393 ) AS sorted_list"""
394 tmp = normalized.strip()
395 args = []
396 args.append(_('last name'))
397 args.append('^' + tmp)
398 args.append(_('first name'))
399 args.append('^' + tmp)
400 args.append(_('any name part'))
401 args.append(tmp)
402
403 queries.append ({
404 'cmd': cmd,
405 'args': args
406 })
407 return queries
408
409
410 parts_list = regex.split(u",|;", normalized)
411
412
413 parts_list = [ p.strip() for p in parts_list if p.strip() != u'' ]
414
415
416 if len(parts_list) == 1:
417
418 sub_parts_list = regex.split(u"\s*|\t*", normalized)
419
420
421 date_count = 0
422 name_parts = []
423 for part in sub_parts_list:
424
425
426 if regex.search(u"\d", part, flags = regex.LOCALE | regex.UNICODE):
427 date_count = date_count + 1
428 date_part = part
429 else:
430 name_parts.append(part)
431
432
433 if len(sub_parts_list) == 2:
434
435 if date_count == 0:
436
437 queries.append ({
438 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text AS match_type FROM dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and n.firstnames ~ %s AND n.lastnames ~ %s",
439 'args': [_('name: first-last'), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES)]
440 })
441 queries.append ({
442 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text AS match_type FROM dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and lower(n.firstnames) ~* lower(%s) AND lower(n.lastnames) ~* lower(%s)",
443 'args': [_('name: first-last'), '^' + name_parts[0], '^' + name_parts[1]]
444 })
445
446 queries.append ({
447 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text AS match_type FROM dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and n.firstnames ~ %s AND n.lastnames ~ %s",
448 'args': [_('name: last-first'), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES)]
449 })
450 queries.append ({
451 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text AS match_type FROM dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and lower(n.firstnames) ~* lower(%s) AND lower(n.lastnames) ~* lower(%s)",
452 'args': [_('name: last-first'), '^' + name_parts[1], '^' + name_parts[0]]
453 })
454
455 queries.append ({
456 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text AS match_type FROM dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and lower(n.firstnames || n.lastnames) ~* lower(%s) AND lower(n.firstnames || n.lastnames) ~* lower(%s)",
457 'args': [_('name'), name_parts[0], name_parts[1]]
458 })
459 return queries
460
461 _log.error("don't know how to generate queries for [%s]" % search_term)
462 return queries
463
464
465 if len(sub_parts_list) == 3:
466
467 if date_count == 1:
468
469 queries.append ({
470 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text AS match_type FROM dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and n.firstnames ~ %s AND n.lastnames ~ %s AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
471 'args': [_('names: first-last, date of birth'), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES), date_part.replace(u',', u'.')]
472 })
473 queries.append ({
474 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text AS match_type FROM dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and lower(n.firstnames) ~* lower(%s) AND lower(n.lastnames) ~* lower(%s) AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
475 'args': [_('names: first-last, date of birth'), '^' + name_parts[0], '^' + name_parts[1], date_part.replace(u',', u'.')]
476 })
477
478 queries.append ({
479 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text AS match_type FROM dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and n.firstnames ~ %s AND n.lastnames ~ %s AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
480 'args': [_('names: last-first, date of birth'), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES), date_part.replace(u',', u'.')]
481 })
482 queries.append ({
483 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text AS match_type FROM dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and lower(n.firstnames) ~* lower(%s) AND lower(n.lastnames) ~* lower(%s) AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
484 'args': [_('names: last-first, dob'), '^' + name_parts[1], '^' + name_parts[0], date_part.replace(u',', u'.')]
485 })
486
487 queries.append ({
488 'cmd': u"SELECT DISTINCT ON (id_identity) vbp.*, %s::text AS match_type FROM dem.v_basic_person vbp, dem.names n WHERE vbp.pk_identity = n.id_identity and lower(n.firstnames || n.lastnames) ~* lower(%s) AND lower(n.firstnames || n.lastnames) ~* lower(%s) AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
489 'args': [_('name, date of birth'), name_parts[0], name_parts[1], date_part.replace(u',', u'.')]
490 })
491 return queries
492
493 queries.append(self._generate_dumb_brute_query(search_term))
494 return queries
495
496
497 queries.append(self._generate_dumb_brute_query(search_term))
498 return queries
499
500
501 else:
502
503 date_parts = []
504 name_parts = []
505 name_count = 0
506 for part in parts_list:
507
508 if regex.search(u"\d+", part, flags = regex.LOCALE | regex.UNICODE):
509
510 date_parts.append(part)
511 else:
512 tmp = part.strip()
513 tmp = regex.split(u"\s*|\t*", tmp)
514 name_count = name_count + len(tmp)
515 name_parts.append(tmp)
516
517 where_parts = []
518
519
520 if (len(name_parts) == 1) and (name_count == 2):
521
522 where_parts.append ({
523 'conditions': u"firstnames ~ %s and lastnames ~ %s",
524 'args': [_('names: first last'), '^' + gmTools.capitalize(name_parts[0][0], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0][1], mode=gmTools.CAPS_NAMES)]
525 })
526 where_parts.append ({
527 'conditions': u"lower(firstnames) ~* lower(%s) and lower(lastnames) ~* lower(%s)",
528 'args': [_('names: first last'), '^' + name_parts[0][0], '^' + name_parts[0][1]]
529 })
530
531 where_parts.append ({
532 'conditions': u"firstnames ~ %s and lastnames ~ %s",
533 'args': [_('names: last, first'), '^' + gmTools.capitalize(name_parts[0][1], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0][0], mode=gmTools.CAPS_NAMES)]
534 })
535 where_parts.append ({
536 'conditions': u"lower(firstnames) ~* lower(%s) and lower(lastnames) ~* lower(%s)",
537 'args': [_('names: last, first'), '^' + name_parts[0][1], '^' + name_parts[0][0]]
538 })
539
540 where_parts.append ({
541 'conditions': u"lower(firstnames || lastnames) ~* lower(%s) OR lower(firstnames || lastnames) ~* lower(%s)",
542 'args': [_('name'), name_parts[0][0], name_parts[0][1]]
543 })
544
545
546 elif len(name_parts) == 2:
547
548 where_parts.append ({
549 'conditions': u"firstnames ~ %s AND lastnames ~ %s",
550 'args': [_('name: last, first'), '^' + ' '.join(map(gmTools.capitalize, name_parts[1])), '^' + ' '.join(map(gmTools.capitalize, name_parts[0]))]
551 })
552 where_parts.append ({
553 'conditions': u"lower(firstnames) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
554 'args': [_('name: last, first'), '^' + ' '.join(name_parts[1]), '^' + ' '.join(name_parts[0])]
555 })
556
557 where_parts.append ({
558 'conditions': u"firstnames ~ %s AND lastnames ~ %s",
559 'args': [_('name: last, first'), '^' + ' '.join(map(gmTools.capitalize, name_parts[0])), '^' + ' '.join(map(gmTools.capitalize, name_parts[1]))]
560 })
561 where_parts.append ({
562 'conditions': u"lower(firstnames) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
563 'args': [_('name: last, first'), '^' + ' '.join(name_parts[0]), '^' + ' '.join(name_parts[1])]
564 })
565
566 where_parts.append ({
567 'conditions': u"lower(firstnames || lastnames) ~* lower(%s) AND lower(firstnames || lastnames) ~* lower(%s)",
568 'args': [_('name'), ' '.join(name_parts[0]), ' '.join(name_parts[1])]
569 })
570
571
572 else:
573
574 if len(name_parts) == 1:
575 for part in name_parts[0]:
576 where_parts.append ({
577 'conditions': u"lower(firstnames || lastnames) ~* lower(%s)",
578 'args': [_('name'), part]
579 })
580 else:
581 tmp = []
582 for part in name_parts:
583 tmp.append(' '.join(part))
584 for part in tmp:
585 where_parts.append ({
586 'conditions': u"lower(firstnames || lastnames) ~* lower(%s)",
587 'args': [_('name'), part]
588 })
589
590
591
592 if len(date_parts) == 1:
593 if len(where_parts) == 0:
594 where_parts.append ({
595 'conditions': u"dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
596 'args': [_('date of birth'), date_parts[0].replace(u',', u'.')]
597 })
598 if len(where_parts) > 0:
599 where_parts[0]['conditions'] += u" AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)"
600 where_parts[0]['args'].append(date_parts[0].replace(u',', u'.'))
601 where_parts[0]['args'][0] += u', ' + _('date of birth')
602 if len(where_parts) > 1:
603 where_parts[1]['conditions'] += u" AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)"
604 where_parts[1]['args'].append(date_parts[0].replace(u',', u'.'))
605 where_parts[1]['args'][0] += u', ' + _('date of birth')
606 elif len(date_parts) > 1:
607 if len(where_parts) == 0:
608 where_parts.append ({
609 'conditions': u"dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp witih time zone) AND dem.date_trunc_utc('day'::text, dem.identity.deceased) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
610 'args': [_('date of birth/death'), date_parts[0].replace(u',', u'.'), date_parts[1].replace(u',', u'.')]
611 })
612 if len(where_parts) > 0:
613 where_parts[0]['conditions'] += u" AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone) AND dem.date_trunc_utc('day'::text, dem.identity.deceased) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
614 where_parts[0]['args'].append(date_parts[0].replace(u',', u'.'), date_parts[1].replace(u',', u'.'))
615 where_parts[0]['args'][0] += u', ' + _('date of birth/death')
616 if len(where_parts) > 1:
617 where_parts[1]['conditions'] += u" AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone) AND dem.date_trunc_utc('day'::text, dem.identity.deceased) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
618 where_parts[1]['args'].append(date_parts[0].replace(u',', u'.'), date_parts[1].replace(u',', u'.'))
619 where_parts[1]['args'][0] += u', ' + _('date of birth/death')
620
621
622 for where_part in where_parts:
623 queries.append ({
624 'cmd': u"SELECT *, %%s::text AS match_type FROM dem.v_basic_person WHERE %s" % where_part['conditions'],
625 'args': where_part['args']
626 })
627 return queries
628
629 return []
630
632
633 _log.debug('_generate_dumb_brute_query("%s")' % search_term)
634
635 where_clause = ''
636 args = []
637
638 for arg in search_term.strip().split():
639 where_clause += u" AND lower(coalesce(vbp.title, '') || vbp.firstnames || vbp.lastnames) ~* lower(%s)"
640 args.append(arg)
641
642 query = u"""
643 SELECT DISTINCT ON (pk_identity) * FROM (
644 SELECT
645 vbp.*,
646 '%s'::text AS match_type
647 FROM
648 dem.v_basic_person vbp,
649 dem.names n
650 WHERE
651 vbp.pk_identity = n.id_identity
652 %s
653 ORDER BY
654 lastnames,
655 firstnames,
656 dob
657 ) AS ordered_list""" % (_('full name'), where_clause)
658
659 return ({'cmd': query, 'args': args})
660
662 """Text mode UI function to ask for patient."""
663
664 person_searcher = cPatientSearcher_SQL()
665
666 while True:
667 search_fragment = gmTools.prompted_input(prompt = "\nEnter person search term or leave blank to exit")
668
669 if search_fragment in ['exit', 'quit', 'bye', None]:
670 print "user cancelled patient search"
671 return None
672
673 pats = person_searcher.get_patients(search_term = search_fragment)
674
675 if (pats is None) or (len(pats) == 0):
676 print "No patient matches the query term."
677 print ""
678 continue
679
680 if len(pats) > 1:
681 print "Several patients match the query term:"
682 print ""
683 for pat in pats:
684 print pat
685 print ""
686 continue
687
688 return pats[0]
689
690 return None
691
692
693
694 if __name__ == '__main__':
695
696 if len(sys.argv) == 1:
697 sys.exit()
698
699 if sys.argv[1] != 'test':
700 sys.exit()
701
702 import datetime
703
704 gmI18N.activate_locale()
705 gmI18N.install_domain()
706 gmDateTime.init()
707
708
721
723 searcher = cPatientSearcher_SQL()
724
725 print "testing _normalize_soundalikes()"
726 print "--------------------------------"
727
728 data = [u'Krüger', u'Krueger', u'Kruger', u'Überle', u'Böger', u'Boger', u'Öder', u'Ähler', u'Däler', u'Großer', u'müller', u'Özdemir', u'özdemir']
729 for name in data:
730 print '%s: %s' % (name, searcher._normalize_soundalikes(name))
731
732 raw_input('press [ENTER] to continue')
733 print "============"
734
735 print "testing _generate_simple_query()"
736 print "----------------------------"
737 data = ['51234', '1 134 153', '#13 41 34', '#3-AFY322.4', '22-04-1906', '1235/32/3525', ' , johnny']
738 for fragment in data:
739 print "fragment:", fragment
740 qs = searcher._generate_simple_query(fragment)
741 for q in qs:
742 print " match on:", q['args'][0]
743 print " query :", q['cmd']
744 raw_input('press [ENTER] to continue')
745 print "============"
746
747 print "testing _generate_queries_from_dto()"
748 print "------------------------------------"
749 dto = cDTO_person()
750 dto.gender = 'm'
751 dto.lastnames = 'Kirk'
752 dto.firstnames = 'James'
753 dto.dob = pyDT.datetime.now(tz=gmDateTime.gmCurrentLocalTimezone)
754 q = searcher._generate_queries_from_dto(dto)[0]
755 print "dto:", dto
756 print " match on:", q['args'][0]
757 print " query:", q['cmd']
758
759 raw_input('press [ENTER] to continue')
760 print "============"
761
762 print "testing _generate_queries_de()"
763 print "------------------------------"
764 qs = searcher._generate_queries_de('Kirk, James')
765 for q in qs:
766 print " match on:", q['args'][0]
767 print " query :", q['cmd']
768 print " args :", q['args']
769 raw_input('press [ENTER] to continue')
770 print "============"
771
772 qs = searcher._generate_queries_de(u'müller')
773 for q in qs:
774 print " match on:", q['args'][0]
775 print " query :", q['cmd']
776 print " args :", q['args']
777 raw_input('press [ENTER] to continue')
778 print "============"
779
780 qs = searcher._generate_queries_de(u'özdemir')
781 for q in qs:
782 print " match on:", q['args'][0]
783 print " query :", q['cmd']
784 print " args :", q['args']
785 raw_input('press [ENTER] to continue')
786 print "============"
787
788 qs = searcher._generate_queries_de(u'Özdemir')
789 for q in qs:
790 print " match on:", q['args'][0]
791 print " query :", q['cmd']
792 print " args :", q['args']
793 raw_input('press [ENTER] to continue')
794 print "============"
795
796 print "testing _generate_dumb_brute_query()"
797 print "------------------------------------"
798 q = searcher._generate_dumb_brute_query('Kirk, James Tiberius')
799 print " match on:", q['args'][0]
800 print " query:", q['cmd']
801 print " args:", q['args']
802
803 raw_input('press [ENTER] to continue')
804
815
816
817
818
819
820
821
822 test_search_by_dto()
823
824
825