Example #1
0
def read_persons_from_pracsoft_file(filename=None, encoding='ascii'):

    pats_file = io.open(filename, mode='rt', encoding=encoding)
    dtos = []

    for line in pats_file:
        if len(line) < PATIENTS_IN_line_len:
            continue  # perhaps raise Exception ?

        dto = gmPerson.cDTO_person()
        dto.external_ids = [{
            'PracSoft No.': line[0:9].strip(),
            'issuer': 'AU PracSoft application'
        }, {
            'CRN': line[166:180].replace(' ', ''),
            'issuer': 'Centrelink (AU)'
        }, {
            'DVA': line[180:194].replace(' ', ''),
            'issuer': "Department of Veteran's Affairs (AU)"
        }, {
            'AU-Medicare': line[153:166].replace(' ', ''),
            'issuer': 'HIC (AU)'
        }]

        dto.title = gmTools.capitalize(line[9:14].strip(), gmTools.CAPS_FIRST)
        dto.firstnames = gmTools.capitalize(line[44:74].strip(),
                                            gmTools.CAPS_NAMES)
        dto.lastnames = gmTools.capitalize(line[14:44].strip(),
                                           gmTools.CAPS_NAMES)

        dto.gender = line[223].lower()
        dob = time.strptime(line[143:153].strip(), PATIENTS_IN_dob_format)
        dto.dob = pyDT.datetime(dob.tm_year,
                                dob.tm_mon,
                                dob.tm_mday,
                                tzinfo=gmDateTime.gmCurrentLocalTimezone)

        # this is the home address
        dto.street = gmTools.capitalize(line[74:114].strip(),
                                        gmTools.CAPS_FIRST)
        dto.zip = line[139:143].strip()
        dto.urb = line[114:139].strip()

        dto.comms = [  # types must correspond to GNUmed database comm type
            {
                'homephone': line[194:208].replace(' ', '')
            }, {
                'workphone': line[208:222].replace(' ', '')
            }
        ]
        dto.pracsoft_billing_flag = line[222]  # P=pensioner R=repatriation

        dtos.append(dto)

    return dtos
Example #2
0
	def __queries_for_firstname_with_comma(self, raw):
		"""Generate search queries for [ , <alpha> ] search terms."""
		if regex.match(",\s*\w+$", raw.strip()) is None:
			return []
		_log.debug("[%s]: a firstname" % raw)
		tmp = self._normalize_soundalikes(raw.strip(' ,'))
		cmd = """
			SELECT DISTINCT ON (pk_identity) * FROM (
				SELECT *, %(match)s AS match_type FROM ((
					SELECT d_vap.*
					FROM dem.names, dem.v_active_persons d_vap
					WHERE dem.names.firstnames ~ %(first)s and d_vap.pk_identity = dem.names.id_identity
				) union all (
					SELECT d_vap.*
					FROM dem.names, dem.v_active_persons d_vap
					WHERE dem.names.firstnames ~ %(first_w_caps)s and d_vap.pk_identity = dem.names.id_identity
				) union all (
					SELECT d_vap.*
					FROM dem.names, dem.v_active_persons d_vap
					WHERE lower(dem.names.firstnames) ~ lower(%(first)s) and d_vap.pk_identity = dem.names.id_identity
				)) AS super_list ORDER BY lastnames, firstnames, dob
			) AS sorted_list"""
		args = {
			'match': _('first name'),
			'first': '^' + tmp,
			'first_w_caps': '^' + gmTools.capitalize(tmp, mode = gmTools.CAPS_NAMES)
		}
		return [{'cmd': cmd, 'args': args}]
Example #3
0
	def __queries_for_LASTNAME(self, raw):
		"""Generate search queries for [ <ALPHA> ] search terms."""
		if regex.match("\w+$", raw) is None:
			return []
		if raw != raw.upper():
			# not all UPPERCASE
			return []
		_log.debug("[%s]: a lastname" % raw)
		tmp = self._normalize_soundalikes(raw)
		cmd = """
			SELECT DISTINCT ON (pk_identity) * FROM (
				SELECT *, %(match)s AS match_type FROM ((
					SELECT d_vap.*
					FROM dem.names, dem.v_active_persons d_vap
					WHERE dem.names.lastnames ~ %(last_w_caps)s and d_vap.pk_identity = dem.names.id_identity
				) union all (
					SELECT d_vap.*
					FROM dem.names, dem.v_active_persons d_vap
					WHERE lower(dem.names.lastnames) ~ lower(%(last)s) and d_vap.pk_identity = dem.names.id_identity
				)) AS super_list ORDER BY lastnames, firstnames, dob
			) AS sorted_list"""
		args = {
			'match': _('last name'),
			'last': '^' + tmp,
			'last_w_caps': '^' + gmTools.capitalize(tmp.lower(), mode=gmTools.CAPS_NAMES)
		}
		return [{'cmd': cmd, 'args': args}]
Example #4
0
	def __queries_for_LASTNAME(self, raw):
		"""Generate search queries for [ <ALPHA> ] search terms."""
		if regex.match("\w+$", raw) is None:
			return []
		if raw != raw.upper():
			# not all UPPERCASE
			return []
		_log.debug("[%s]: a lastname" % raw)
		tmp = self._normalize_soundalikes(raw)
		cmd = """
			SELECT DISTINCT ON (pk_identity) * FROM (
				SELECT *, %(match)s AS match_type FROM ((
					SELECT d_vap.*
					FROM dem.names, dem.v_active_persons d_vap
					WHERE dem.names.lastnames ~ %(last_w_caps)s and d_vap.pk_identity = dem.names.id_identity
				) union all (
					SELECT d_vap.*
					FROM dem.names, dem.v_active_persons d_vap
					WHERE lower(dem.names.lastnames) ~ lower(%(last)s) and d_vap.pk_identity = dem.names.id_identity
				)) AS super_list ORDER BY lastnames, firstnames, dob
			) AS sorted_list"""
		args = {
			'match': _('last name'),
			'last': '^' + tmp,
			'last_w_caps': '^' + gmTools.capitalize(tmp.lower(), mode=gmTools.CAPS_NAMES)
		}
		return [{'cmd': cmd, 'args': args}]
Example #5
0
	def __queries_for_firstname_with_comma(self, raw):
		"""Generate search queries for [ , <alpha> ] search terms."""
		if regex.match(",\s*\w+$", raw.strip()) is None:
			return []
		_log.debug("[%s]: a firstname" % raw)
		tmp = self._normalize_soundalikes(raw.strip(' ,'))
		cmd = """
			SELECT DISTINCT ON (pk_identity) * FROM (
				SELECT *, %(match)s AS match_type FROM ((
					SELECT d_vap.*
					FROM dem.names, dem.v_active_persons d_vap
					WHERE dem.names.firstnames ~ %(first)s and d_vap.pk_identity = dem.names.id_identity
				) union all (
					SELECT d_vap.*
					FROM dem.names, dem.v_active_persons d_vap
					WHERE dem.names.firstnames ~ %(first_w_caps)s and d_vap.pk_identity = dem.names.id_identity
				) union all (
					SELECT d_vap.*
					FROM dem.names, dem.v_active_persons d_vap
					WHERE lower(dem.names.firstnames) ~ lower(%(first)s) and d_vap.pk_identity = dem.names.id_identity
				)) AS super_list ORDER BY lastnames, firstnames, dob
			) AS sorted_list"""
		args = {
			'match': _('first name'),
			'first': '^' + tmp,
			'first_w_caps': '^' + gmTools.capitalize(tmp, mode = gmTools.CAPS_NAMES)
		}
		return [{'cmd': cmd, 'args': args}]
Example #6
0
def read_persons_from_pracsoft_file(filename=None, encoding='ascii'):

	pats_file = io.open(filename, mode = 'rt', encoding = encoding)
	dtos = []

	for line in pats_file:
		if len(line) < PATIENTS_IN_line_len:
			continue			# perhaps raise Exception ?

		dto = gmPerson.cDTO_person()
		dto.external_ids = [
			{'PracSoft No.': line[0:9].strip(), 'issuer': 'AU PracSoft application'},
			{'CRN': line[166:180].replace(' ', ''), 'issuer': 'Centrelink (AU)'},
			{'DVA': line[180:194].replace(' ', ''), 'issuer': "Department of Veteran's Affairs (AU)"},
			{'AU-Medicare': line[153:166].replace(' ', ''), 'issuer': 'HIC (AU)'}
		]

		dto.title = gmTools.capitalize(line[9:14].strip(), gmTools.CAPS_FIRST)
		dto.firstnames = gmTools.capitalize(line[44:74].strip(), gmTools.CAPS_NAMES)
		dto.lastnames = gmTools.capitalize(line[14:44].strip(), gmTools.CAPS_NAMES)

		dto.gender = line[223].lower()
		dob = time.strptime(line[143:153].strip(), PATIENTS_IN_dob_format)
		dto.dob = pyDT.datetime(dob.tm_year, dob.tm_mon, dob.tm_mday, tzinfo = gmDateTime.gmCurrentLocalTimezone)

		# this is the home address
		dto.street = gmTools.capitalize(line[74:114].strip(), gmTools.CAPS_FIRST)
		dto.zip = line[139:143].strip()
		dto.urb = line[114:139].strip()

		dto.comms = [					# types must correspond to GNUmed database comm type
			{'homephone': line[194:208].replace(' ', '')},
			{'workphone': line[208:222].replace(' ', '')}
		]
		dto.pracsoft_billing_flag = line[222] # P=pensioner R=repatriation

		dtos.append(dto)

	return dtos
Example #7
0
	def _on_text_update (self, event):
		"""Internal handler for wx.EVT_TEXT.

		Called when text was changed by user or by SetValue().
		"""
		if self.suppress_text_update_smarts:
			self.suppress_text_update_smarts = False
			return

		self._adjust_data_after_text_update()
		self._current_match_candidates = []

		val = self.GetValue().strip()
		ins_point = self.GetInsertionPoint()

		# if empty string then hide list dropdown window
		# we also don't need a timer event then
		if val == u'':
			self._hide_picklist()
			self.__timer.Stop()
		else:
			new_val = gmTools.capitalize(text = val, mode = self.capitalisation_mode)
			if new_val != val:
				self.suppress_text_update_smarts = True
				super(cPhraseWheelBase, self).SetValue(new_val)
				if ins_point > len(new_val):
					self.SetInsertionPointEnd()
				else:
					self.SetInsertionPoint(ins_point)
					# FIXME: SetSelection() ?

			# start timer for delayed match retrieval
			self.__timer.Start(oneShot = True, milliseconds = self.picklist_delay)

		# notify interested parties
		for callback in self._on_modified_callbacks:
			callback()

		return
