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 [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
166 if regex.match(u"^(\s|\t)*\d+(\s|\t)*$", raw, flags = regex.LOCALE | regex.UNICODE):
167 _log.debug("[%s]: a PK or DOB" % raw)
168 tmp = raw.strip()
169 queries.append ({
170 'cmd': u"select *, %s::text as match_type FROM dem.v_basic_person WHERE pk_identity = %s order by lastnames, firstnames, dob",
171 'args': [_('internal patient ID'), tmp]
172 })
173 queries.append ({
174 '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",
175 'args': [_('date of birth'), tmp.replace(',', '.')]
176 })
177 queries.append ({
178 'cmd': u"""
179 select vba.*, %s::text as match_type from dem.lnk_identity2ext_id li2ext_id, dem.v_basic_person vba
180 where vba.pk_identity = li2ext_id.id_identity and lower(li2ext_id.external_id) ~* lower(%s)
181 order by lastnames, firstnames, dob""",
182 'args': [_('external patient ID'), tmp]
183 })
184 return queries
185
186
187 if regex.match(u"^(\d|\s|\t)+$", raw, flags = regex.LOCALE | regex.UNICODE):
188 _log.debug("[%s]: a DOB or PK" % raw)
189 queries.append ({
190 '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",
191 'args': [_('date of birth'), raw.replace(',', '.')]
192 })
193 tmp = raw.replace(u' ', u'')
194 tmp = tmp.replace(u'\t', u'')
195 queries.append ({
196 'cmd': u"SELECT *, %s::text as match_type from dem.v_basic_person WHERE pk_identity LIKE %s%%",
197 'args': [_('internal patient ID'), tmp]
198 })
199 return queries
200
201
202 if regex.match(u"^(\s|\t)*#(\d|\s|\t)+$", raw, flags = regex.LOCALE | regex.UNICODE):
203 _log.debug("[%s]: a PK or external ID" % raw)
204 tmp = raw.replace(u'#', u'')
205 tmp = tmp.strip()
206 tmp = tmp.replace(u' ', u'')
207 tmp = tmp.replace(u'\t', u'')
208
209 queries.append ({
210 'cmd': u"SELECT *, %s::text as match_type from dem.v_basic_person WHERE pk_identity = %s order by lastnames, firstnames, dob",
211 'args': [_('internal patient ID'), tmp]
212 })
213
214 tmp = raw.replace(u'#', u'')
215 tmp = tmp.strip()
216 tmp = tmp.replace(u' ', u'***DUMMY***')
217 tmp = tmp.replace(u'\t', u'***DUMMY***')
218 tmp = tmp.replace(u'***DUMMY***', u'(\s|\t|-|/)*')
219 queries.append ({
220 'cmd': u"""
221 select vba.*, %s::text as match_type from dem.lnk_identity2ext_id li2ext_id, dem.v_basic_person vba
222 where vba.pk_identity = li2ext_id.id_identity and lower(li2ext_id.external_id) ~* lower(%s)
223 order by lastnames, firstnames, dob""",
224 'args': [_('external patient ID'), tmp]
225 })
226 return queries
227
228
229 if regex.match(u"^(\s|\t)*#.+$", raw, flags = regex.LOCALE | regex.UNICODE):
230 _log.debug("[%s]: an external ID" % raw)
231 tmp = raw.replace(u'#', u'')
232 tmp = tmp.strip()
233 tmp = tmp.replace(u' ', u'***DUMMY***')
234 tmp = tmp.replace(u'\t', u'***DUMMY***')
235 tmp = tmp.replace(u'-', u'***DUMMY***')
236 tmp = tmp.replace(u'/', u'***DUMMY***')
237 tmp = tmp.replace(u'***DUMMY***', u'(\s|\t|-|/)*')
238 queries.append ({
239 'cmd': u"""
240 select vba.*, %s::text as match_type from dem.lnk_identity2ext_id li2ext_id, dem.v_basic_person vba
241 where vba.pk_identity = li2ext_id.id_identity and lower(li2ext_id.external_id) ~* lower(%s)
242 order by lastnames, firstnames, dob""",
243 'args': [_('external patient ID'), tmp]
244 })
245 return queries
246
247
248 if regex.match(u"^(\s|\t)*\d+(\s|\t|\.|\-|/)*\d+(\s|\t|\.|\-|/)*\d+(\s|\t|\.)*$", raw, flags = regex.LOCALE | regex.UNICODE):
249 _log.debug("[%s]: a DOB" % raw)
250 tmp = raw.strip()
251 while u'\t\t' in tmp: tmp = tmp.replace(u'\t\t', u' ')
252 while u' ' in tmp: tmp = tmp.replace(u' ', u' ')
253
254
255
256 queries.append ({
257 '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",
258 'args': [_('date of birth'), tmp.replace(',', '.')]
259 })
260 return queries
261
262
263 if regex.match(u"^(\s|\t)*,(\s|\t)*([^0-9])+(\s|\t)*$", raw, flags = regex.LOCALE | regex.UNICODE):
264 _log.debug("[%s]: a firstname" % raw)
265 tmp = self._normalize_soundalikes(raw[1:].strip())
266 cmd = u"""
267 SELECT DISTINCT ON (pk_identity) * from (
268 select *, %s as match_type from ((
269 select vbp.*
270 FROM dem.names, dem.v_basic_person vbp
271 WHERE dem.names.firstnames ~ %s and vbp.pk_identity = dem.names.id_identity
272 ) union all (
273 select vbp.*
274 FROM dem.names, dem.v_basic_person vbp
275 WHERE dem.names.firstnames ~ %s and vbp.pk_identity = dem.names.id_identity
276 )) as super_list order by lastnames, firstnames, dob
277 ) as sorted_list"""
278 queries.append ({
279 'cmd': cmd,
280 'args': [_('first name'), '^' + gmTools.capitalize(tmp, mode=gmTools.CAPS_NAMES), '^' + tmp]
281 })
282 return queries
283
284
285 if regex.match(u"^(\s|\t)*(\*|\$).+$", raw, flags = regex.LOCALE | regex.UNICODE):
286 _log.debug("[%s]: a DOB" % raw)
287 tmp = raw.replace(u'*', u'')
288 tmp = tmp.replace(u'$', u'')
289 queries.append ({
290 '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",
291 'args': [_('date of birth'), tmp.replace(u',', u'.')]
292 })
293 return queries
294
295 return queries
296
297
298
300 """Generate generic queries.
301
302 - not locale dependant
303 - data -> firstnames, lastnames, dob, gender
304 """
305 _log.debug(u'_generate_queries_from_dto("%s")' % dto)
306
307 if not isinstance(dto, gmPerson.cDTO_person):
308 return None
309
310 vals = [_('name, gender, date of birth')]
311 where_snippets = []
312
313 vals.append(dto.firstnames)
314 where_snippets.append(u'firstnames=%s')
315 vals.append(dto.lastnames)
316 where_snippets.append(u'lastnames=%s')
317
318 if dto.dob is not None:
319 vals.append(dto.dob)
320
321 where_snippets.append(u"dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s)")
322
323 if dto.gender is not None:
324 vals.append(dto.gender)
325 where_snippets.append('gender=%s')
326
327
328 if len(where_snippets) == 0:
329 _log.error('invalid search dict structure')
330 _log.debug(data)
331 return None
332
333 cmd = u"""
334 select *, %%s as match_type from dem.v_basic_person
335 where pk_identity in (
336 select id_identity from dem.names where %s
337 ) order by lastnames, firstnames, dob""" % ' and '.join(where_snippets)
338
339 queries = [
340 {'cmd': cmd, 'args': vals}
341 ]
342
343
344
345 return queries
346
347
348
350
351 if search_term is None:
352 return []
353
354
355 queries = self._generate_simple_query(search_term)
356 if len(queries) > 0:
357 return queries
358
359 _log.debug('[%s]: not a search term with a "suggestive" structure' % search_term)
360
361
362 queries = []
363
364
365 normalized = self._normalize_soundalikes(search_term)
366
367
368
369 if regex.match(u"^(\s|\t)*[a-zäöüßéáúóçøA-ZÄÖÜÇØ]+(\s|\t)*$", search_term, flags = regex.LOCALE | regex.UNICODE):
370
371 cmd = u"""
372 SELECT DISTINCT ON (pk_identity) * from (
373 select * from ((
374 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)
375 ) union all (
376 -- first name
377 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)
378 ) union all (
379 -- anywhere in name
380 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)
381 )) as super_list order by lastnames, firstnames, dob
382 ) as sorted_list"""
383 tmp = normalized.strip()
384 args = []
385 args.append(_('last name'))
386 args.append('^' + tmp)
387 args.append(_('first name'))
388 args.append('^' + tmp)
389 args.append(_('any name part'))
390 args.append(tmp)
391
392 queries.append ({
393 'cmd': cmd,
394 'args': args
395 })
396 return queries
397
398
399 parts_list = regex.split(u",|;", normalized)
400
401
402 if len(parts_list) == 1:
403
404 sub_parts_list = regex.split(u"\s*|\t*", normalized)
405
406
407 date_count = 0
408 name_parts = []
409 for part in sub_parts_list:
410
411
412 if regex.search(u"\d", part, flags = regex.LOCALE | regex.UNICODE):
413 date_count = date_count + 1
414 date_part = part
415 else:
416 name_parts.append(part)
417
418
419 if len(sub_parts_list) == 2:
420
421 if date_count == 0:
422
423 queries.append ({
424 '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",
425 'args': [_('name: first-last'), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES)]
426 })
427 queries.append ({
428 '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)",
429 'args': [_('name: first-last'), '^' + name_parts[0], '^' + name_parts[1]]
430 })
431
432 queries.append ({
433 '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",
434 'args': [_('name: last-first'), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES)]
435 })
436 queries.append ({
437 '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)",
438 'args': [_('name: last-first'), '^' + name_parts[1], '^' + name_parts[0]]
439 })
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 || n.lastnames) ~* lower(%s) AND lower(n.firstnames || n.lastnames) ~* lower(%s)",
443 'args': [_('name'), name_parts[0], name_parts[1]]
444 })
445 return queries
446
447 _log.error("don't know how to generate queries for [%s]" % search_term)
448 return queries
449
450
451 if len(sub_parts_list) == 3:
452
453 if date_count == 1:
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 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)",
457 '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'.')]
458 })
459 queries.append ({
460 '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)",
461 'args': [_('names: first-last, date of birth'), '^' + name_parts[0], '^' + name_parts[1], date_part.replace(u',', u'.')]
462 })
463
464 queries.append ({
465 '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)",
466 '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'.')]
467 })
468 queries.append ({
469 '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)",
470 'args': [_('names: last-first, dob'), '^' + name_parts[1], '^' + name_parts[0], date_part.replace(u',', u'.')]
471 })
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 || 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)",
475 'args': [_('name, date of birth'), name_parts[0], name_parts[1], date_part.replace(u',', u'.')]
476 })
477 return queries
478
479 queries.append(self._generate_dumb_brute_query(search_term))
480 return queries
481
482
483 queries.append(self._generate_dumb_brute_query(search_term))
484 return queries
485
486
487 else:
488
489 date_parts = []
490 name_parts = []
491 name_count = 0
492 for part in parts_list:
493
494 if regex.search(u"\d+", part, flags = regex.LOCALE | regex.UNICODE):
495
496 date_parts.append(part)
497 else:
498 tmp = part.strip()
499 tmp = regex.split(u"\s*|\t*", tmp)
500 name_count = name_count + len(tmp)
501 name_parts.append(tmp)
502
503 where_parts = []
504
505
506 if (len(name_parts) == 1) and (name_count == 2):
507
508 where_parts.append ({
509 'conditions': u"firstnames ~ %s and lastnames ~ %s",
510 'args': [_('names: first last'), '^' + gmTools.capitalize(name_parts[0][0], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0][1], mode=gmTools.CAPS_NAMES)]
511 })
512 where_parts.append ({
513 'conditions': u"lower(firstnames) ~* lower(%s) and lower(lastnames) ~* lower(%s)",
514 'args': [_('names: first last'), '^' + name_parts[0][0], '^' + name_parts[0][1]]
515 })
516
517 where_parts.append ({
518 'conditions': u"firstnames ~ %s and lastnames ~ %s",
519 'args': [_('names: last, first'), '^' + gmTools.capitalize(name_parts[0][1], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0][0], mode=gmTools.CAPS_NAMES)]
520 })
521 where_parts.append ({
522 'conditions': u"lower(firstnames) ~* lower(%s) and lower(lastnames) ~* lower(%s)",
523 'args': [_('names: last, first'), '^' + name_parts[0][1], '^' + name_parts[0][0]]
524 })
525
526 where_parts.append ({
527 'conditions': u"lower(firstnames || lastnames) ~* lower(%s) OR lower(firstnames || lastnames) ~* lower(%s)",
528 'args': [_('name'), name_parts[0][0], name_parts[0][1]]
529 })
530
531
532 elif len(name_parts) == 2:
533
534 where_parts.append ({
535 'conditions': u"firstnames ~ %s AND lastnames ~ %s",
536 'args': [_('name: last, first'), '^' + ' '.join(map(gmTools.capitalize, name_parts[1])), '^' + ' '.join(map(gmTools.capitalize, name_parts[0]))]
537 })
538 where_parts.append ({
539 'conditions': u"lower(firstnames) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
540 'args': [_('name: last, first'), '^' + ' '.join(name_parts[1]), '^' + ' '.join(name_parts[0])]
541 })
542
543 where_parts.append ({
544 'conditions': u"firstnames ~ %s AND lastnames ~ %s",
545 'args': [_('name: last, first'), '^' + ' '.join(map(gmTools.capitalize, name_parts[0])), '^' + ' '.join(map(gmTools.capitalize, name_parts[1]))]
546 })
547 where_parts.append ({
548 'conditions': u"lower(firstnames) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
549 'args': [_('name: last, first'), '^' + ' '.join(name_parts[0]), '^' + ' '.join(name_parts[1])]
550 })
551
552 where_parts.append ({
553 'conditions': u"lower(firstnames || lastnames) ~* lower(%s) AND lower(firstnames || lastnames) ~* lower(%s)",
554 'args': [_('name'), ' '.join(name_parts[0]), ' '.join(name_parts[1])]
555 })
556
557
558 else:
559
560 if len(name_parts) == 1:
561 for part in name_parts[0]:
562 where_parts.append ({
563 'conditions': u"lower(firstnames || lastnames) ~* lower(%s)",
564 'args': [_('name'), part]
565 })
566 else:
567 tmp = []
568 for part in name_parts:
569 tmp.append(' '.join(part))
570 for part in tmp:
571 where_parts.append ({
572 'conditions': u"lower(firstnames || lastnames) ~* lower(%s)",
573 'args': [_('name'), part]
574 })
575
576
577
578 if len(date_parts) == 1:
579 if len(where_parts) == 0:
580 where_parts.append ({
581 'conditions': u"dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
582 'args': [_('date of birth'), date_parts[0].replace(u',', u'.')]
583 })
584 if len(where_parts) > 0:
585 where_parts[0]['conditions'] += u" AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)"
586 where_parts[0]['args'].append(date_parts[0].replace(u',', u'.'))
587 where_parts[0]['args'][0] += u', ' + _('date of birth')
588 if len(where_parts) > 1:
589 where_parts[1]['conditions'] += u" AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)"
590 where_parts[1]['args'].append(date_parts[0].replace(u',', u'.'))
591 where_parts[1]['args'][0] += u', ' + _('date of birth')
592 elif 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 witih time zone) AND dem.date_trunc_utc('day'::text, dem.identity.deceased) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
596 'args': [_('date of birth/death'), date_parts[0].replace(u',', u'.'), date_parts[1].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) AND dem.date_trunc_utc('day'::text, dem.identity.deceased) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
600 where_parts[0]['args'].append(date_parts[0].replace(u',', u'.'), date_parts[1].replace(u',', u'.'))
601 where_parts[0]['args'][0] += u', ' + _('date of birth/death')
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) AND dem.date_trunc_utc('day'::text, dem.identity.deceased) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
604 where_parts[1]['args'].append(date_parts[0].replace(u',', u'.'), date_parts[1].replace(u',', u'.'))
605 where_parts[1]['args'][0] += u', ' + _('date of birth/death')
606
607
608 for where_part in where_parts:
609 queries.append ({
610 'cmd': u"select *, %%s::text as match_type from dem.v_basic_person where %s" % where_part['conditions'],
611 'args': where_part['args']
612 })
613 return queries
614
615 return []
616
618
619 _log.debug('_generate_dumb_brute_query("%s")' % search_term)
620
621 where_clause = ''
622 args = []
623
624 for arg in search_term.strip().split():
625 where_clause += u' and lower(vbp.title || vbp.firstnames || vbp.lastnames) ~* lower(%s)'
626 args.append(arg)
627
628 query = u"""
629 select distinct on (pk_identity) * from (
630 select
631 vbp.*, '%s'::text as match_type
632 from
633 dem.v_basic_person vbp,
634 dem.names n
635 where
636 vbp.pk_identity = n.id_identity
637 %s
638 order by
639 lastnames,
640 firstnames,
641 dob
642 ) as ordered_list""" % (_('full name'), where_clause)
643
644 return ({'cmd': query, 'args': args})
645
647 """Text mode UI function to ask for patient."""
648
649 person_searcher = cPatientSearcher_SQL()
650
651 while True:
652 search_fragment = gmTools.prompted_input(prompt = "\nEnter person search term or leave blank to exit")
653
654 if search_fragment in ['exit', 'quit', 'bye', None]:
655 print "user cancelled patient search"
656 return None
657
658 pats = person_searcher.get_patients(search_term = search_fragment)
659
660 if (pats is None) or (len(pats) == 0):
661 print "No patient matches the query term."
662 print ""
663 continue
664
665 if len(pats) > 1:
666 print "Several patients match the query term:"
667 print ""
668 for pat in pats:
669 print pat
670 print ""
671 continue
672
673 return pats[0]
674
675 return None
676
677
678
679 if __name__ == '__main__':
680
681 if len(sys.argv) == 1:
682 sys.exit()
683
684 if sys.argv[1] != 'test':
685 sys.exit()
686
687 import datetime
688
689 gmI18N.activate_locale()
690 gmI18N.install_domain()
691 gmDateTime.init()
692
693
706
708 searcher = cPatientSearcher_SQL()
709
710 print "testing _normalize_soundalikes()"
711 print "--------------------------------"
712
713 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']
714 for name in data:
715 print '%s: %s' % (name, searcher._normalize_soundalikes(name))
716
717 raw_input('press [ENTER] to continue')
718 print "============"
719
720 print "testing _generate_simple_query()"
721 print "----------------------------"
722 data = ['51234', '1 134 153', '#13 41 34', '#3-AFY322.4', '22-04-1906', '1235/32/3525', ' , johnny']
723 for fragment in data:
724 print "fragment:", fragment
725 qs = searcher._generate_simple_query(fragment)
726 for q in qs:
727 print " match on:", q['args'][0]
728 print " query :", q['cmd']
729 raw_input('press [ENTER] to continue')
730 print "============"
731
732 print "testing _generate_queries_from_dto()"
733 print "------------------------------------"
734 dto = cDTO_person()
735 dto.gender = 'm'
736 dto.lastnames = 'Kirk'
737 dto.firstnames = 'James'
738 dto.dob = pyDT.datetime.now(tz=gmDateTime.gmCurrentLocalTimezone)
739 q = searcher._generate_queries_from_dto(dto)[0]
740 print "dto:", dto
741 print " match on:", q['args'][0]
742 print " query:", q['cmd']
743
744 raw_input('press [ENTER] to continue')
745 print "============"
746
747 print "testing _generate_queries_de()"
748 print "------------------------------"
749 qs = searcher._generate_queries_de('Kirk, James')
750 for q in qs:
751 print " match on:", q['args'][0]
752 print " query :", q['cmd']
753 print " args :", q['args']
754 raw_input('press [ENTER] to continue')
755 print "============"
756
757 qs = searcher._generate_queries_de(u'müller')
758 for q in qs:
759 print " match on:", q['args'][0]
760 print " query :", q['cmd']
761 print " args :", q['args']
762 raw_input('press [ENTER] to continue')
763 print "============"
764
765 qs = searcher._generate_queries_de(u'özdemir')
766 for q in qs:
767 print " match on:", q['args'][0]
768 print " query :", q['cmd']
769 print " args :", q['args']
770 raw_input('press [ENTER] to continue')
771 print "============"
772
773 qs = searcher._generate_queries_de(u'Özdemir')
774 for q in qs:
775 print " match on:", q['args'][0]
776 print " query :", q['cmd']
777 print " args :", q['args']
778 raw_input('press [ENTER] to continue')
779 print "============"
780
781 print "testing _generate_dumb_brute_query()"
782 print "------------------------------------"
783 q = searcher._generate_dumb_brute_query('Kirk, James Tiberius')
784 print " match on:", q['args'][0]
785 print " query:", q['cmd']
786 print " args:", q['args']
787
788 raw_input('press [ENTER] to continue')
789
800
801
802
803
804
805
806
807 test_search_by_dto()
808
809
810