Example #8
0
	def _generate_queries_de(self, search_term=None):

		if search_term is None:
			return []

		# check to see if we get away with a simple query ...
		queries = self._generate_simple_query(search_term)
		if len(queries) > 0:
			_log.debug('[%s]: search term with a simple, unambigous structure' % search_term)
			return queries

		# no we don't
		_log.debug('[%s]: not a search term with a simple, unambigous structure' % search_term)

		search_term = search_term.strip().strip(',').strip(';').strip()
		normalized = self._normalize_soundalikes(search_term)

		queries = []

		# "<CHARS>" - single name part
		# yes, I know, this is culture specific (did you read the docs ?)
		if regex.match("^(\s|\t)*[a-zäöüßéáúóçøA-ZÄÖÜÇØ]+(\s|\t)*$", search_term):
			_log.debug("[%s]: a single name part", search_term)
			# there's no intermediate whitespace due to the regex
			cmd = """
				SELECT DISTINCT ON (pk_identity) * FROM (
					SELECT * FROM ((
						-- lastname
						SELECT d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n
						WHERE d_vap.pk_identity = n.id_identity and lower(n.lastnames) ~* lower(%s)
					) union all (
						-- firstname
						SELECT d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n
						WHERE d_vap.pk_identity = n.id_identity and lower(n.firstnames) ~* lower(%s)
					) union all (
						-- nickname
						SELECT d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n
						WHERE d_vap.pk_identity = n.id_identity and lower(n.preferred) ~* lower(%s)
					) union all (
						-- anywhere in name
						SELECT
							d_vap.*,
							%s::text AS match_type
						FROM
							dem.v_active_persons d_vap,
							dem.names n
						WHERE
							d_vap.pk_identity = n.id_identity
								AND
							lower(n.firstnames || ' ' || n.lastnames || ' ' || coalesce(n.preferred, '')) ~* lower(%s)
					)) AS super_list ORDER BY lastnames, firstnames, dob
				) AS sorted_list
			"""
			tmp = normalized.strip()
			args = []
			args.append(_('lastname'))
			args.append('^' + tmp)
			args.append(_('firstname'))
			args.append('^' + tmp)
			args.append(_('nickname'))
			args.append('^' + tmp)
			args.append(_('any name part'))
			args.append(tmp)

			queries.append ({
				'cmd': cmd,
				'args': args
			})
			return queries

		# try to split on (major) part separators
		major_parts = regex.split(',|;', normalized)

		# ignore empty parts
		major_parts = [ p.strip() for p in major_parts if p.strip() != '' ]

		# only one "major" part ? (i.e. no ",;" ?)
		if len(major_parts) == 1:
			_log.debug('[%s]: only one non-empty part after splitting by , or ; ("major" part)', normalized)
			queries = self.__generate_queries_from_single_major_part(part = normalized)
			if len(queries) > 0:
				return queries
			return self._generate_dumb_brute_query(search_term)

		# more than one major part (separated by ';,')
		# this else is not needed
		else:
			_log.debug('[%s]: more than one non-empty part after splitting by , or ; ("major" parts)', normalized)
			# parse into name and date parts
			date_parts = []
			name_parts = []
			name_count = 0
			for part in major_parts:
				if part.strip() == '':
					continue
				# any digits ?
				if regex.search("\d+", part):
					# FIXME: parse out whitespace *not* adjacent to a *word*
					date_parts.append(part)
				else:
					tmp = part.strip()
					tmp = regex.split("\s+|\t+", tmp)
					name_count = name_count + len(tmp)
					name_parts.append(tmp)

			_log.debug('found %s character (name) parts and %s number (date ?) parts', len(name_parts), len(date_parts))

			where_parts = []
			# first, handle name parts
			# special case: "<date(s)>, <name> <name>, <date(s)>"
			if (len(name_parts) == 1) and (name_count == 2):
				# usually "first last"
				where_parts.append ({
					'conditions': "firstnames ~ %s and lastnames ~ %s",
					'args': [_('names: first last'), '^' + gmTools.capitalize(name_parts[0][0], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0][1], mode=gmTools.CAPS_NAMES)]
				})
				where_parts.append ({
					'conditions': "lower(firstnames) ~* lower(%s) and lower(lastnames) ~* lower(%s)",
					'args': [_('names: first last'), '^' + name_parts[0][0], '^' + name_parts[0][1]]
				})
				# but sometimes "last first""
				where_parts.append ({
					'conditions': "firstnames ~ %s and lastnames ~ %s",
					'args': [_('names: last, first'), '^' + gmTools.capitalize(name_parts[0][1], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0][0], mode=gmTools.CAPS_NAMES)]
				})
				where_parts.append ({
					'conditions': "lower(firstnames) ~* lower(%s) and lower(lastnames) ~* lower(%s)",
					'args': [_('names: last, first'), '^' + name_parts[0][1], '^' + name_parts[0][0]]
				})
				# or even substrings anywhere in name
				where_parts.append ({
					'conditions': "lower(firstnames || ' ' || lastnames) ~* lower(%s) OR lower(firstnames || ' ' || lastnames) ~* lower(%s)",
					'args': [_('name'), name_parts[0][0], name_parts[0][1]]
				})

			# special case: "<date(s)>, <name(s)>, <name(s)>, <date(s)>"
			elif len(name_parts) == 2:
				# usually "last, first"
				where_parts.append ({
					'conditions': "firstnames ~ %s AND lastnames ~ %s",
					'args': [_('name: last, first'), '^' + ' '.join(map(gmTools.capitalize, name_parts[1])), '^' + ' '.join(map(gmTools.capitalize, name_parts[0]))]
				})
				where_parts.append ({
					'conditions': "lower(firstnames) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
					'args': [_('name: last, first'), '^' + ' '.join(name_parts[1]), '^' + ' '.join(name_parts[0])]
				})
				# but sometimes "first, last"
				where_parts.append ({
					'conditions': "firstnames ~ %s AND lastnames ~ %s",
					'args': [_('name: last, first'), '^' + ' '.join(map(gmTools.capitalize, name_parts[0])), '^' + ' '.join(map(gmTools.capitalize, name_parts[1]))]
				})
				where_parts.append ({
					'conditions': "lower(firstnames) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
					'args': [_('name: last, first'), '^' + ' '.join(name_parts[0]), '^' + ' '.join(name_parts[1])]
				})
				# and sometimes "last, nick"
				where_parts.append ({
					'conditions': "preferred ~ %s AND lastnames ~ %s",
					'args': [_('name: last, first'), '^' + ' '.join(map(gmTools.capitalize, name_parts[1])), '^' + ' '.join(map(gmTools.capitalize, name_parts[0]))]
				})
				where_parts.append ({
					'conditions': "lower(preferred) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
					'args': [_('name: last, first'), '^' + ' '.join(name_parts[1]), '^' + ' '.join(name_parts[0])]
				})

				# or even substrings anywhere in name
				where_parts.append ({
					'conditions': "lower(firstnames || ' ' || lastnames) ~* lower(%s) AND lower(firstnames || ' ' || lastnames) ~* lower(%s)",
					'args': [_('name'), ' '.join(name_parts[0]), ' '.join(name_parts[1])]
				})

			# big trouble - arbitrary number of names
			else:
				# FIXME: deep magic, not sure of rationale ...
				if len(name_parts) == 1:
					for part in name_parts[0]:
						where_parts.append ({
							'conditions': "lower(firstnames || ' ' || lastnames) ~* lower(%s)",
							'args': [_('name'), part]
						})
				else:
					tmp = []
					for part in name_parts:
						tmp.append(' '.join(part))
					for part in tmp:
						where_parts.append ({
							'conditions': "lower(firstnames || ' ' || lastnames) ~* lower(%s)",
							'args': [_('name'), part]
						})

			# secondly handle date parts
			# FIXME: this needs a considerable smart-up !
			if len(date_parts) == 1:
				if len(where_parts) == 0:
					where_parts.append ({
						'conditions': "dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
						'args': [_('date of birth'), date_parts[0].replace(',', '.')]
					})
				if len(where_parts) > 0:
					where_parts[0]['conditions'] += " AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)"
					where_parts[0]['args'].append(date_parts[0].replace(',', '.'))
					where_parts[0]['args'][0] += ', ' + _('date of birth')
				if len(where_parts) > 1:
					where_parts[1]['conditions'] += " AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)"
					where_parts[1]['args'].append(date_parts[0].replace(',', '.'))
					where_parts[1]['args'][0] += ', ' + _('date of birth')
			elif len(date_parts) > 1:
				if len(where_parts) == 0:
					where_parts.append ({
						'conditions': "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)",
						'args': [_('date of birth/death'), date_parts[0].replace(',', '.'), date_parts[1].replace(',', '.')]
					})
				if len(where_parts) > 0:
					where_parts[0]['conditions'] += " 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)",
					where_parts[0]['args'].append(date_parts[0].replace(',', '.'), date_parts[1].replace(',', '.'))
					where_parts[0]['args'][0] += ', ' + _('date of birth/death')
				if len(where_parts) > 1:
					where_parts[1]['conditions'] += " 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)",
					where_parts[1]['args'].append(date_parts[0].replace(',', '.'), date_parts[1].replace(',', '.'))
					where_parts[1]['args'][0] += ', ' + _('date of birth/death')

			# and finally generate the queries ...
			for where_part in where_parts:
				queries.append ({
					'cmd': "SELECT *, %%s::text AS match_type FROM dem.v_active_persons WHERE %s" % where_part['conditions'],
					'args': where_part['args']
				})
			return queries

		return []
Example #9
0
	def __generate_queries_from_single_major_part(self, part=None):

		# split on whitespace
		parts_list = regex.split("\s+|\t+", part)
		# ignore empty parts
		parts_list = [ p.strip() for p in parts_list if p.strip() != '' ]

		# parse into name/date parts
		date_count = 0
		name_parts = []
		for part in parts_list:
			# any digit signifies a date,		 FIXME: what about "<40" ?
			if regex.search("\d", part):
				date_count = date_count + 1
				date_part = part
			else:
				name_parts.append(part)

		# exactly 1 word ?
		if len(parts_list) == 1:
			return []

		# exactly 2 words ?
		if len(parts_list) == 2:
			if date_count > 0:
				# FIXME: either "name date" or "date date"
				_log.error("don't know how to generate queries for [%s]" % search_term)
				return []
			# no date = "first last" or "last first"
			queries = []
			# assumption: first last
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.pk_identity = n.id_identity and n.firstnames ~ %s AND n.lastnames ~ %s",
				'args': [_('name: first-last'), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES)]
			})
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.pk_identity = n.id_identity and lower(n.firstnames) ~* lower(%s) AND lower(n.lastnames) ~* lower(%s)",
				'args': [_('name: first-last'), '^' + name_parts[0], '^' + name_parts[1]]
			})
			# assumption: last first
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.pk_identity = n.id_identity and n.firstnames ~ %s AND n.lastnames ~ %s",
				'args': [_('name: last-first'), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES)]
			})
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.pk_identity = n.id_identity and lower(n.firstnames) ~* lower(%s) AND lower(n.lastnames) ~* lower(%s)",
				'args': [_('name: last-first'), '^' + name_parts[1], '^' + name_parts[0]]
			})
			# assumption: last nick
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.pk_identity = n.id_identity and n.preferred ~ %s AND n.lastnames ~ %s",
				'args': [_('name: last-nick'), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES)]
			})
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.pk_identity = n.id_identity and lower(n.preferred) ~* lower(%s) AND lower(n.lastnames) ~* lower(%s)",
				'args': [_('name: last-nick'), '^' + name_parts[1], '^' + name_parts[0]]
			})
			# name parts anywhere inside name - third order query ...
			queries.append ({
				'cmd': """SELECT DISTINCT ON (id_identity)
							d_vap.*,
							%s::text AS match_type
						FROM
							dem.v_active_persons d_vap,
							dem.names n
						WHERE
							d_vap.pk_identity = n.id_identity
								AND
							-- name_parts[0]
							lower(n.firstnames || ' ' || n.lastnames) ~* lower(%s)
								AND
							-- name_parts[1]
							lower(n.firstnames || ' ' || n.lastnames) ~* lower(%s)""",
				'args': [_('name'), name_parts[0], name_parts[1]]
			})
			return queries

		# exactly 3 words ?
		if len(parts_list) == 3:
			if date_count != 1:
				# FIXME: "name name name" or "name date date"
				return []

			# special case: 3 words, exactly 1 of them a date, no ",;"
			# assumption: first, last, dob - first order
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.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)",
				'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(',', '.')]
			})
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.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)",
				'args': [_('names: first-last, date of birth'), '^' + name_parts[0], '^' + name_parts[1], date_part.replace(',', '.')]
			})
			# assumption: last, first, dob - second order query
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.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)",
				'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(',', '.')]
			})
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.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)",
				'args': [_('names: last-first, dob'), '^' + name_parts[1], '^' + name_parts[0], date_part.replace(',', '.')]
			})
			# name parts anywhere in name - third order query ...
			queries.append ({
				'cmd': """SELECT DISTINCT ON (id_identity)
							d_vap.*,
							%s::text AS match_type
						FROM
							dem.v_active_persons d_vap,
							dem.names n
						WHERE
							d_vap.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)
				""",
				'args': [_('name, date of birth'), name_parts[0], name_parts[1], date_part.replace(',', '.')]
			})
			return queries

		return []
Example #10
0
	def __generate_queries_from_single_major_part(self, part=None):

		# split on whitespace
		parts_list = regex.split("\s+|\t+", part)
		# ignore empty parts
		parts_list = [ p.strip() for p in parts_list if p.strip() != '' ]

		# parse into name/date parts
		date_count = 0
		name_parts = []
		for part in parts_list:
			# any digit signifies a date,		 FIXME: what about "<40" ?
			if regex.search("\d", part):
				date_count = date_count + 1
				date_part = part
			else:
				name_parts.append(part)

		# exactly 1 word ?
		if len(parts_list) == 1:
			return []

		# exactly 2 words ?
		if len(parts_list) == 2:
			if date_count > 0:
				# FIXME: either "name date" or "date date"
				_log.error("don't know how to generate queries for [%s]" % search_term)
				return []
			# no date = "first last" or "last first"
			queries = []
			# assumption: first last
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.pk_identity = n.id_identity and n.firstnames ~ %s AND n.lastnames ~ %s",
				'args': [_('name: first-last'), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES)]
			})
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.pk_identity = n.id_identity and lower(n.firstnames) ~* lower(%s) AND lower(n.lastnames) ~* lower(%s)",
				'args': [_('name: first-last'), '^' + name_parts[0], '^' + name_parts[1]]
			})
			# assumption: last first
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.pk_identity = n.id_identity and n.firstnames ~ %s AND n.lastnames ~ %s",
				'args': [_('name: last-first'), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES)]
			})
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.pk_identity = n.id_identity and lower(n.firstnames) ~* lower(%s) AND lower(n.lastnames) ~* lower(%s)",
				'args': [_('name: last-first'), '^' + name_parts[1], '^' + name_parts[0]]
			})
			# assumption: last nick
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.pk_identity = n.id_identity and n.preferred ~ %s AND n.lastnames ~ %s",
				'args': [_('name: last-nick'), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES)]
			})
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.pk_identity = n.id_identity and lower(n.preferred) ~* lower(%s) AND lower(n.lastnames) ~* lower(%s)",
				'args': [_('name: last-nick'), '^' + name_parts[1], '^' + name_parts[0]]
			})
			# name parts anywhere inside name - third order query ...
			queries.append ({
				'cmd': """SELECT DISTINCT ON (id_identity)
							d_vap.*,
							%s::text AS match_type
						FROM
							dem.v_active_persons d_vap,
							dem.names n
						WHERE
							d_vap.pk_identity = n.id_identity
								AND
							-- name_parts[0]
							lower(n.firstnames || ' ' || n.lastnames) ~* lower(%s)
								AND
							-- name_parts[1]
							lower(n.firstnames || ' ' || n.lastnames) ~* lower(%s)""",
				'args': [_('name'), name_parts[0], name_parts[1]]
			})
			return queries

		# exactly 3 words ?
		if len(parts_list) == 3:
			if date_count != 1:
				# FIXME: "name name name" or "name date date"
				return []

			# special case: 3 words, exactly 1 of them a date, no ",;"
			# assumption: first, last, dob - first order
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.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)",
				'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(',', '.')]
			})
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.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)",
				'args': [_('names: first-last, date of birth'), '^' + name_parts[0], '^' + name_parts[1], date_part.replace(',', '.')]
			})
			# assumption: last, first, dob - second order query
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.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)",
				'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(',', '.')]
			})
			queries.append ({
				'cmd': "SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.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)",
				'args': [_('names: last-first, dob'), '^' + name_parts[1], '^' + name_parts[0], date_part.replace(',', '.')]
			})
			# name parts anywhere in name - third order query ...
			queries.append ({
				'cmd': """SELECT DISTINCT ON (id_identity)
							d_vap.*,
							%s::text AS match_type
						FROM
							dem.v_active_persons d_vap,
							dem.names n
						WHERE
							d_vap.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)
				""",
				'args': [_('name, date of birth'), name_parts[0], name_parts[1], date_part.replace(',', '.')]
			})
			return queries

		return []
Example #11
0
	def _generate_queries_de(self, search_term=None):

		if search_term is None:
			return []

		# check to see if we get away with a simple query ...
		queries = self._generate_simple_query(search_term)
		if len(queries) > 0:
			_log.debug('[%s]: search term with a simple, unambigous structure' % search_term)
			return queries

		# no we don't
		_log.debug('[%s]: not a search term with a simple, unambigous structure' % search_term)

		search_term = search_term.strip().strip(',').strip(';').strip()
		normalized = self._normalize_soundalikes(search_term)

		queries = []

		# "<CHARS>" - single name part
		# yes, I know, this is culture specific (did you read the docs ?)
		if regex.match("^(\s|\t)*[a-zäöüßéáúóçøA-ZÄÖÜÇØ]+(\s|\t)*$", search_term):
			_log.debug("[%s]: a single name part", search_term)
			# there's no intermediate whitespace due to the regex
			cmd = """
				SELECT DISTINCT ON (pk_identity) * FROM (
					SELECT * FROM ((
						-- lastname
						SELECT d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n
						WHERE d_vap.pk_identity = n.id_identity and lower(n.lastnames) ~* lower(%s)
					) union all (
						-- firstname
						SELECT d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n
						WHERE d_vap.pk_identity = n.id_identity and lower(n.firstnames) ~* lower(%s)
					) union all (
						-- nickname
						SELECT d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n
						WHERE d_vap.pk_identity = n.id_identity and lower(n.preferred) ~* lower(%s)
					) union all (
						-- anywhere in name
						SELECT
							d_vap.*,
							%s::text AS match_type
						FROM
							dem.v_active_persons d_vap,
							dem.names n
						WHERE
							d_vap.pk_identity = n.id_identity
								AND
							lower(n.firstnames || ' ' || n.lastnames || ' ' || coalesce(n.preferred, '')) ~* lower(%s)
					)) AS super_list ORDER BY lastnames, firstnames, dob
				) AS sorted_list
			"""
			tmp = normalized.strip()
			args = []
			args.append(_('lastname'))
			args.append('^' + tmp)
			args.append(_('firstname'))
			args.append('^' + tmp)
			args.append(_('nickname'))
			args.append('^' + tmp)
			args.append(_('any name part'))
			args.append(tmp)

			queries.append ({
				'cmd': cmd,
				'args': args
			})
			return queries

		# try to split on (major) part separators
		major_parts = regex.split(',|;', normalized)

		# ignore empty parts
		major_parts = [ p.strip() for p in major_parts if p.strip() != '' ]

		# only one "major" part ? (i.e. no ",;" ?)
		if len(major_parts) == 1:
			_log.debug('[%s]: only one non-empty part after splitting by , or ; ("major" part)', normalized)
			queries = self.__generate_queries_from_single_major_part(part = normalized)
			if len(queries) > 0:
				return queries
			return self._generate_dumb_brute_query(search_term)

		# more than one major part (separated by ';,')
		# this else is not needed
		else:
			_log.debug('[%s]: more than one non-empty part after splitting by , or ; ("major" parts)', normalized)
			# parse into name and date parts
			date_parts = []
			name_parts = []
			name_count = 0
			for part in major_parts:
				if part.strip() == '':
					continue
				# any digits ?
				if regex.search("\d+", part):
					# FIXME: parse out whitespace *not* adjacent to a *word*
					date_parts.append(part)
				else:
					tmp = part.strip()
					tmp = regex.split("\s+|\t+", tmp)
					name_count = name_count + len(tmp)
					name_parts.append(tmp)

			_log.debug('found %s character (name) parts and %s number (date ?) parts', len(name_parts), len(date_parts))

			where_parts = []
			# first, handle name parts
			# special case: "<date(s)>, <name> <name>, <date(s)>"
			if (len(name_parts) == 1) and (name_count == 2):
				# usually "first last"
				where_parts.append ({
					'conditions': "firstnames ~ %s and lastnames ~ %s",
					'args': [_('names: first last'), '^' + gmTools.capitalize(name_parts[0][0], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0][1], mode=gmTools.CAPS_NAMES)]
				})
				where_parts.append ({
					'conditions': "lower(firstnames) ~* lower(%s) and lower(lastnames) ~* lower(%s)",
					'args': [_('names: first last'), '^' + name_parts[0][0], '^' + name_parts[0][1]]
				})
				# but sometimes "last first""
				where_parts.append ({
					'conditions': "firstnames ~ %s and lastnames ~ %s",
					'args': [_('names: last, first'), '^' + gmTools.capitalize(name_parts[0][1], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0][0], mode=gmTools.CAPS_NAMES)]
				})
				where_parts.append ({
					'conditions': "lower(firstnames) ~* lower(%s) and lower(lastnames) ~* lower(%s)",
					'args': [_('names: last, first'), '^' + name_parts[0][1], '^' + name_parts[0][0]]
				})
				# or even substrings anywhere in name
				where_parts.append ({
					'conditions': "lower(firstnames || ' ' || lastnames) ~* lower(%s) OR lower(firstnames || ' ' || lastnames) ~* lower(%s)",
					'args': [_('name'), name_parts[0][0], name_parts[0][1]]
				})

			# special case: "<date(s)>, <name(s)>, <name(s)>, <date(s)>"
			elif len(name_parts) == 2:
				# usually "last, first"
				where_parts.append ({
					'conditions': "firstnames ~ %s AND lastnames ~ %s",
					'args': [_('name: last, first'), '^' + ' '.join(map(gmTools.capitalize, name_parts[1])), '^' + ' '.join(map(gmTools.capitalize, name_parts[0]))]
				})
				where_parts.append ({
					'conditions': "lower(firstnames) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
					'args': [_('name: last, first'), '^' + ' '.join(name_parts[1]), '^' + ' '.join(name_parts[0])]
				})
				# but sometimes "first, last"
				where_parts.append ({
					'conditions': "firstnames ~ %s AND lastnames ~ %s",
					'args': [_('name: last, first'), '^' + ' '.join(map(gmTools.capitalize, name_parts[0])), '^' + ' '.join(map(gmTools.capitalize, name_parts[1]))]
				})
				where_parts.append ({
					'conditions': "lower(firstnames) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
					'args': [_('name: last, first'), '^' + ' '.join(name_parts[0]), '^' + ' '.join(name_parts[1])]
				})
				# and sometimes "last, nick"
				where_parts.append ({
					'conditions': "preferred ~ %s AND lastnames ~ %s",
					'args': [_('name: last, first'), '^' + ' '.join(map(gmTools.capitalize, name_parts[1])), '^' + ' '.join(map(gmTools.capitalize, name_parts[0]))]
				})
				where_parts.append ({
					'conditions': "lower(preferred) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
					'args': [_('name: last, first'), '^' + ' '.join(name_parts[1]), '^' + ' '.join(name_parts[0])]
				})

				# or even substrings anywhere in name
				where_parts.append ({
					'conditions': "lower(firstnames || ' ' || lastnames) ~* lower(%s) AND lower(firstnames || ' ' || lastnames) ~* lower(%s)",
					'args': [_('name'), ' '.join(name_parts[0]), ' '.join(name_parts[1])]
				})

			# big trouble - arbitrary number of names
			else:
				# FIXME: deep magic, not sure of rationale ...
				if len(name_parts) == 1:
					for part in name_parts[0]:
						where_parts.append ({
							'conditions': "lower(firstnames || ' ' || lastnames) ~* lower(%s)",
							'args': [_('name'), part]
						})
				else:
					tmp = []
					for part in name_parts:
						tmp.append(' '.join(part))
					for part in tmp:
						where_parts.append ({
							'conditions': "lower(firstnames || ' ' || lastnames) ~* lower(%s)",
							'args': [_('name'), part]
						})

			# secondly handle date parts
			# FIXME: this needs a considerable smart-up !
			if len(date_parts) == 1:
				if len(where_parts) == 0:
					where_parts.append ({
						'conditions': "dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
						'args': [_('date of birth'), date_parts[0].replace(',', '.')]
					})
				if len(where_parts) > 0:
					where_parts[0]['conditions'] += " AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)"
					where_parts[0]['args'].append(date_parts[0].replace(',', '.'))
					where_parts[0]['args'][0] += ', ' + _('date of birth')
				if len(where_parts) > 1:
					where_parts[1]['conditions'] += " AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)"
					where_parts[1]['args'].append(date_parts[0].replace(',', '.'))
					where_parts[1]['args'][0] += ', ' + _('date of birth')
			elif len(date_parts) > 1:
				if len(where_parts) == 0:
					where_parts.append ({
						'conditions': "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)",
						'args': [_('date of birth/death'), date_parts[0].replace(',', '.'), date_parts[1].replace(',', '.')]
					})
				if len(where_parts) > 0:
					where_parts[0]['conditions'] += " 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)",
					where_parts[0]['args'].append(date_parts[0].replace(',', '.'), date_parts[1].replace(',', '.'))
					where_parts[0]['args'][0] += ', ' + _('date of birth/death')
				if len(where_parts) > 1:
					where_parts[1]['conditions'] += " 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)",
					where_parts[1]['args'].append(date_parts[0].replace(',', '.'), date_parts[1].replace(',', '.'))
					where_parts[1]['args'][0] += ', ' + _('date of birth/death')

			# and finally generate the queries ...
			for where_part in where_parts:
				queries.append ({
					'cmd': "SELECT *, %%s::text AS match_type FROM dem.v_active_persons WHERE %s" % where_part['conditions'],
					'args': where_part['args']
				})
			return queries

		return []
Example #12
0
    def _generate_queries_de(self, search_term=None):

        if search_term is None:
            return []

        # check to see if we get away with a simple query ...
        queries = self._generate_simple_query(search_term)
        if len(queries) > 0:
            _log.debug(
                '[%s]: search term with a simple, unambigous structure' %
                search_term)
            return queries

        # no we don't
        _log.debug(
            '[%s]: not a search term with a simple, unambigous structure' %
            search_term)

        search_term = search_term.strip().strip(u',').strip(u';').strip()
        normalized = self._normalize_soundalikes(search_term)

        queries = []

        # "<CHARS>" - single name part
        # yes, I know, this is culture specific (did you read the docs ?)
        if regex.match(u"^(\s|\t)*[a-zäöüßéáúóçøA-ZÄÖÜÇØ]+(\s|\t)*$",
                       search_term,
                       flags=regex.UNICODE):
            _log.debug("[%s]: a single name part", search_term)
            # there's no intermediate whitespace due to the regex
            cmd = u"""
				SELECT DISTINCT ON (pk_identity) * FROM (
					SELECT * FROM ((
						-- lastname
						SELECT d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n
						WHERE d_vap.pk_identity = n.id_identity and lower(n.lastnames) ~* lower(%s)
					) union all (
						-- firstname
						SELECT d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n
						WHERE d_vap.pk_identity = n.id_identity and lower(n.firstnames) ~* lower(%s)
					) union all (
						-- nickname
						SELECT d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n
						WHERE d_vap.pk_identity = n.id_identity and lower(n.preferred) ~* lower(%s)
					) union all (
						-- anywhere in name
						SELECT
							d_vap.*,
							%s::text AS match_type
						FROM
							dem.v_active_persons d_vap,
							dem.names n
						WHERE
							d_vap.pk_identity = n.id_identity
								AND
							lower(n.firstnames || ' ' || n.lastnames || ' ' || coalesce(n.preferred, '')) ~* lower(%s)
					)) AS super_list ORDER BY lastnames, firstnames, dob
				) AS sorted_list
			"""
            tmp = normalized.strip()
            args = []
            args.append(_('lastname'))
            args.append('^' + tmp)
            args.append(_('firstname'))
            args.append('^' + tmp)
            args.append(_('nickname'))
            args.append('^' + tmp)
            args.append(_('any name part'))
            args.append(tmp)

            queries.append({'cmd': cmd, 'args': args})
            return queries

        # try to split on (major) part separators
        parts_list = regex.split(u",|;", normalized)

        # ignore empty parts
        parts_list = [p.strip() for p in parts_list if p.strip() != u'']

        # only one "major" part ? (i.e. no ",;" ?)
        if len(parts_list) == 1:
            # re-split on whitespace
            sub_parts_list = regex.split(u"\s*|\t*", normalized)
            # ignore empty parts
            sub_parts_list = [
                p.strip() for p in sub_parts_list if p.strip() != u''
            ]

            # parse into name/date parts
            date_count = 0
            name_parts = []
            for part in sub_parts_list:
                # skip empty parts
                if part.strip() == u'':
                    continue
                # any digit signifies a date
                # FIXME: what about "<40" ?
                if regex.search(u"\d", part, flags=regex.UNICODE):
                    date_count = date_count + 1
                    date_part = part
                else:
                    name_parts.append(part)

            # exactly 2 words ?
            if len(sub_parts_list) == 2:
                # no date = "first last" or "last first"
                if date_count == 0:
                    # assumption: first last
                    queries.append({
                        'cmd':
                        u"SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.pk_identity = n.id_identity and n.firstnames ~ %s AND n.lastnames ~ %s",
                        'args': [
                            _('name: first-last'), '^' + gmTools.capitalize(
                                name_parts[0], mode=gmTools.CAPS_NAMES),
                            '^' + gmTools.capitalize(name_parts[1],
                                                     mode=gmTools.CAPS_NAMES)
                        ]
                    })
                    queries.append({
                        'cmd':
                        u"SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.pk_identity = n.id_identity and lower(n.firstnames) ~* lower(%s) AND lower(n.lastnames) ~* lower(%s)",
                        'args': [
                            _('name: first-last'), '^' + name_parts[0],
                            '^' + name_parts[1]
                        ]
                    })
                    # assumption: last first
                    queries.append({
                        'cmd':
                        u"SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.pk_identity = n.id_identity and n.firstnames ~ %s AND n.lastnames ~ %s",
                        'args': [
                            _('name: last-first'), '^' + gmTools.capitalize(
                                name_parts[1], mode=gmTools.CAPS_NAMES),
                            '^' + gmTools.capitalize(name_parts[0],
                                                     mode=gmTools.CAPS_NAMES)
                        ]
                    })
                    queries.append({
                        'cmd':
                        u"SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.pk_identity = n.id_identity and lower(n.firstnames) ~* lower(%s) AND lower(n.lastnames) ~* lower(%s)",
                        'args': [
                            _('name: last-first'), '^' + name_parts[1],
                            '^' + name_parts[0]
                        ]
                    })
                    # assumption: last nick
                    queries.append({
                        'cmd':
                        u"SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.pk_identity = n.id_identity and n.preferred ~ %s AND n.lastnames ~ %s",
                        'args': [
                            _('name: last-nick'), '^' + gmTools.capitalize(
                                name_parts[1], mode=gmTools.CAPS_NAMES),
                            '^' + gmTools.capitalize(name_parts[0],
                                                     mode=gmTools.CAPS_NAMES)
                        ]
                    })
                    queries.append({
                        'cmd':
                        u"SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.pk_identity = n.id_identity and lower(n.preferred) ~* lower(%s) AND lower(n.lastnames) ~* lower(%s)",
                        'args': [
                            _('name: last-nick'), '^' + name_parts[1],
                            '^' + name_parts[0]
                        ]
                    })
                    # name parts anywhere inside name - third order query ...
                    queries.append({
                        'cmd':
                        u"""SELECT DISTINCT ON (id_identity)
									d_vap.*,
									%s::text AS match_type
								FROM
									dem.v_active_persons d_vap,
									dem.names n
								WHERE
									d_vap.pk_identity = n.id_identity
										AND
									-- name_parts[0]
									lower(n.firstnames || ' ' || n.lastnames) ~* lower(%s)
										AND
									-- name_parts[1]
									lower(n.firstnames || ' ' || n.lastnames) ~* lower(%s)""",
                        'args': [_('name'), name_parts[0], name_parts[1]]
                    })
                    return queries
                # FIXME: either "name date" or "date date"
                _log.error("don't know how to generate queries for [%s]" %
                           search_term)
                return queries

            # exactly 3 words ?
            if len(sub_parts_list) == 3:
                # special case: 3 words, exactly 1 of them a date, no ",;"
                if date_count == 1:
                    # assumption: first, last, dob - first order
                    queries.append({
                        'cmd':
                        u"SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.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)",
                        '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'.')
                        ]
                    })
                    queries.append({
                        'cmd':
                        u"SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.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)",
                        'args': [
                            _('names: first-last, date of birth'),
                            '^' + name_parts[0], '^' + name_parts[1],
                            date_part.replace(u',', u'.')
                        ]
                    })
                    # assumption: last, first, dob - second order query
                    queries.append({
                        'cmd':
                        u"SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.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)",
                        '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'.')
                        ]
                    })
                    queries.append({
                        'cmd':
                        u"SELECT DISTINCT ON (id_identity) d_vap.*, %s::text AS match_type FROM dem.v_active_persons d_vap, dem.names n WHERE d_vap.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)",
                        'args': [
                            _('names: last-first, dob'), '^' + name_parts[1],
                            '^' + name_parts[0],
                            date_part.replace(u',', u'.')
                        ]
                    })
                    # name parts anywhere in name - third order query ...
                    queries.append({
                        'cmd':
                        u"""SELECT DISTINCT ON (id_identity)
									d_vap.*,
									%s::text AS match_type
								FROM
									dem.v_active_persons d_vap,
									dem.names n
								WHERE
									d_vap.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)
						""",
                        'args': [
                            _('name, date of birth'), name_parts[0],
                            name_parts[1],
                            date_part.replace(u',', u'.')
                        ]
                    })
                    return queries
                # FIXME: "name name name" or "name date date"
                queries.append(self._generate_dumb_brute_query(search_term))
                return queries

            # FIXME: no ',;' but neither "name name" nor "name name date"
            queries.append(self._generate_dumb_brute_query(search_term))
            return queries

        # more than one major part (separated by ';,')
        else:
            # parse into name and date parts
            date_parts = []
            name_parts = []
            name_count = 0
            for part in parts_list:
                if part.strip() == u'':
                    continue
                # any digits ?
                if regex.search(u"\d+", part, flags=regex.UNICODE):
                    # FIXME: parse out whitespace *not* adjacent to a *word*
                    date_parts.append(part)
                else:
                    tmp = part.strip()
                    tmp = regex.split(u"\s*|\t*", tmp)
                    name_count = name_count + len(tmp)
                    name_parts.append(tmp)

            where_parts = []
            # first, handle name parts
            # special case: "<date(s)>, <name> <name>, <date(s)>"
            if (len(name_parts) == 1) and (name_count == 2):
                # usually "first last"
                where_parts.append({
                    'conditions':
                    u"firstnames ~ %s and lastnames ~ %s",
                    'args': [
                        _('names: first last'), '^' + gmTools.capitalize(
                            name_parts[0][0], mode=gmTools.CAPS_NAMES),
                        '^' + gmTools.capitalize(name_parts[0][1],
                                                 mode=gmTools.CAPS_NAMES)
                    ]
                })
                where_parts.append({
                    'conditions':
                    u"lower(firstnames) ~* lower(%s) and lower(lastnames) ~* lower(%s)",
                    'args': [
                        _('names: first last'), '^' + name_parts[0][0],
                        '^' + name_parts[0][1]
                    ]
                })
                # but sometimes "last first""
                where_parts.append({
                    'conditions':
                    u"firstnames ~ %s and lastnames ~ %s",
                    'args': [
                        _('names: last, first'), '^' + gmTools.capitalize(
                            name_parts[0][1], mode=gmTools.CAPS_NAMES),
                        '^' + gmTools.capitalize(name_parts[0][0],
                                                 mode=gmTools.CAPS_NAMES)
                    ]
                })
                where_parts.append({
                    'conditions':
                    u"lower(firstnames) ~* lower(%s) and lower(lastnames) ~* lower(%s)",
                    'args': [
                        _('names: last, first'), '^' + name_parts[0][1],
                        '^' + name_parts[0][0]
                    ]
                })
                # or even substrings anywhere in name
                where_parts.append({
                    'conditions':
                    u"lower(firstnames || ' ' || lastnames) ~* lower(%s) OR lower(firstnames || ' ' || lastnames) ~* lower(%s)",
                    'args': [_('name'), name_parts[0][0], name_parts[0][1]]
                })

            # special case: "<date(s)>, <name(s)>, <name(s)>, <date(s)>"
            elif len(name_parts) == 2:
                # usually "last, first"
                where_parts.append({
                    'conditions':
                    u"firstnames ~ %s AND lastnames ~ %s",
                    'args': [
                        _('name: last, first'),
                        '^' + ' '.join(map(gmTools.capitalize, name_parts[1])),
                        '^' + ' '.join(map(gmTools.capitalize, name_parts[0]))
                    ]
                })
                where_parts.append({
                    'conditions':
                    u"lower(firstnames) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
                    'args': [
                        _('name: last, first'), '^' + ' '.join(name_parts[1]),
                        '^' + ' '.join(name_parts[0])
                    ]
                })
                # but sometimes "first, last"
                where_parts.append({
                    'conditions':
                    u"firstnames ~ %s AND lastnames ~ %s",
                    'args': [
                        _('name: last, first'),
                        '^' + ' '.join(map(gmTools.capitalize, name_parts[0])),
                        '^' + ' '.join(map(gmTools.capitalize, name_parts[1]))
                    ]
                })
                where_parts.append({
                    'conditions':
                    u"lower(firstnames) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
                    'args': [
                        _('name: last, first'), '^' + ' '.join(name_parts[0]),
                        '^' + ' '.join(name_parts[1])
                    ]
                })
                # and sometimes "last, nick"
                where_parts.append({
                    'conditions':
                    u"preferred ~ %s AND lastnames ~ %s",
                    'args': [
                        _('name: last, first'),
                        '^' + ' '.join(map(gmTools.capitalize, name_parts[1])),
                        '^' + ' '.join(map(gmTools.capitalize, name_parts[0]))
                    ]
                })
                where_parts.append({
                    'conditions':
                    u"lower(preferred) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
                    'args': [
                        _('name: last, first'), '^' + ' '.join(name_parts[1]),
                        '^' + ' '.join(name_parts[0])
                    ]
                })

                # or even substrings anywhere in name
                where_parts.append({
                    'conditions':
                    u"lower(firstnames || ' ' || lastnames) ~* lower(%s) AND lower(firstnames || ' ' || lastnames) ~* lower(%s)",
                    'args': [
                        _('name'), ' '.join(name_parts[0]),
                        ' '.join(name_parts[1])
                    ]
                })

            # big trouble - arbitrary number of names
            else:
                # FIXME: deep magic, not sure of rationale ...
                if len(name_parts) == 1:
                    for part in name_parts[0]:
                        where_parts.append({
                            'conditions':
                            u"lower(firstnames || ' ' || lastnames) ~* lower(%s)",
                            'args': [_('name'), part]
                        })
                else:
                    tmp = []
                    for part in name_parts:
                        tmp.append(' '.join(part))
                    for part in tmp:
                        where_parts.append({
                            'conditions':
                            u"lower(firstnames || ' ' || lastnames) ~* lower(%s)",
                            'args': [_('name'), part]
                        })

            # secondly handle date parts
            # FIXME: this needs a considerable smart-up !
            if len(date_parts) == 1:
                if len(where_parts) == 0:
                    where_parts.append({
                        'conditions':
                        u"dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
                        'args': [
                            _('date of birth'),
                            date_parts[0].replace(u',', u'.')
                        ]
                    })
                if len(where_parts) > 0:
                    where_parts[0][
                        'conditions'] += u" AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)"
                    where_parts[0]['args'].append(date_parts[0].replace(
                        u',', u'.'))
                    where_parts[0]['args'][0] += u', ' + _('date of birth')
                if len(where_parts) > 1:
                    where_parts[1][
                        'conditions'] += u" AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)"
                    where_parts[1]['args'].append(date_parts[0].replace(
                        u',', u'.'))
                    where_parts[1]['args'][0] += u', ' + _('date of birth')
            elif len(date_parts) > 1:
                if len(where_parts) == 0:
                    where_parts.append({
                        'conditions':
                        u"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)",
                        'args': [
                            _('date of birth/death'),
                            date_parts[0].replace(u',', u'.'),
                            date_parts[1].replace(u',', u'.')
                        ]
                    })
                if len(where_parts) > 0:
                    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)",
                    where_parts[0]['args'].append(
                        date_parts[0].replace(u',', u'.'),
                        date_parts[1].replace(u',', u'.'))
                    where_parts[0]['args'][0] += u', ' + _(
                        'date of birth/death')
                if len(where_parts) > 1:
                    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)",
                    where_parts[1]['args'].append(
                        date_parts[0].replace(u',', u'.'),
                        date_parts[1].replace(u',', u'.'))
                    where_parts[1]['args'][0] += u', ' + _(
                        'date of birth/death')

            # and finally generate the queries ...
            for where_part in where_parts:
                queries.append({
                    'cmd':
                    u"SELECT *, %%s::text AS match_type FROM dem.v_active_persons WHERE %s"
                    % where_part['conditions'],
                    'args':
                    where_part['args']
                })
            return queries

        return []
Example #13
0
    def _generate_simple_query(self, raw):
        """Compose queries if search term seems unambigous."""
        queries = []

        raw = raw.strip().rstrip(u',').rstrip(u';').strip()

        # "<digits>" - GNUmed patient PK or DOB
        if regex.match(u"^(\s|\t)*\d+(\s|\t)*$", raw, flags=regex.UNICODE):
            _log.debug("[%s]: a PK or DOB" % raw)
            tmp = raw.strip()
            queries.append({
                'cmd':
                u"SELECT *, %s::text AS match_type FROM dem.v_active_persons WHERE pk_identity = %s ORDER BY lastnames, firstnames, dob",
                'args': [_('internal patient ID'), tmp]
            })
            if len(tmp) > 7:  # DOB needs at least 8 digits
                queries.append({
                    'cmd':
                    u"SELECT *, %s::text AS match_type FROM dem.v_active_persons WHERE dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone) ORDER BY lastnames, firstnames, dob",
                    'args': [_('date of birth'),
                             tmp.replace(',', '.')]
                })
            queries.append({
                'cmd': u"""
					SELECT vba.*, %s::text AS match_type
					FROM
						dem.lnk_identity2ext_id li2ext_id,
						dem.v_active_persons vba
					WHERE
						vba.pk_identity = li2ext_id.id_identity and lower(li2ext_id.external_id) ~* lower(%s)
					ORDER BY
						lastnames, firstnames, dob
				""",
                'args': [_('external patient ID'), tmp]
            })
            return queries

        # "<d igi ts>" - DOB or patient PK
        if regex.match(u"^(\d|\s|\t)+$", raw, flags=regex.UNICODE):
            _log.debug("[%s]: a DOB or PK" % raw)
            queries.append({
                'cmd':
                u"SELECT *, %s::text AS match_type FROM dem.v_active_persons WHERE dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone) ORDER BY lastnames, firstnames, dob",
                'args': [_('date of birth'),
                         raw.replace(',', '.')]
            })
            tmp = raw.replace(u' ', u'')
            tmp = tmp.replace(u'\t', u'')
            queries.append({
                'cmd':
                u"SELECT *, %s::text AS match_type FROM dem.v_active_persons WHERE pk_identity LIKE %s%%",
                'args': [_('internal patient ID'), tmp]
            })
            return queries

        # "#<di git  s>" - GNUmed patient PK
        if regex.match(u"^(\s|\t)*#(\d|\s|\t)+$", raw, flags=regex.UNICODE):
            _log.debug("[%s]: a PK or external ID" % raw)
            tmp = raw.replace(u'#', u'')
            tmp = tmp.strip()
            tmp = tmp.replace(u' ', u'')
            tmp = tmp.replace(u'\t', u'')
            # this seemingly stupid query ensures the PK actually exists
            queries.append({
                'cmd':
                u"SELECT *, %s::text AS match_type FROM dem.v_active_persons WHERE pk_identity = %s ORDER BY lastnames, firstnames, dob",
                'args': [_('internal patient ID'), tmp]
            })
            # but might also be an external ID
            tmp = raw.replace(u'#', u'')
            tmp = tmp.strip()
            tmp = tmp.replace(u' ', u'***DUMMY***')
            tmp = tmp.replace(u'\t', u'***DUMMY***')
            tmp = tmp.replace(u'***DUMMY***', u'(\s|\t|-|/)*')
            queries.append({
                'cmd': u"""
					SELECT vba.*, %s::text AS match_type FROM dem.lnk_identity2ext_id li2ext_id, dem.v_active_persons vba
					WHERE vba.pk_identity = li2ext_id.id_identity and lower(li2ext_id.external_id) ~* lower(%s)
					ORDER BY lastnames, firstnames, dob""",
                'args': [_('external patient ID'), tmp]
            })
            return queries

        # "#<di/git s or c-hars>" - external ID
        if regex.match(u"^(\s|\t)*#.+$", raw, flags=regex.UNICODE):
            _log.debug("[%s]: an external ID" % raw)
            tmp = raw.replace(u'#', u'')
            tmp = tmp.strip()
            tmp = tmp.replace(u' ', u'***DUMMY***')
            tmp = tmp.replace(u'\t', u'***DUMMY***')
            tmp = tmp.replace(u'-', u'***DUMMY***')
            tmp = tmp.replace(u'/', u'***DUMMY***')
            tmp = tmp.replace(u'***DUMMY***', u'(\s|\t|-|/)*')
            queries.append({
                'cmd': u"""
					SELECT
						vba.*,
						%s::text AS match_type
					FROM
						dem.lnk_identity2ext_id li2ext_id,
						dem.v_active_persons vba
					WHERE
						vba.pk_identity = li2ext_id.id_identity
							AND
						lower(li2ext_id.external_id) ~* lower(%s)
					ORDER BY
						lastnames, firstnames, dob""",
                'args': [_('external patient ID'), tmp]
            })
            return queries

        # digits interspersed with "./-" or blank space - DOB
        if regex.match(
                u"^(\s|\t)*\d+(\s|\t|\.|\-|/)*\d+(\s|\t|\.|\-|/)*\d+(\s|\t|\.)*$",
                raw,
                flags=regex.UNICODE):
            _log.debug("[%s]: a DOB" % raw)
            tmp = raw.strip()
            while u'\t\t' in tmp:
                tmp = tmp.replace(u'\t\t', u' ')
            while u'  ' in tmp:
                tmp = tmp.replace(u'  ', u' ')
            # apparently not needed due to PostgreSQL smarts...
            #tmp = tmp.replace('-', '.')
            #tmp = tmp.replace('/', '.')
            queries.append({
                'cmd':
                u"SELECT *, %s AS match_type FROM dem.v_active_persons WHERE dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone) ORDER BY lastnames, firstnames, dob",
                'args': [_('date of birth'),
                         tmp.replace(',', '.')]
            })
            return queries

        # " , <alpha>" - first name
        if regex.match(u"^(\s|\t)*,(\s|\t)*([^0-9])+(\s|\t)*$",
                       raw,
                       flags=regex.UNICODE):
            _log.debug("[%s]: a firstname" % raw)
            tmp = self._normalize_soundalikes(raw[1:].strip())
            cmd = u"""
SELECT DISTINCT ON (pk_identity) * FROM (
	SELECT *, %s AS match_type FROM ((
		SELECT d_vap.*
		FROM dem.names, dem.v_active_persons d_vap
		WHERE dem.names.firstnames ~ %s and d_vap.pk_identity = dem.names.id_identity
	) union all (
		SELECT d_vap.*
		FROM dem.names, dem.v_active_persons d_vap
		WHERE dem.names.firstnames ~ %s and d_vap.pk_identity = dem.names.id_identity
	)) AS super_list ORDER BY lastnames, firstnames, dob
) AS sorted_list"""
            queries.append({
                'cmd':
                cmd,
                'args': [
                    _('first name'),
                    '^' + gmTools.capitalize(tmp, mode=gmTools.CAPS_NAMES),
                    '^' + tmp
                ]
            })
            return queries

        # "*|$<...>" - DOB
        if regex.match(u"^(\s|\t)*(\*|\$).+$", raw, flags=regex.UNICODE):
            _log.debug("[%s]: a DOB" % raw)
            tmp = raw.replace(u'*', u'')
            tmp = tmp.replace(u'$', u'')
            queries.append({
                'cmd':
                u"SELECT *, %s AS match_type FROM dem.v_active_persons WHERE dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone) ORDER BY lastnames, firstnames, dob",
                'args': [_('date of birth'),
                         tmp.replace(u',', u'.')]
            })
            return queries

        return queries  # = []
Example #14
0
def read_persons_from_msva_file(filename=None, encoding=None):

	if encoding is None:
		encoding = MSVA_encoding

	pats_file = io.open(filename, mode = 'rt', encoding = encoding)

	dtos = []

	for line in pats_file:
		if len(line) < MSVA_line_len:
			continue			# perhaps raise Exception ?

		dto = gmPerson.cDTO_person()
		dto.source = 'Med.Manager/CA'

		dto.firstnames = '%s %s' % (
			gmTools.capitalize(line[:20].strip(), gmTools.CAPS_FIRST_ONLY),		# should be _NAMES
			gmTools.capitalize(line[20:22].strip(), gmTools.CAPS_FIRST_ONLY)	# should be _NAMES
		)
		dto.lastnames = gmTools.capitalize(line[22:47].strip(), gmTools.CAPS_FIRST_ONLY)	# should be _NAMES

		region = line[59:61]
		dto.remember_external_id (
			name = 'PHN (%s.CA)' % region,
			value = line[47:57],
			issuer = 'MOH (%s.CA)' % region
		)

		dob = time.strptime(line[65:73].strip(), MSVA_dob_format)
		dto.dob = pyDT.datetime(dob.tm_year, dob.tm_mon, dob.tm_mday, tzinfo = gmDateTime.gmCurrentLocalTimezone)
		dto.gender = line[83].lower()

		dto.remember_external_id (
			name = 'MM (CA) Chart #',
			value = line[84:92],
			issuer = 'Medical Manager (CA) application'
		)

		# this is the home address
		street = '%s // %s' % (
			gmTools.capitalize(line[92:117].strip(), gmTools.CAPS_FIRST),
			gmTools.capitalize(line[117:142].strip(), gmTools.CAPS_FIRST)
		)
		dto.remember_address (
			number = '?',
			street = street,
			urb = line[142:167],
			region_code = line[167:169],
			zip = line[169:178],
			country_code = 'CA'
		)

		# channel types must correspond to GNUmed database comm type
		dto.remember_comm_channel(channel = 'homephone', url = line[178:188])
		dto.remember_comm_channel(channel = 'workphone', url = line[188:198])

		dto.remember_external_id (
			name = 'Social Insurance Number',
			value = line[198:207],
			issuer = 'Canada'
		)

		dtos.append(dto)

	pats_file.close()

	return dtos
Example #15
0
def read_persons_from_msva_file(filename=None, encoding=None):

    if encoding is None:
        encoding = MSVA_encoding

    pats_file = io.open(filename, mode='rt', encoding=encoding)

    dtos = []

    for line in pats_file:
        if len(line) < MSVA_line_len:
            continue  # perhaps raise Exception ?

        dto = gmPerson.cDTO_person()
        dto.source = 'Med.Manager/CA'

        dto.firstnames = '%s %s' % (
            gmTools.capitalize(line[:20].strip(),
                               gmTools.CAPS_FIRST_ONLY),  # should be _NAMES
            gmTools.capitalize(line[20:22].strip(),
                               gmTools.CAPS_FIRST_ONLY)  # should be _NAMES
        )
        dto.lastnames = gmTools.capitalize(
            line[22:47].strip(), gmTools.CAPS_FIRST_ONLY)  # should be _NAMES

        region = line[59:61]
        dto.remember_external_id(name='PHN (%s.CA)' % region,
                                 value=line[47:57],
                                 issuer='MOH (%s.CA)' % region)

        dob = time.strptime(line[65:73].strip(), MSVA_dob_format)
        dto.dob = pyDT.datetime(dob.tm_year,
                                dob.tm_mon,
                                dob.tm_mday,
                                tzinfo=gmDateTime.gmCurrentLocalTimezone)
        dto.gender = line[83].casefold()

        dto.remember_external_id(name='MM (CA) Chart #',
                                 value=line[84:92],
                                 issuer='Medical Manager (CA) application')

        # this is the home address
        street = '%s // %s' % (
            gmTools.capitalize(line[92:117].strip(), gmTools.CAPS_FIRST),
            gmTools.capitalize(line[117:142].strip(), gmTools.CAPS_FIRST))
        dto.remember_address(number='?',
                             street=street,
                             urb=line[142:167],
                             region_code=line[167:169],
                             zip=line[169:178],
                             country_code='CA')

        # channel types must correspond to GNUmed database comm type
        dto.remember_comm_channel(channel='homephone', url=line[178:188])
        dto.remember_comm_channel(channel='workphone', url=line[188:198])

        dto.remember_external_id(name='Social Insurance Number',
                                 value=line[198:207],
                                 issuer='Canada')

        dtos.append(dto)

    pats_file.close()

    return dtos
Example #16
0
	def _generate_simple_query(self, raw):
		"""Compose queries if search term seems unambigous."""
		queries = []

		raw = raw.strip().rstrip(u',').rstrip(u';').strip()

		# "<digits>" - GNUmed patient PK or DOB
		if regex.match(u"^(\s|\t)*\d+(\s|\t)*$", raw, flags = regex.LOCALE | regex.UNICODE):
			_log.debug("[%s]: a PK or DOB" % raw)
			tmp = raw.strip()
			queries.append ({
				'cmd': u"SELECT *, %s::text AS match_type FROM dem.v_basic_person WHERE pk_identity = %s ORDER BY lastnames, firstnames, dob",
				'args': [_('internal patient ID'), tmp]
			})
			if len(tmp) > 7:	# DOB needs at least 8 digits
				queries.append ({
					'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",
					'args': [_('date of birth'), tmp.replace(',', '.')]
				})
			queries.append ({
				'cmd': u"""
					SELECT vba.*, %s::text AS match_type
					FROM
						dem.lnk_identity2ext_id li2ext_id,
						dem.v_basic_person vba
					WHERE
						vba.pk_identity = li2ext_id.id_identity and lower(li2ext_id.external_id) ~* lower(%s)
					ORDER BY
						lastnames, firstnames, dob
				""",
				'args': [_('external patient ID'), tmp]
			})
			return queries

		# "<d igi ts>" - DOB or patient PK
		if regex.match(u"^(\d|\s|\t)+$", raw, flags = regex.LOCALE | regex.UNICODE):
			_log.debug("[%s]: a DOB or PK" % raw)
			queries.append ({
				'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",
				'args': [_('date of birth'), raw.replace(',', '.')]
			})
			tmp = raw.replace(u' ', u'')
			tmp = tmp.replace(u'\t', u'')
			queries.append ({
				'cmd': u"SELECT *, %s::text AS match_type FROM dem.v_basic_person WHERE pk_identity LIKE %s%%",
				'args': [_('internal patient ID'), tmp]
			})
			return queries

		# "#<di git  s>" - GNUmed patient PK
		if regex.match(u"^(\s|\t)*#(\d|\s|\t)+$", raw, flags = regex.LOCALE | regex.UNICODE):
			_log.debug("[%s]: a PK or external ID" % raw)
			tmp = raw.replace(u'#', u'')
			tmp = tmp.strip()
			tmp = tmp.replace(u' ', u'')
			tmp = tmp.replace(u'\t', u'')
			# this seemingly stupid query ensures the PK actually exists
			queries.append ({
				'cmd': u"SELECT *, %s::text AS match_type FROM dem.v_basic_person WHERE pk_identity = %s ORDER BY lastnames, firstnames, dob",
				'args': [_('internal patient ID'), tmp]
			})
			# but might also be an external ID
			tmp = raw.replace(u'#', u'')
			tmp = tmp.strip()
			tmp = tmp.replace(u' ',  u'***DUMMY***')
			tmp = tmp.replace(u'\t', u'***DUMMY***')
			tmp = tmp.replace(u'***DUMMY***', u'(\s|\t|-|/)*')
			queries.append ({
				'cmd': u"""
					SELECT vba.*, %s::text AS match_type FROM dem.lnk_identity2ext_id li2ext_id, dem.v_basic_person vba
					WHERE vba.pk_identity = li2ext_id.id_identity and lower(li2ext_id.external_id) ~* lower(%s)
					ORDER BY lastnames, firstnames, dob""",
				'args': [_('external patient ID'), tmp]
			})
			return queries

		# "#<di/git s or c-hars>" - external ID (or PUPIC)
		if regex.match(u"^(\s|\t)*#.+$", raw, flags = regex.LOCALE | regex.UNICODE):
			_log.debug("[%s]: an external ID" % raw)
			tmp = raw.replace(u'#', u'')
			tmp = tmp.strip()
			tmp = tmp.replace(u' ',  u'***DUMMY***')
			tmp = tmp.replace(u'\t', u'***DUMMY***')
			tmp = tmp.replace(u'-',  u'***DUMMY***')
			tmp = tmp.replace(u'/',  u'***DUMMY***')
			tmp = tmp.replace(u'***DUMMY***', u'(\s|\t|-|/)*')
			queries.append ({
				'cmd': u"""
					SELECT
						vba.*,
						%s::text AS match_type
					FROM
						dem.lnk_identity2ext_id li2ext_id,
						dem.v_basic_person vba
					WHERE
						vba.pk_identity = li2ext_id.id_identity
							AND
						lower(li2ext_id.external_id) ~* lower(%s)
					ORDER BY
						lastnames, firstnames, dob""",
				'args': [_('external patient ID'), tmp]
			})
			return queries

		# digits interspersed with "./-" or blank space - DOB
		if regex.match(u"^(\s|\t)*\d+(\s|\t|\.|\-|/)*\d+(\s|\t|\.|\-|/)*\d+(\s|\t|\.)*$", raw, flags = regex.LOCALE | regex.UNICODE):
			_log.debug("[%s]: a DOB" % raw)
			tmp = raw.strip()
			while u'\t\t' in tmp: tmp = tmp.replace(u'\t\t', u' ')
			while u'  ' in tmp: tmp = tmp.replace(u'  ', u' ')
			# apparently not needed due to PostgreSQL smarts...
			#tmp = tmp.replace('-', '.')
			#tmp = tmp.replace('/', '.')
			queries.append ({
				'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",
				'args': [_('date of birth'), tmp.replace(',', '.')]
			})
			return queries

		# " , <alpha>" - first name
		if regex.match(u"^(\s|\t)*,(\s|\t)*([^0-9])+(\s|\t)*$", raw, flags = regex.LOCALE | regex.UNICODE):
			_log.debug("[%s]: a firstname" % raw)
			tmp = self._normalize_soundalikes(raw[1:].strip())
			cmd = u"""
SELECT DISTINCT ON (pk_identity) * FROM (
	SELECT *, %s AS match_type FROM ((
		SELECT vbp.*
		FROM dem.names, dem.v_basic_person vbp
		WHERE dem.names.firstnames ~ %s and vbp.pk_identity = dem.names.id_identity
	) union all (
		SELECT vbp.*
		FROM dem.names, dem.v_basic_person vbp
		WHERE dem.names.firstnames ~ %s and vbp.pk_identity = dem.names.id_identity
	)) AS super_list ORDER BY lastnames, firstnames, dob
) AS sorted_list"""
			queries.append ({
				'cmd': cmd,
				'args': [_('first name'), '^' + gmTools.capitalize(tmp, mode=gmTools.CAPS_NAMES), '^' + tmp]
			})
			return queries

		# "*|$<...>" - DOB
		if regex.match(u"^(\s|\t)*(\*|\$).+$", raw, flags = regex.LOCALE | regex.UNICODE):
			_log.debug("[%s]: a DOB" % raw)
			tmp = raw.replace(u'*', u'')
			tmp = tmp.replace(u'$', u'')
			queries.append ({
				'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",
				'args': [_('date of birth'), tmp.replace(u',', u'.')]
			})
			return queries

		return queries	# = []
Example #17
0
	def _generate_queries_de(self, search_term=None):

		if search_term is None:
			return []

		# check to see if we get away with a simple query ...
		queries = self._generate_simple_query(search_term)
		if len(queries) > 0:
			_log.debug('[%s]: search term with a simple, unambigous structure' % search_term)
			return queries

		# no we don't
		_log.debug('[%s]: not a search term with a simple, unambigous structure' % search_term)

		search_term = search_term.strip().strip(u',').strip(u';').strip()
		normalized = self._normalize_soundalikes(search_term)

		queries = []

		# "<CHARS>" - single name part
		# yes, I know, this is culture specific (did you read the docs ?)
		if regex.match(u"^(\s|\t)*[a-zäöüßéáúóçøA-ZÄÖÜÇØ]+(\s|\t)*$", search_term, flags = regex.LOCALE | regex.UNICODE):	
			_log.debug("[%s]: a single name part", search_term)
			# there's no intermediate whitespace due to the regex
			cmd = u"""
				SELECT DISTINCT ON (pk_identity) * FROM (
					SELECT * FROM ((
						-- lastname
						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)
					) union all (
						-- firstname
						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)
					) union all (
						-- nickname
						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.preferred) ~* lower(%s)
					) union all (
						-- anywhere in name
						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)
					)) AS super_list ORDER BY lastnames, firstnames, dob
				) AS sorted_list
			"""
			tmp = normalized.strip()
			args = []
			args.append(_('lastname'))
			args.append('^' + tmp)
			args.append(_('firstname'))
			args.append('^' + tmp)
			args.append(_('nickname'))
			args.append('^' + tmp)
			args.append(_('any name part'))
			args.append(tmp)

			queries.append ({
				'cmd': cmd,
				'args': args
			})
			return queries

		# try to split on (major) part separators
		parts_list = regex.split(u",|;", normalized)

		# ignore empty parts
		parts_list = [ p.strip() for p in parts_list if p.strip() != u'' ]

		# only one "major" part ? (i.e. no ",;" ?)
		if len(parts_list) == 1:
			# re-split on whitespace
			sub_parts_list = regex.split(u"\s*|\t*", normalized)
			# ignore empty parts
			sub_parts_list = [ p.strip() for p in sub_parts_list if p.strip() != u'' ]

			# parse into name/date parts
			date_count = 0
			name_parts = []
			for part in sub_parts_list:
				# skip empty parts
				if part.strip() == u'':
					continue
				# any digit signifies a date
				# FIXME: what about "<40" ?
				if regex.search(u"\d", part, flags = regex.LOCALE | regex.UNICODE):
					date_count = date_count + 1
					date_part = part
				else:
					name_parts.append(part)

			# exactly 2 words ?
			if len(sub_parts_list) == 2:
				# no date = "first last" or "last first"
				if date_count == 0:
					# assumption: first last
					queries.append ({
						'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",
						'args': [_('name: first-last'), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES)]
					})
					queries.append ({
						'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)",
						'args': [_('name: first-last'), '^' + name_parts[0], '^' + name_parts[1]]
					})
					# assumption: last first
					queries.append ({
						'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",
						'args': [_('name: last-first'), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES)]
					})
					queries.append ({
						'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)",
						'args': [_('name: last-first'), '^' + name_parts[1], '^' + name_parts[0]]
					})
					print "before nick"
					print queries
					# assumption: last nick
					queries.append ({
						'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.preferred ~ %s AND n.lastnames ~ %s",
						'args': [_('name: last-nick'), '^' + gmTools.capitalize(name_parts[1], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0], mode=gmTools.CAPS_NAMES)]
					})
					queries.append ({
						'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.preferred) ~* lower(%s) AND lower(n.lastnames) ~* lower(%s)",
						'args': [_('name: last-nick'), '^' + name_parts[1], '^' + name_parts[0]]
					})
					print "after nick"
					print queries
					# name parts anywhere inside name - third order query ...
					queries.append ({
						'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
									-- name_parts[0]
									lower(n.firstnames || ' ' || n.lastnames) ~* lower(%s)
										AND
									-- name_parts[1]
									lower(n.firstnames || ' ' || n.lastnames) ~* lower(%s)""",
						'args': [_('name'), name_parts[0], name_parts[1]]
					})
					return queries
				# FIXME: either "name date" or "date date"
				_log.error("don't know how to generate queries for [%s]" % search_term)
				return queries

			# exactly 3 words ?
			if len(sub_parts_list) == 3:
				# special case: 3 words, exactly 1 of them a date, no ",;"
				if date_count == 1:
					# assumption: first, last, dob - first order
					queries.append ({
						'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)",
						'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'.')]
					})
					queries.append ({
						'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)",
						'args': [_('names: first-last, date of birth'), '^' + name_parts[0], '^' + name_parts[1], date_part.replace(u',', u'.')]
					})
					# assumption: last, first, dob - second order query
					queries.append ({
						'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)",
						'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'.')]
					})
					queries.append ({
						'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)",
						'args': [_('names: last-first, dob'), '^' + name_parts[1], '^' + name_parts[0], date_part.replace(u',', u'.')]
					})
					# name parts anywhere in name - third order query ...
					queries.append ({
						'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)
						""",
						'args': [_('name, date of birth'), name_parts[0], name_parts[1], date_part.replace(u',', u'.')]
					})
					return queries
				# FIXME: "name name name" or "name date date"
				queries.append(self._generate_dumb_brute_query(search_term))
				return queries

			# FIXME: no ',;' but neither "name name" nor "name name date"
			queries.append(self._generate_dumb_brute_query(search_term))
			return queries

		# more than one major part (separated by ';,')
		else:
			# parse into name and date parts
			date_parts = []
			name_parts = []
			name_count = 0
			for part in parts_list:
				if part.strip() == u'':
					continue
				# any digits ?
				if regex.search(u"\d+", part, flags = regex.LOCALE | regex.UNICODE):
					# FIXME: parse out whitespace *not* adjacent to a *word*
					date_parts.append(part)
				else:
					tmp = part.strip()
					tmp = regex.split(u"\s*|\t*", tmp)
					name_count = name_count + len(tmp)
					name_parts.append(tmp)

			where_parts = []
			# first, handle name parts
			# special case: "<date(s)>, <name> <name>, <date(s)>"
			if (len(name_parts) == 1) and (name_count == 2):
				# usually "first last"
				where_parts.append ({
					'conditions': u"firstnames ~ %s and lastnames ~ %s",
					'args': [_('names: first last'), '^' + gmTools.capitalize(name_parts[0][0], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0][1], mode=gmTools.CAPS_NAMES)]
				})
				where_parts.append ({
					'conditions': u"lower(firstnames) ~* lower(%s) and lower(lastnames) ~* lower(%s)",
					'args': [_('names: first last'), '^' + name_parts[0][0], '^' + name_parts[0][1]]
				})
				# but sometimes "last first""
				where_parts.append ({
					'conditions': u"firstnames ~ %s and lastnames ~ %s",
					'args': [_('names: last, first'), '^' + gmTools.capitalize(name_parts[0][1], mode=gmTools.CAPS_NAMES), '^' + gmTools.capitalize(name_parts[0][0], mode=gmTools.CAPS_NAMES)]
				})
				where_parts.append ({
					'conditions': u"lower(firstnames) ~* lower(%s) and lower(lastnames) ~* lower(%s)",
					'args': [_('names: last, first'), '^' + name_parts[0][1], '^' + name_parts[0][0]]
				})
				# or even substrings anywhere in name
				where_parts.append ({
					'conditions': u"lower(firstnames || ' ' || lastnames) ~* lower(%s) OR lower(firstnames || ' ' || lastnames) ~* lower(%s)",
					'args': [_('name'), name_parts[0][0], name_parts[0][1]]
				})

			# special case: "<date(s)>, <name(s)>, <name(s)>, <date(s)>"
			elif len(name_parts) == 2:
				# usually "last, first"
				where_parts.append ({
					'conditions': u"firstnames ~ %s AND lastnames ~ %s",
					'args': [_('name: last, first'), '^' + ' '.join(map(gmTools.capitalize, name_parts[1])), '^' + ' '.join(map(gmTools.capitalize, name_parts[0]))]
				})
				where_parts.append ({
					'conditions': u"lower(firstnames) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
					'args': [_('name: last, first'), '^' + ' '.join(name_parts[1]), '^' + ' '.join(name_parts[0])]
				})
				# but sometimes "first, last"
				where_parts.append ({
					'conditions': u"firstnames ~ %s AND lastnames ~ %s",
					'args': [_('name: last, first'), '^' + ' '.join(map(gmTools.capitalize, name_parts[0])), '^' + ' '.join(map(gmTools.capitalize, name_parts[1]))]
				})
				where_parts.append ({
					'conditions': u"lower(firstnames) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
					'args': [_('name: last, first'), '^' + ' '.join(name_parts[0]), '^' + ' '.join(name_parts[1])]
				})
				# and sometimes "last, nick"
				where_parts.append ({
					'conditions': u"preferred ~ %s AND lastnames ~ %s",
					'args': [_('name: last, first'), '^' + ' '.join(map(gmTools.capitalize, name_parts[1])), '^' + ' '.join(map(gmTools.capitalize, name_parts[0]))]
				})
				where_parts.append ({
					'conditions': u"lower(preferred) ~* lower(%s) AND lower(lastnames) ~* lower(%s)",
					'args': [_('name: last, first'), '^' + ' '.join(name_parts[1]), '^' + ' '.join(name_parts[0])]
				})

				# or even substrings anywhere in name
				where_parts.append ({
					'conditions': u"lower(firstnames || ' ' || lastnames) ~* lower(%s) AND lower(firstnames || ' ' || lastnames) ~* lower(%s)",
					'args': [_('name'), ' '.join(name_parts[0]), ' '.join(name_parts[1])]
				})

			# big trouble - arbitrary number of names
			else:
				# FIXME: deep magic, not sure of rationale ...
				if len(name_parts) == 1:
					for part in name_parts[0]:
						where_parts.append ({
							'conditions': u"lower(firstnames || ' ' || lastnames) ~* lower(%s)",
							'args': [_('name'), part]
						})
				else:
					tmp = []
					for part in name_parts:
						tmp.append(' '.join(part))
					for part in tmp:
						where_parts.append ({
							'conditions': u"lower(firstnames || ' ' || lastnames) ~* lower(%s)",
							'args': [_('name'), part]
						})

			# secondly handle date parts
			# FIXME: this needs a considerable smart-up !
			if len(date_parts) == 1:
				if len(where_parts) == 0:
					where_parts.append ({
						'conditions': u"dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)",
						'args': [_('date of birth'), date_parts[0].replace(u',', u'.')]
					})
				if len(where_parts) > 0:
					where_parts[0]['conditions'] += u" AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)"
					where_parts[0]['args'].append(date_parts[0].replace(u',', u'.'))
					where_parts[0]['args'][0] += u', ' + _('date of birth')
				if len(where_parts) > 1:
					where_parts[1]['conditions'] += u" AND dem.date_trunc_utc('day'::text, dob) = dem.date_trunc_utc('day'::text, %s::timestamp with time zone)"
					where_parts[1]['args'].append(date_parts[0].replace(u',', u'.'))
					where_parts[1]['args'][0] += u', ' + _('date of birth')
			elif len(date_parts) > 1:
				if len(where_parts) == 0:
					where_parts.append ({
						'conditions': u"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)",
						'args': [_('date of birth/death'), date_parts[0].replace(u',', u'.'), date_parts[1].replace(u',', u'.')]
					})
				if len(where_parts) > 0:
					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)",
					where_parts[0]['args'].append(date_parts[0].replace(u',', u'.'), date_parts[1].replace(u',', u'.'))
					where_parts[0]['args'][0] += u', ' + _('date of birth/death')
				if len(where_parts) > 1:
					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)",
					where_parts[1]['args'].append(date_parts[0].replace(u',', u'.'), date_parts[1].replace(u',', u'.'))
					where_parts[1]['args'][0] += u', ' + _('date of birth/death')

			# and finally generate the queries ...
			for where_part in where_parts:
				queries.append ({
					'cmd': u"SELECT *, %%s::text AS match_type FROM dem.v_basic_person WHERE %s" % where_part['conditions'],
					'args': where_part['args']
				})
			return queries

		return []