示例#1
0
def export_patients_as_xdt(base_path=None):

	path = gmTools.get_unique_filename (
		prefix = u'gm-export-',
		suffix = u'',
		tmp_dir = base_path
	)
	path = os.path.splitext(path)[0]
	gmTools.mkdir(path)

	for ID in gmPerson.get_person_IDs():
		_log.info(u'exporting patient #%s', ID)
		identity = gmPerson.cPerson(aPK_obj = ID)
		_log.info(u'identity: %s', identity)
		filename = gmTools.get_unique_filename (
			prefix = u'gm_exp-%s-' % identity.dirname,
			suffix = u'.xdt',
			tmp_dir = path
		)
		_log.info(u'file: %s', filename)
		identity.export_as_gdt (
			filename = filename,
			encoding = u'utf8'
			#encoding = u'iso-8859-15'
		)
示例#2
0
def download_data_pack_old(url, target_dir=None):

    if target_dir is None:
        target_dir = gmTools.get_unique_filename(prefix='gm-dl-')

    _log.debug('downloading [%s]', url)
    _log.debug('unpacking into [%s]', target_dir)

    gmTools.mkdir(directory=target_dir)

    # FIXME: rewrite to use urllib.urlretrieve() and

    paths = gmTools.gmPaths()
    local_script = os.path.join(paths.local_base_dir, '..', 'external-tools',
                                'gm-download_data')

    candidates = [
        u'gm-download_data', u'gm-download_data.bat', local_script,
        u'gm-download_data.bat'
    ]
    args = u' %s %s' % (url, target_dir)

    success = gmShellAPI.run_first_available_in_shell(binaries=candidates,
                                                      args=args,
                                                      blocking=True,
                                                      run_last_one_anyway=True)

    if success:
        return True, target_dir

    _log.error('download failed')
    return False, None
示例#3
0
def download_data_pack_old(url, target_dir=None):

	if target_dir is None:
		target_dir = gmTools.get_unique_filename(prefix = 'gm-dl-')

	_log.debug('downloading [%s]', url)
	_log.debug('unpacking into [%s]', target_dir)

	gmTools.mkdir(directory = target_dir)

	# FIXME: rewrite to use urllib.urlretrieve() and 

	paths = gmTools.gmPaths()
	local_script = os.path.join(paths.local_base_dir, '..', 'external-tools', 'gm-download_data')

	candidates = [u'gm-download_data', u'gm-download_data.bat', local_script, u'gm-download_data.bat']
	args = u' %s %s' % (url, target_dir)

	success = gmShellAPI.run_first_available_in_shell (
		binaries = candidates,
		args = args,
		blocking = True,
		run_last_one_anyway = True
	)

	if success:
		return True, target_dir

	_log.error('download failed')
	return False, None
示例#4
0
def setup_hook_dir():
	_log.debug('known hooks:')
	for hook in known_hooks:
		_log.debug(hook)

	hook_dir = os.path.expanduser(os.path.join('~', '.gnumed', 'scripts'))
	gmTools.mkdir(hook_dir)
	gmTools.create_directory_description_file(directory = hook_dir, readme = README_hook_dir)
示例#5
0
def setup_hook_dir():
    _log.debug('known hooks:')
    for hook in known_hooks:
        _log.debug(hook)
    gmTools.mkdir(HOOK_SCRIPT_DIR)
    gmTools.create_directory_description_file(directory=HOOK_SCRIPT_DIR,
                                              readme=README_hook_dir)
    # create hook script example/template
    example_name = os.path.join(HOOK_SCRIPT_DIR, HOOK_SCRIPT_NAME + '.example')
    example = open(example_name, mode='wt', encoding='utf8')
    example.write(HOOK_SCRIPT_EXAMPLE)
    example.close()
    os.chmod(example_name, 384)
示例#6
0
def unzip_data_pack(filename=None):

    unzip_dir = os.path.splitext(filename)[0]
    _log.debug('unzipping data pack into [%s]', unzip_dir)
    gmTools.mkdir(unzip_dir)
    try:
        data_pack = zipfile.ZipFile(filename, 'r')
    except (zipfile.BadZipfile):
        _log.exception('cannot unzip data pack [%s]', filename)
        gmLog2.log_stack_trace()
        return None

    data_pack.extractall(unzip_dir)

    return unzip_dir
示例#7
0
def unzip_data_pack(filename=None):

	unzip_dir = os.path.splitext(filename)[0]
	_log.debug('unzipping data pack into [%s]', unzip_dir)
	gmTools.mkdir(unzip_dir)
	try:
		data_pack = zipfile.ZipFile(filename, 'r')
	except (zipfile.BadZipfile):
		_log.exception('cannot unzip data pack [%s]', filename)
		gmLog2.log_stack_trace()
		return None

	data_pack.extractall(unzip_dir)

	return unzip_dir
示例#8
0
文件: gmHooks.py 项目: ncqgm/gnumed
def setup_hook_dir():
	_old_path = os.path.join(gmTools.gmPaths().home_dir, '.gnumed', 'scripts')
	if os.path.isdir(_old_path):
		print('obsolete: [%s], use [%s]' %(_old_path, HOOK_SCRIPT_DIR))
		_log.debug('obsolete: %s', _old_path)
	_log.debug('known hooks:')
	for hook in known_hooks:
		_log.debug(hook)
	gmTools.mkdir(HOOK_SCRIPT_DIR)
	gmTools.create_directory_description_file(directory = HOOK_SCRIPT_DIR, readme = README_hook_dir)
	# create hook script example/template
	example_name = os.path.join(HOOK_SCRIPT_DIR, HOOK_SCRIPT_NAME + '.example')
	example = open(example_name, mode = 'wt', encoding = 'utf8')
	example.write(HOOK_SCRIPT_EXAMPLE)
	example.close()
	os.chmod(example_name, 384)
示例#9
0
    def dump_items_to_disk(self, base_dir=None, items=None):
        if items is None:
            items = self.items

        if len(items) == 0:
            return None

        if base_dir is None:
            from Gnumed.business.gmPerson import cPatient
            pat = cPatient(aPK_obj=self.__pk_identity)
            base_dir = gmTools.mk_sandbox_dir(prefix=u'exp-%s-' % pat.dirname)
        _log.debug('dumping export items to: %s', base_dir)

        gmTools.mkdir(base_dir)
        for item in items:
            item.save_to_file(directory=base_dir)
        return base_dir
示例#10
0
def extract_HL7_from_XML_CDATA(filename, xml_path, target_dir=None):

	_log.debug('extracting HL7 from CDATA of <%s> nodes in XML file [%s]', xml_path, filename)

	# sanity checks/setup
	try:
		open(filename).close()
		orig_dir = os.path.split(filename)[0]
		work_filename = gmTools.get_unique_filename(prefix = 'gm-x2h-%s-' % gmTools.fname_stem(filename), suffix = '.hl7')
		if target_dir is None:
			target_dir = os.path.join(orig_dir, 'HL7')
			done_dir = os.path.join(orig_dir, 'done')
		else:
			done_dir = os.path.join(target_dir, 'done')
		_log.debug('target dir: %s', target_dir)
		gmTools.mkdir(target_dir)
		gmTools.mkdir(done_dir)
	except Exception:
		_log.exception('cannot setup unwrapping environment')
		return None

	hl7_xml = pyxml.ElementTree()
	try:
		hl7_xml.parse(filename)
	except pyxml.ParseError:
		_log.exception('cannot parse [%s]' % filename)
		return None
	nodes = hl7_xml.findall(xml_path)
	if len(nodes) == 0:
		_log.debug('no nodes found for data extraction')
		return None

	_log.debug('unwrapping HL7 from XML into [%s]', work_filename)
	hl7_file = io.open(work_filename, mode = 'wt', encoding = 'utf8', newline = '')		# universal newlines acceptance but no translation on output
	for node in nodes:
#		hl7_file.write(node.text.rstrip() + HL7_EOL)
		hl7_file.write(node.text + '')		# trick to make node.text unicode
	hl7_file.close()

	target_fname = os.path.join(target_dir, os.path.split(work_filename)[1])
	shutil.copy(work_filename, target_dir)
	shutil.move(filename, done_dir)

	return target_fname
示例#11
0
文件: gmHL7.py 项目: ncqgm/gnumed
def extract_HL7_from_XML_CDATA(filename, xml_path, target_dir=None):

	_log.debug('extracting HL7 from CDATA of <%s> nodes in XML file [%s]', xml_path, filename)

	# sanity checks/setup
	try:
		open(filename).close()
		orig_dir = os.path.split(filename)[0]
		work_filename = gmTools.get_unique_filename(prefix = 'gm-x2h-%s-' % gmTools.fname_stem(filename), suffix = '.hl7')
		if target_dir is None:
			target_dir = os.path.join(orig_dir, 'HL7')
			done_dir = os.path.join(orig_dir, 'done')
		else:
			done_dir = os.path.join(target_dir, 'done')
		_log.debug('target dir: %s', target_dir)
		gmTools.mkdir(target_dir)
		gmTools.mkdir(done_dir)
	except Exception:
		_log.exception('cannot setup unwrapping environment')
		return None

	hl7_xml = pyxml.ElementTree()
	try:
		hl7_xml.parse(filename)
	except pyxml.ParseError:
		_log.exception('cannot parse [%s]' % filename)
		return None
	nodes = hl7_xml.findall(xml_path)
	if len(nodes) == 0:
		_log.debug('no nodes found for data extraction')
		return None

	_log.debug('unwrapping HL7 from XML into [%s]', work_filename)
	hl7_file = io.open(work_filename, mode = 'wt', encoding = 'utf8', newline = '')		# universal newlines acceptance but no translation on output
	for node in nodes:
#		hl7_file.write(node.text.rstrip() + HL7_EOL)
		hl7_file.write(node.text + '')		# trick to make node.text unicode
	hl7_file.close()

	target_fname = os.path.join(target_dir, os.path.split(work_filename)[1])
	shutil.copy(work_filename, target_dir)
	shutil.move(filename, done_dir)

	return target_fname
示例#12
0
def export_patients_as_xdt(base_path=None):

    path = gmTools.get_unique_filename(prefix='gm-export-',
                                       suffix='',
                                       tmp_dir=base_path)
    path = os.path.splitext(path)[0]
    gmTools.mkdir(path)

    for ID in gmPerson.get_person_IDs():
        _log.info('exporting patient #%s', ID)
        identity = gmPerson.cPerson(aPK_obj=ID)
        _log.info('identity: %s', identity)
        filename = gmTools.get_unique_filename(prefix='gm_exp-%s-' %
                                               identity.subdir_name,
                                               suffix='.xdt',
                                               tmp_dir=path)
        _log.info('file: %s', filename)
        identity.export_as_gdt(filename=filename,
                               encoding='utf8'
                               #encoding = u'iso-8859-15'
                               )
示例#13
0
文件: gmExportArea.py 项目: sk/gnumed
	def export(self, base_dir=None, items=None, with_metadata=True):

		if items is None:
			items_found = self.items

		if len(items) == 0:
			return None

		from Gnumed.business.gmPerson import cPatient
		pat = cPatient(aPK_obj = self.__pk_identity)
		if base_dir is None:
			base_dir = gmTools.mk_sandbox_dir(prefix = u'exp-%s-' % pat.dirname)

		_log.debug('base dir: %s', base_dir)

		doc_dir = os.path.join(base_dir, r'documents')
		gmTools.mkdir(doc_dir)

		mugshot = pat.document_folder.latest_mugshot
		if mugshot is None:
			mugshot_url = u'documents/no-such-file.png'
			mugshot_alt = _('no patient photograph available')
			mugshot_title = u''
		else:
			mugshot_url = mugshot.export_to_file(directory = doc_dir)
			mugshot_alt =_('patient photograph from %s') % gmDateTime.pydt_strftime(mugshot['date_generated'], '%B %Y')
			mugshot_title = gmDateTime.pydt_strftime(mugshot['date_generated'], '%B %Y')

		# index.html
		idx_fname = os.path.join(base_dir, u'index.html')
		idx_file = io.open(idx_fname, mode = u'wt', encoding = u'utf8')

		# header
		browse_dicomdir = u''
		existing_files = os.listdir(base_dir)
		if u'DICOMDIR' in existing_files:
			browse_dicomdir = u'	<li><a href="./DICOMDIR">browse DICOMDIR</a></li>'
		idx_file.write(_html_start % {
			u'html_title_header': _('Patient data for'),
			u'html_title_patient': gmTools.html_escape_string(pat['description_gender'] + u', ' + _(u'born') + u' ' + pat.get_formatted_dob('%Y %B %d')),
			u'title': _('Patient data export'),
			u'pat_name': gmTools.html_escape_string(pat['description_gender']),
			u'pat_dob': gmTools.html_escape_string(_(u'born') + u' ' + pat.get_formatted_dob('%Y %B %d')),
			u'mugshot_url': mugshot_url,
			u'mugshot_alt': mugshot_alt,
			u'mugshot_title': mugshot_title,
			u'docs_title': _(u'Documents'),
			u'browse_root': _(u'browse storage medium'),
			u'browse_docs': _(u'browse documents area'),
			u'browse_dicomdir': browse_dicomdir
		})
		# middle
		for item in items:
			item_path = item.export_to_file(directory = doc_dir)
			item_fname = os.path.split(item_path)[1]
			idx_file.write(_html_list_item % (
				item_fname,
				gmTools.html_escape_string(item['description'])
			))
		# footer
		_cfg = gmCfg2.gmCfgData()
		idx_file.write(_html_end % (
			gmTools.html_escape_string(gmDateTime.pydt_strftime(format = '%Y %B %d', encoding = u'utf8')),
			gmTools.html_escape_string(_cfg.get(option = u'client_version'))
		))
		idx_file.close()

		# autorun.inf
		autorun_fname = os.path.join(base_dir, u'autorun.inf')
		autorun_file = io.open(autorun_fname, mode = u'wt', encoding = u'utf8')
		autorun_file.write(_autorun_inf % (
			(pat['description_gender'] + u', ' + _(u'born') + u' ' + pat.get_formatted_dob('%Y %B %d')).strip(),
			_('Browse patient data')
		))
		autorun_file.close()

		# cd.inf
		cd_inf_fname = os.path.join(base_dir, u'cd.inf')
		cd_inf_file = io.open(cd_inf_fname, mode = u'wt', encoding = u'utf8')
		cd_inf_file.write(_cd_inf % (
			pat['lastnames'],
			pat['firstnames'],
			gmTools.coalesce(pat['gender'], u'?'),
			pat.get_formatted_dob('%Y-%m-%d'),
			gmDateTime.pydt_strftime(format = '%Y-%m-%d', encoding = u'utf8'),
			pat.ID,
			_cfg.get(option = u'client_version'),
			u' / '.join([ u'%s = %s (%s)' % (g['tag'], g['label'], g['l10n_label']) for g in pat.gender_list ])
		))
		cd_inf_file.close()

		# README
		readme_fname = os.path.join(base_dir, u'README')
		readme_file = io.open(readme_fname, mode = u'wt', encoding = u'utf8')
		readme_file.write(_README % (
			pat['description_gender'] + u', ' + _(u'born') + u' ' + pat.get_formatted_dob('%Y %B %d')
		))
		readme_file.close()

		# patient demographics as GDT/XML/VCF
		pat.export_as_gdt(filename = os.path.join(base_dir, u'patient.gdt'))
		pat.export_as_xml_linuxmednews(filename = os.path.join(base_dir, u'patient.xml'))
		pat.export_as_vcard(filename = os.path.join(base_dir, u'patient.vcf'))

		return base_dir
示例#14
0
def create_encrypted_zip_archive_from_dir(source_dir: str,
                                          comment: str = None,
                                          overwrite: bool = True,
                                          passphrase: str = None,
                                          verbose: bool = False) -> bool:
    """Create encrypted archive of a directory.

	The encrypted archive file will always be named
	"datawrapper.zip" for confidentiality reasons. If callers
	want another name they will have to shutil.move() the zip
	file themselves. This archive will be compressed and
	AES256 encrypted with the given passphrase. Therefore,
	the result will not decrypt with earlier versions of
	unzip software. On Windows, 7z oder WinZip are needed.

	The zip format does not support header encryption thereby
	allowing attackers to gain knowledge of patient details
	by observing the names of files and directories inside
	the encrypted archive. To reduce that attack surface,
	GNUmed will create _another_ zip archive inside
	"datawrapper.zip", which eventually wraps up the patient
	data as "data.zip". That archive is not compressed and
	not encrypted, and can thus be unpacked with any old
	unzipper.

	Note that GNUmed does NOT remember the passphrase for
	you. You will have to take care of that yourself, and
	possibly also safely hand over the passphrase to any
	receivers of the zip archive.

	Args:
		source_dir: the directory to archive and encrypt
		comment: included as a file containing the comment
		overwrite: remove preexisting archive before creation, avoiding *updating* of same, and thereby including unintended data
		passphrase: minimum length of 5 if given

	Returns:
		True / False / None (other error)
	"""
    assert (source_dir is not None), '<source_dir> must not be <None>'
    if len(passphrase) < 5:
        _log.error('<passphrase> must be at least 5 characters/signs/digits')
        return None

    gmLog2.add_word2hide(passphrase)
    source_dir = os.path.abspath(source_dir)
    if not os.path.isdir(source_dir):
        _log.error('<source_dir> does not exist or is not a directory: %s',
                   source_dir)
        return False

    for cmd in ['7z', '7z.exe']:
        found, binary = gmShellAPI.detect_external_binary(binary=cmd)
        if found:
            break
    if not found:
        _log.warning('no 7z binary found')
        return None

    sandbox_dir = gmTools.mk_sandbox_dir()
    archive_path_inner = os.path.join(sandbox_dir, 'data')
    if not gmTools.mkdir(archive_path_inner):
        _log.error('cannot create scratch space for inner achive: %s',
                   archive_path_inner)
    archive_fname_inner = 'data.zip'
    archive_name_inner = os.path.join(archive_path_inner, archive_fname_inner)
    archive_path_outer = gmTools.gmPaths().tmp_dir
    archive_fname_outer = 'datawrapper.zip'
    archive_name_outer = os.path.join(archive_path_outer, archive_fname_outer)
    # remove existing archives so they don't get *updated* rather than newly created
    if overwrite:
        if not gmTools.remove_file(archive_name_inner, force=True):
            _log.error('cannot remove existing archive [%s]',
                       archive_name_inner)
            return False

        if not gmTools.remove_file(archive_name_outer, force=True):
            _log.error('cannot remove existing archive [%s]',
                       archive_name_outer)
            return False

    # 7z does not support ZIP comments so create a text file holding the comment
    if comment is not None:
        tmp, fname = os.path.split(source_dir.rstrip(os.sep))
        comment_filename = os.path.join(sandbox_dir,
                                        '000-%s-comment.txt' % fname)
        with open(comment_filename,
                  mode='wt',
                  encoding='utf8',
                  errors='replace') as comment_file:
            comment_file.write(comment)

    # create inner (data) archive: uncompressed, unencrypted, similar to a tar archive
    args = [
        binary,
        'a',  # create archive
        '-sas',  # be smart about archive name extension
        '-bd',  # no progress indicator
        '-mx0',  # no compression (only store files)
        '-mcu=on',  # UTF8 filenames
        '-l',  # store content of links, not links
        '-scsUTF-8',  # console charset
        '-tzip'  # force ZIP format
    ]
    if verbose:
        args.append('-bb3')
        args.append('-bt')
    else:
        args.append('-bb1')
    args.append(archive_name_inner)
    args.append(source_dir)
    if comment is not None:
        args.append(comment_filename)
    success, exit_code, stdout = gmShellAPI.run_process(cmd_line=args,
                                                        encoding='utf8',
                                                        verbose=verbose)
    if not success:
        _log.error('cannot create inner archive')
        return None

    # create "decompress instructions" file
    instructions_filename = os.path.join(
        archive_path_inner, '000-on_Windows-open_with-WinZip_or_7z_tools')
    open(instructions_filename, mode='wt').close()

    # create outer (wrapper) archive: compressed, encrypted
    args = [
        binary,
        'a',  # create archive
        '-sas',  # be smart about archive name extension
        '-bd',  # no progress indicator
        '-mx9',  # best available zip compression ratio
        '-mcu=on',  # UTF8 filenames
        '-l',  # store content of links, not links
        '-scsUTF-8',  # console charset
        '-tzip',  # force ZIP format
        '-mem=AES256',  # force useful encryption
        '-p%s' % passphrase  # set passphrase
    ]
    if verbose:
        args.append('-bb3')
        args.append('-bt')
    else:
        args.append('-bb1')
    args.append(archive_name_outer)
    args.append(archive_path_inner)
    success, exit_code, stdout = gmShellAPI.run_process(cmd_line=args,
                                                        encoding='utf8',
                                                        verbose=verbose)
    if success:
        return archive_name_outer

    _log.error('cannot create outer archive')
    return None
示例#15
0
def print_generic_document(parent=None, jobtype:str=None, episode=None):
	"""Call LibreOffice Writer with a generated (fake) ODT file.

	Once Writer is closed, the ODT is imported if different from its initial content.

	Note:
		The file passed to LO _must_ reside in a directory the parents of
		which are all R-X by the user or else LO will throw up its arms
		in despair and fall back to the user's home dir upon saving.

		So,

			/tmp/user/$UID/some-file.odt

		will *not* work (because .../user/... is "drwx--x--x root.root") while

			/home/$USER/some/dir/some-file.odt

		does.
	"""
	sandbox = os.path.join(gmTools.gmPaths().user_tmp_dir, 'libreoffice')
	gmTools.mkdir(sandbox)
	fpath = gmTools.get_unique_filename(suffix = '.txt', tmp_dir = sandbox)
	doc_file = open(fpath, mode = 'wt')
	doc_file.write(__ODT_FILE_PREAMBLE)
	doc_file.write(_('Today: %s') % gmDateTime.pydt_now_here().strftime('%c'))
	doc_file.write('\n\n')
	prax = gmPraxis.gmCurrentPraxisBranch()
	doc_file.write('Praxis:\n')
	doc_file.write(prax.format())
	doc_file.write('\n')
	doc_file.write('Praxis branch:\n')
	doc_file.write('\n'.join(prax.org_unit.format (
		with_address = True,
		with_org = True,
		with_comms = True
	)))
	doc_file.write('\n\n')
	pat = gmPerson.gmCurrentPatient(gmPerson.cPatient(12))
	if pat.connected:
		doc_file.write('Patient:\n')
		doc_file.write(pat.get_description_gender())
		doc_file.write('\n\n')
		for adr in pat.get_addresses():
			doc_file.write(adr.format(single_line = False, verbose = True, show_type = True))
		doc_file.write('\n\n')
		for chan in pat.get_comm_channels():
			doc_file.werite(chan.format())
		doc_file.write('\n\n')
	doc_file.write('Provider:\n')
	doc_file.write('\n'.join(gmStaff.gmCurrentProvider().get_staff().format()))
	doc_file.write('\n\n-----------------------------------------------------------------------------\n\n')
	doc_file.close()

	# convert txt -> odt
	success, ret_code, stdout = gmShellAPI.run_process (
		cmd_line = [
			'lowriter',
			'--convert-to', 'odt',
			'--outdir', os.path.split(fpath)[0],
			fpath
		],
		verbose = True
	)
	if success:
		fpath = gmTools.fname_stem_with_path(fpath) + '.odt'
	else:
		_log.warning('cannot convert .txt to .odt')
	md5_before = gmTools.file2md5(fpath)
	gmShellAPI.run_process(cmd_line = ['lowriter', fpath], verbose = True)
	md5_after = gmTools.file2md5(fpath)
	if md5_before == md5_after:
		gmDispatcher.send(signal = 'statustext', msg = _('Document not modified. Discarding.'), beep = False)
		return

	if not pat.connected:
		shutil.move(fpath, gmTools.gmPaths().user_work_dir)
		gmDispatcher.send(signal = 'statustext', msg = _('No patient. Moved file into %s') % gmTools.gmPaths().user_work_dir, beep = False)
		return

	pat.export_area.add_file (
		filename = fpath,
		hint = _('Generic letter, written at %s') % gmDateTime.pydt_now_here().strftime('%Y %b %d  %H:%M')
	)
	gmDispatcher.send(signal = 'display_widget', name = 'gmExportAreaPlugin')
示例#16
0
文件: gmHL7.py 项目: sk/gnumed
def stage_single_PID_hl7_file(filename, source=None, encoding='utf8'):
	"""Multi-step processing of HL7 files.

	- input must be single-MSH / single-PID / normalized HL7

	- imports into clin.incoming_data_unmatched

	- needs write permissions in dir_of(filename)
	- moves PID files which were successfully staged into dir_of(filename)/done/PID/
	"""
	local_log_name = gmTools.get_unique_filename (
		prefix = gmTools.fname_stem(filename) + '-',
		suffix = '.stage.log'
	)
	local_logger = logging.FileHandler(local_log_name)
	local_logger.setLevel(logging.DEBUG)
	root_logger = logging.getLogger('')
	root_logger.addHandler(local_logger)
	_log.info(u'staging [%s] as unmatched incoming HL7%s', filename, gmTools.coalesce(source, u'', u' (%s)'))
	_log.debug(u'log file: %s', local_log_name)

	# sanity checks/setup
	try:
		open(filename).close()
		orig_dir = os.path.split(filename)[0]
		done_dir = os.path.join(orig_dir, u'done')
		gmTools.mkdir(done_dir)
		error_dir = os.path.join(orig_dir, u'failed')
		gmTools.mkdir(error_dir)
	except Exception:
		_log.exception('cannot setup staging environment')
		root_logger.removeHandler(local_logger)
		return False

	# stage
	try:
		inc = create_incoming_data(u'HL7%s' % gmTools.coalesce(source, u'', u' (%s)'), filename)
		if inc is None:
			_log.error(u'cannot stage PID file: %s', filename)
			root_logger.removeHandler(local_logger)
			shutil.move(filename, error_dir)
			shutil.move(local_log_name, error_dir)
			return False
		inc.update_data_from_file(fname = filename)
	except Exception:
		_log.exception(u'error staging PID file')
		root_logger.removeHandler(local_logger)
		shutil.move(filename, error_dir)
		shutil.move(local_log_name, error_dir)
		return False

	# set additional data
	MSH_file = io.open(filename, mode = 'rt', encoding = 'utf8')
	raw_hl7 = MSH_file.read(1024 * 1024 * 5)	# 5 MB max
	MSH_file.close()
	shutil.move(filename, done_dir)
	inc['comment'] = format_hl7_message (
		message = raw_hl7,
		skip_empty_fields = True,
		eol = u'\n'
	)
	HL7 = pyhl7.parse(raw_hl7)
	del raw_hl7
	inc['comment'] += u'\n'
	inc['comment'] += (u'-' * 80)
	inc['comment'] += u'\n\n'
	log = io.open(local_log_name, mode = 'rt', encoding = 'utf8')
	inc['comment'] += log.read()
	log.close()
	try:
		inc['lastnames'] = HL7.extract_field('PID', segment_num = 1, field_num = PID_field__name, component_num = PID_component__lastname)
		inc['firstnames'] = HL7.extract_field('PID', segment_num = 1, field_num = PID_field__name, component_num = PID_component__firstname)
		val = HL7.extract_field('PID', segment_num = 1, field_num = PID_field__name, component_num = PID_component__middlename)
		if val is not None:
			inc['firstnames'] += u' '
			inc['firstnames'] += val
		val = HL7.extract_field('PID', segment_num = 1, field_num = PID_field__dob)
		if val is not None:
			tmp = time.strptime(val, '%Y%m%d')
			inc['dob'] = pyDT.datetime(tmp.tm_year, tmp.tm_mon, tmp.tm_mday, tzinfo = gmDateTime.gmCurrentLocalTimezone)
		val = HL7.extract_field('PID', segment_num = 1, field_num = PID_field__gender)
		if val is not None:
			inc['gender'] = val
		inc['external_data_id'] = filename
		#u'fk_patient_candidates',
		#	u'request_id',						# request ID as found in <data>
		#	u'postcode',
		#	u'other_info',						# other identifying info in .data
		#	u'requestor',						# Requestor of data (e.g. who ordered test results) if available in source data.
		#	u'fk_identity_disambiguated',
		#	u'comment',							# a free text comment on this row, eg. why is it here, error logs etc
		#	u'fk_provider_disambiguated'		# The provider the data is relevant to.
	except Exception:
		_log.exception(u'cannot add more data')
	inc.save()

	_log.info(u'successfully staged')
	root_logger.removeHandler(local_logger)
	shutil.move(local_log_name, done_dir)
	return True
示例#17
0
文件: gmHL7.py 项目: sk/gnumed
def split_hl7_file(filename, target_dir=None, encoding='utf8'):
	"""Multi-step processing of HL7 files.

	- input can be multi-MSH / multi-PID / partially malformed HL7
	- tries to fix oddities
	- splits by MSH
	- splits by PID into <target_dir>

	- needs write permissions in dir_of(filename)
	- moves HL7 files which were successfully split up into dir_of(filename)/done/

	- returns (True|False, list_of_PID_files)
	"""
	local_log_name = gmTools.get_unique_filename (
		prefix = gmTools.fname_stem(filename) + '-',
		suffix = '.split.log'
	)
	local_logger = logging.FileHandler(local_log_name)
	local_logger.setLevel(logging.DEBUG)
	root_logger = logging.getLogger('')
	root_logger.addHandler(local_logger)
	_log.info('splitting HL7 file: %s', filename)
	_log.debug('log file: %s', local_log_name)

	# sanity checks/setup
	try:
		open(filename).close()
		orig_dir = os.path.split(filename)[0]
		done_dir = os.path.join(orig_dir, u'done')
		gmTools.mkdir(done_dir)
		error_dir = os.path.join(orig_dir, u'failed')
		gmTools.mkdir(error_dir)
		work_filename = gmTools.get_unique_filename(prefix = gmTools.fname_stem(filename) + '-', suffix = '.hl7')
		if target_dir is None:
			target_dir = os.path.join(orig_dir, u'PID')
		_log.debug('target dir: %s', target_dir)
		gmTools.mkdir(target_dir)
	except Exception:
		_log.exception('cannot setup splitting environment')
		root_logger.removeHandler(local_logger)
		return False, None

	# split
	target_names = []
	try:
		shutil.copy(filename, work_filename)
		fixed_filename = __fix_malformed_hl7_file(work_filename, encoding = encoding)
		MSH_fnames = __split_hl7_file_by_MSH(fixed_filename, encoding)
		PID_fnames = []
		for MSH_fname in MSH_fnames:
			PID_fnames.extend(__split_MSH_by_PID(MSH_fname))
		for PID_fname in PID_fnames:
			shutil.move(PID_fname, target_dir)
			target_names.append(os.path.join(target_dir, os.path.split(PID_fname)[1]))
	except Exception:
		_log.exception('cannot split HL7 file')
		for target_name in target_names:
			try: os.remove(target_name)
			except: pass
		root_logger.removeHandler(local_logger)
		shutil.move(local_log_name, error_dir)
		return False, None

	_log.info('successfully split')
	root_logger.removeHandler(local_logger)
	try:
		shutil.move(filename, done_dir)
		shutil.move(local_log_name, done_dir)
	except shutil.Error:
		_log.exception('cannot move hl7 file or log file to holding area')
	return True, target_names
示例#18
0
def stage_single_PID_hl7_file(filename, source=None, encoding='utf8'):
	"""Multi-step processing of HL7 files.

	- input must be single-MSH / single-PID / normalized HL7

	- imports into clin.incoming_data_unmatched

	- needs write permissions in dir_of(filename)
	- moves PID files which were successfully staged into dir_of(filename)/done/PID/
	"""
	local_log_name = gmTools.get_unique_filename (
		prefix = gmTools.fname_stem(filename) + '-',
		suffix = '.stage.log'
	)
	local_logger = logging.FileHandler(local_log_name)
	local_logger.setLevel(logging.DEBUG)
	root_logger = logging.getLogger('')
	root_logger.addHandler(local_logger)
	_log.info('staging [%s] as unmatched incoming HL7%s', filename, gmTools.coalesce(source, '', ' (%s)'))
	_log.debug('log file: %s', local_log_name)

	# sanity checks/setup
	try:
		open(filename).close()
		orig_dir = os.path.split(filename)[0]
		done_dir = os.path.join(orig_dir, 'done')
		gmTools.mkdir(done_dir)
		error_dir = os.path.join(orig_dir, 'failed')
		gmTools.mkdir(error_dir)
	except Exception:
		_log.exception('cannot setup staging environment')
		root_logger.removeHandler(local_logger)
		return False

	# stage
	try:
		incoming = gmIncomingData.create_incoming_data('HL7%s' % gmTools.coalesce(source, '', ' (%s)'), filename)
		if incoming is None:
			_log.error('cannot stage PID file: %s', filename)
			root_logger.removeHandler(local_logger)
			shutil.move(filename, error_dir)
			shutil.move(local_log_name, error_dir)
			return False
		incoming.update_data_from_file(fname = filename)
	except Exception:
		_log.exception('error staging PID file')
		root_logger.removeHandler(local_logger)
		shutil.move(filename, error_dir)
		shutil.move(local_log_name, error_dir)
		return False

	# set additional data
	MSH_file = io.open(filename, mode = 'rt', encoding = 'utf8', newline = '')
	raw_hl7 = MSH_file.read(1024 * 1024 * 5)	# 5 MB max
	MSH_file.close()
	shutil.move(filename, done_dir)
	incoming['comment'] = format_hl7_message (
		message = raw_hl7,
		skip_empty_fields = True,
		eol = '\n'
	)
	HL7 = pyhl7.parse(raw_hl7)
	del raw_hl7
	incoming['comment'] += '\n'
	incoming['comment'] += ('-' * 80)
	incoming['comment'] += '\n\n'
	log = io.open(local_log_name, mode = 'rt', encoding = 'utf8')
	incoming['comment'] += log.read()
	log.close()
	try:
		incoming['lastnames'] = HL7.extract_field('PID', segment_num = 1, field_num = PID_field__name, component_num = PID_component__lastname)
		incoming['firstnames'] = HL7.extract_field('PID', segment_num = 1, field_num = PID_field__name, component_num = PID_component__firstname)
		val = HL7.extract_field('PID', segment_num = 1, field_num = PID_field__name, component_num = PID_component__middlename)
		if val is not None:
			incoming['firstnames'] += ' '
			incoming['firstnames'] += val
		val = HL7.extract_field('PID', segment_num = 1, field_num = PID_field__dob)
		if val is not None:
			tmp = time.strptime(val, '%Y%m%d')
			incoming['dob'] = pyDT.datetime(tmp.tm_year, tmp.tm_mon, tmp.tm_mday, tzinfo = gmDateTime.gmCurrentLocalTimezone)
		val = HL7.extract_field('PID', segment_num = 1, field_num = PID_field__gender)
		if val is not None:
			incoming['gender'] = val
		incoming['external_data_id'] = filename
		#u'fk_patient_candidates',
		#	u'request_id',						# request ID as found in <data>
		#	u'postcode',
		#	u'other_info',						# other identifying info in .data
		#	u'requestor',						# Requestor of data (e.g. who ordered test results) if available in source data.
		#	u'fk_identity_disambiguated',
		#	u'comment',							# a free text comment on this row, eg. why is it here, error logs etc
		#	u'fk_provider_disambiguated'		# The provider the data is relevant to.
	except Exception:
		_log.exception('cannot add more data')
	incoming.save()

	_log.info('successfully staged')
	root_logger.removeHandler(local_logger)
	shutil.move(local_log_name, done_dir)
	return True
示例#19
0
    'before_print_doc', 'before_fax_doc', 'before_mail_doc',
    'before_print_doc_part', 'before_fax_doc_part', 'before_mail_doc_part',
    'before_external_doc_access', 'db_maintenance_warning'
]

README_pat_dir = """Directory for data files containing the current patient.

Whenever the patient is changed GNUmed will export
formatted demographics into files in this directory
for use by 3rd party software.

Currently exported formats:

GDT, VCF (vcard), MCF (mecard), XML (linuxmednews)"""

HOOK_SCRIPT_EXAMPLE = """#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
#===========================================================================
#
# Example script to be run off GNUmed hooks.
#
# It can print a message to stdout whenever any hook is invoked.
#
# Copy this file to ~/.gnumed/scripts/hook_script.py and modify as needed.
#
# Known hooks:
#
#	%s
#
#===========================================================================
示例#20
0
    def export(self, base_dir=None, items=None, expand_compressed=False):

        if items is None:
            items = self.items

        if len(items) == 0:
            return None

        media_base_dir = base_dir

        from Gnumed.business.gmPerson import cPatient
        pat = cPatient(aPK_obj=self.__pk_identity)
        if media_base_dir is None:
            media_base_dir = gmTools.mk_sandbox_dir(prefix=u'exp-%s-' %
                                                    pat.dirname)
        _log.debug('patient media base dir: %s', media_base_dir)

        doc_dir = os.path.join(media_base_dir, r'documents')
        if os.path.isdir(doc_dir):
            index_existing_docs = True
        else:
            index_existing_docs = False
            gmTools.mkdir(doc_dir)

        _html_start_data = {
            u'html_title_header':
            _('Patient data for'),
            u'html_title_patient':
            gmTools.html_escape_string(
                pat.get_description_gender(with_nickname=False) + u', ' +
                _(u'born') + u' ' + pat.get_formatted_dob('%Y %B %d')),
            u'title':
            _('Patient data export'),
            u'pat_name':
            gmTools.html_escape_string(
                pat.get_description_gender(with_nickname=False)),
            u'pat_dob':
            gmTools.html_escape_string(
                _(u'born') + u' ' + pat.get_formatted_dob('%Y %B %d')),
            u'mugshot_url':
            u'documents/no-such-file.png',
            u'mugshot_alt':
            _('no patient photograph available'),
            u'mugshot_title':
            u'',
            u'docs_title':
            _(u'Documents'),
            u'browse_root':
            _(u'browse storage medium'),
            u'browse_docs':
            _(u'browse documents area'),
            u'browse_dicomdir':
            u'',
            u'run_dicom_viewer':
            u''
        }

        mugshot = pat.document_folder.latest_mugshot
        if mugshot is not None:
            _html_start_data['mugshot_url'] = mugshot.save_to_file(
                directory=doc_dir, adjust_extension=True)
            _html_start_data['mugshot_alt'] = _(
                'patient photograph from %s') % gmDateTime.pydt_strftime(
                    mugshot['date_generated'], '%B %Y')
            _html_start_data['mugshot_title'] = gmDateTime.pydt_strftime(
                mugshot['date_generated'], '%B %Y')

        if u'DICOMDIR' in os.listdir(media_base_dir):
            _html_start_data[
                u'browse_dicomdir'] = u'<li><a href="./DICOMDIR">%s</a></li>' % _(
                    u'show DICOMDIR file')
            # copy DWV into target dir
            dwv_target_dir = os.path.join(media_base_dir, u'dwv')
            gmTools.rmdir(dwv_target_dir)
            dwv_src_dir = os.path.join(gmTools.gmPaths().local_base_dir,
                                       u'dwv4export')
            if not os.path.isdir(dwv_src_dir):
                dwv_src_dir = os.path.join(
                    gmTools.gmPaths().system_app_data_dir, u'dwv4export')
            try:
                shutil.copytree(dwv_src_dir, dwv_target_dir)
                _html_start_data[
                    u'run_dicom_viewer'] = u'<li><a href="./dwv/viewers/mobile-local/index.html">%s</a></li>' % _(
                        u'run Radiology Images (DICOM) Viewer')
            except shutil.Error, OSError:
                _log.exception('cannot include DWV, skipping')
示例#21
0
    def export(self, base_dir=None, items=None, expand_compressed=False):

        if items is None:
            items = self.items

        if len(items) == 0:
            return None

        media_base_dir = base_dir

        from Gnumed.business.gmPerson import cPatient
        pat = cPatient(aPK_obj=self.__pk_identity)
        if media_base_dir is None:
            media_base_dir = gmTools.mk_sandbox_dir(prefix='exp-%s-' %
                                                    pat.subdir_name)
        _log.debug('patient media base dir: %s', media_base_dir)

        doc_dir = os.path.join(media_base_dir, r'documents')
        if os.path.isdir(doc_dir):
            index_existing_docs = True
        else:
            index_existing_docs = False
            gmTools.mkdir(doc_dir)

        _html_start_data = {
            'html_title_header':
            _('Patient data for'),
            'html_title_patient':
            gmTools.html_escape_string(
                pat.get_description_gender(with_nickname=False) + ', ' +
                _('born') + ' ' + pat.get_formatted_dob('%Y %B %d')),
            'title':
            _('Patient data export'),
            'pat_name':
            gmTools.html_escape_string(
                pat.get_description_gender(with_nickname=False)),
            'pat_dob':
            gmTools.html_escape_string(
                _('born') + ' ' + pat.get_formatted_dob('%Y %B %d')),
            'mugshot_url':
            'documents/no-such-file.png',
            'mugshot_alt':
            _('no patient photograph available'),
            'mugshot_title':
            '',
            'docs_title':
            _('Documents'),
            'browse_root':
            _('browse storage medium'),
            'browse_docs':
            _('browse documents area'),
            'browse_dicomdir':
            '',
            'run_dicom_viewer':
            ''
        }

        mugshot = pat.document_folder.latest_mugshot
        if mugshot is not None:
            _html_start_data['mugshot_url'] = mugshot.save_to_file(
                directory=doc_dir, adjust_extension=True)
            _html_start_data['mugshot_alt'] = _(
                'patient photograph from %s') % gmDateTime.pydt_strftime(
                    mugshot['date_generated'], '%B %Y')
            _html_start_data['mugshot_title'] = gmDateTime.pydt_strftime(
                mugshot['date_generated'], '%B %Y')

        if 'DICOMDIR' in os.listdir(media_base_dir):
            _html_start_data[
                'browse_dicomdir'] = '<li><a href="./DICOMDIR">%s</a></li>' % _(
                    'show DICOMDIR file')
            # copy DWV into target dir
            dwv_src_dir = os.path.join(gmTools.gmPaths().local_base_dir,
                                       'dwv4export')
            if not os.path.isdir(dwv_src_dir):
                dwv_src_dir = os.path.join(
                    gmTools.gmPaths().system_app_data_dir, 'dwv4export')
            if os.path.isdir(dwv_src_dir):
                dwv_target_dir = os.path.join(media_base_dir, 'dwv')
                gmTools.rmdir(dwv_target_dir)
                try:
                    shutil.copytree(dwv_src_dir, dwv_target_dir)
                    _html_start_data[
                        'run_dicom_viewer'] = '<li><a href="./dwv/viewers/mobile-local/index.html">%s</a></li>' % _(
                            'run Radiology Images (DICOM) Viewer')
                except (shutil.Error, OSError):
                    _log.exception('cannot include DWV, skipping')

        # index.html
        # - header
        idx_fname = os.path.join(media_base_dir, 'index.html')
        idx_file = io.open(idx_fname, mode='wt', encoding='utf8')
        idx_file.write(_html_start % _html_start_data)
        # - middle (side effect ! -> exports items into files ...)
        existing_docs = os.listdir(
            doc_dir
        )  # get them now, or else we will include the to-be-exported items
        # - export items
        for item in items:
            item_path = item.save_to_file(directory=doc_dir)
            item_fname = os.path.split(item_path)[1]
            idx_file.write(
                _html_list_item %
                (item_fname, gmTools.html_escape_string(item['description'])))
        # - preexisting documents
        for doc_fname in existing_docs:
            idx_file.write(
                _html_list_item %
                (doc_fname,
                 gmTools.html_escape_string(_('other: %s') % doc_fname)))
        # - footer
        _cfg = gmCfg2.gmCfgData()
        from Gnumed.business.gmPraxis import gmCurrentPraxisBranch
        prax = gmCurrentPraxisBranch()
        lines = []
        adr = prax.branch.org_unit.address
        if adr is not None:
            lines.extend(adr.format())
        for comm in prax.branch.org_unit.comm_channels:
            if comm['is_confidential'] is True:
                continue
            lines.append('%s: %s' % (comm['l10n_comm_type'], comm['url']))
        adr = ''
        if len(lines) > 0:
            adr = gmTools.html_escape_string('\n'.join(lines),
                                             replace_eol=True,
                                             keep_visual_eol=True)
        _html_end_data = {
            'branch':
            gmTools.html_escape_string(prax['branch']),
            'praxis':
            gmTools.html_escape_string(prax['praxis']),
            'date':
            gmTools.html_escape_string(
                gmDateTime.pydt_strftime(gmDateTime.pydt_now_here(),
                                         format='%Y %B %d')),
            'gm_ver':
            gmTools.html_escape_string(_cfg.get(option='client_version')),
            #'gm_ver': 'git HEAD',				# for testing
            'adr':
            adr
        }
        idx_file.write(_html_end % _html_end_data)
        idx_file.close()

        # start.html (just a copy of index.html, really ;-)
        start_fname = os.path.join(media_base_dir, 'start.html')
        try:
            shutil.copy2(idx_fname, start_fname)
        except Exception:
            _log.exception('cannot copy %s to %s', idx_fname, start_fname)

        # autorun.inf
        autorun_dict = {}
        autorun_dict['label'] = self._compute_autorun_inf_label(pat)
        autorun_dict['action'] = _('Browse patient data')
        autorun_dict['icon'] = ''
        media_icon_kwd = '$$gnumed_patient_media_export_icon'
        media_icon_kwd_exp = gmKeywordExpansion.get_expansion(
            keyword=media_icon_kwd, textual_only=False, binary_only=True)
        icon_tmp_file = media_icon_kwd_exp.save_to_file(
            target_mime='image/x-icon',
            target_extension='.ico',
            ignore_conversion_problems=True)
        if icon_tmp_file is None:
            _log.debug('cannot retrieve <%s>', media_icon_kwd)
        else:
            media_icon_fname = os.path.join(media_base_dir, 'gnumed.ico')
            try:
                shutil.move(icon_tmp_file, media_icon_fname)
                autorun_dict['icon'] = 'icon=gnumed.ico'
            except Exception:
                _log.exception('cannot move %s to %s', icon_tmp_file,
                               media_icon_fname)
        autorun_fname = os.path.join(media_base_dir, 'autorun.inf')
        autorun_file = io.open(autorun_fname,
                               mode='wt',
                               encoding='cp1252',
                               errors='replace')
        autorun_file.write(_autorun_inf % autorun_dict)
        autorun_file.close()

        # cd.inf
        cd_inf_fname = os.path.join(media_base_dir, 'cd.inf')
        cd_inf_file = io.open(cd_inf_fname, mode='wt', encoding='utf8')
        cd_inf_file.write(
            _cd_inf %
            (pat['lastnames'], pat['firstnames'],
             gmTools.coalesce(pat['gender'],
                              '?'), pat.get_formatted_dob('%Y-%m-%d'),
             gmDateTime.pydt_strftime(gmDateTime.pydt_now_here(),
                                      format='%Y-%m-%d'), pat.ID,
             _cfg.get(option='client_version'), ' / '.join([
                 '%s = %s (%s)' % (g['tag'], g['label'], g['l10n_label'])
                 for g in pat.gender_list
             ])))
        cd_inf_file.close()

        # README
        readme_fname = os.path.join(media_base_dir, 'README')
        readme_file = io.open(readme_fname, mode='wt', encoding='utf8')
        readme_file.write(
            _README % (pat.get_description_gender(with_nickname=False) + ', ' +
                       _('born') + ' ' + pat.get_formatted_dob('%Y %B %d')))
        readme_file.close()

        # patient demographics as GDT/XML/VCF
        pat.export_as_gdt(filename=os.path.join(media_base_dir, 'patient.gdt'))
        pat.export_as_xml_linuxmednews(
            filename=os.path.join(media_base_dir, 'patient.xml'))
        pat.export_as_vcard(
            filename=os.path.join(media_base_dir, 'patient.vcf'))

        # praxis VCF
        shutil.move(prax.vcf, os.path.join(media_base_dir, 'praxis.vcf'))

        return media_base_dir
示例#22
0
    def export(self,
               base_dir=None,
               items=None,
               with_metadata=True,
               expand_compressed=False):

        if items is None:
            items = self.items

        if len(items) == 0:
            return None

        from Gnumed.business.gmPerson import cPatient
        pat = cPatient(aPK_obj=self.__pk_identity)
        if base_dir is None:
            base_dir = gmTools.mk_sandbox_dir(prefix=u'exp-%s-' % pat.dirname)
        _log.debug('base dir: %s', base_dir)

        doc_dir = os.path.join(base_dir, r'documents')
        gmTools.mkdir(doc_dir)

        _html_start_data = {
            u'html_title_header':
            _('Patient data for'),
            u'html_title_patient':
            gmTools.html_escape_string(
                pat.get_description_gender(with_nickname=False) + u', ' +
                _(u'born') + u' ' + pat.get_formatted_dob('%Y %B %d')),
            u'title':
            _('Patient data export'),
            u'pat_name':
            gmTools.html_escape_string(
                pat.get_description_gender(with_nickname=False)),
            u'pat_dob':
            gmTools.html_escape_string(
                _(u'born') + u' ' + pat.get_formatted_dob('%Y %B %d')),
            u'mugshot_url':
            u'documents/no-such-file.png',
            u'mugshot_alt':
            _('no patient photograph available'),
            u'mugshot_title':
            u'',
            u'docs_title':
            _(u'Documents'),
            u'browse_root':
            _(u'browse storage medium'),
            u'browse_docs':
            _(u'browse documents area'),
            u'browse_dicomdir':
            u''
        }

        mugshot = pat.document_folder.latest_mugshot
        if mugshot is not None:
            _html_start_data['mugshot_url'] = mugshot.export_to_file(
                directory=doc_dir)
            _html_start_data['mugshot_alt'] = _(
                'patient photograph from %s') % gmDateTime.pydt_strftime(
                    mugshot['date_generated'], '%B %Y')
            _html_start_data['mugshot_title'] = gmDateTime.pydt_strftime(
                mugshot['date_generated'], '%B %Y')

        # index.html
        idx_fname = os.path.join(base_dir, u'index.html')
        idx_file = io.open(idx_fname, mode=u'wt', encoding=u'utf8')
        # header
        existing_files = os.listdir(base_dir)
        if u'DICOMDIR' in existing_files:
            _html_start_data[
                u'browse_dicomdir'] = u'	<li><a href="./DICOMDIR">browse DICOMDIR</a></li>'
        idx_file.write(_html_start % _html_start_data)
        # middle
        for item in items:
            item_path = item.export_to_file(directory=doc_dir)
            item_fname = os.path.split(item_path)[1]
            idx_file.write(
                _html_list_item %
                (item_fname, gmTools.html_escape_string(item['description'])))
        # footer
        _cfg = gmCfg2.gmCfgData()
        from Gnumed.business.gmPraxis import gmCurrentPraxisBranch
        prax = gmCurrentPraxisBranch()
        lines = []
        adr = prax.branch.org_unit.address
        if adr is not None:
            lines.extend(adr.format())
        for comm in prax.branch.org_unit.comm_channels:
            if comm['is_confidential'] is True:
                continue
            lines.append(u'%s: %s' % (comm['l10n_comm_type'], comm['url']))
        adr = u''
        if len(lines) > 0:
            adr = gmTools.html_escape_string(u'\n'.join(lines),
                                             replace_eol=True,
                                             keep_visual_eol=True)
        _html_end_data = {
            'branch':
            gmTools.html_escape_string(prax['branch']),
            'praxis':
            gmTools.html_escape_string(prax['praxis']),
            'date':
            gmTools.html_escape_string(
                gmDateTime.pydt_strftime(gmDateTime.pydt_now_here(),
                                         format='%Y %B %d',
                                         encoding=u'utf8')),
            'gm_ver':
            gmTools.html_escape_string(_cfg.get(option=u'client_version')),
            #'gm_ver': 'git HEAD',				# for testing
            'adr':
            adr
        }
        idx_file.write(_html_end % _html_end_data)
        idx_file.close()

        # autorun.inf
        autorun_fname = os.path.join(base_dir, u'autorun.inf')
        autorun_file = io.open(autorun_fname, mode=u'wt', encoding=u'utf8')
        autorun_file.write(
            _autorun_inf %
            ((pat.get_description_gender(with_nickname=False) + u', ' +
              _(u'born') + u' ' + pat.get_formatted_dob('%Y %B %d')).strip(),
             _('Browse patient data')))
        autorun_file.close()

        # cd.inf
        cd_inf_fname = os.path.join(base_dir, u'cd.inf')
        cd_inf_file = io.open(cd_inf_fname, mode=u'wt', encoding=u'utf8')
        cd_inf_file.write(
            _cd_inf %
            (pat['lastnames'], pat['firstnames'],
             gmTools.coalesce(pat['gender'],
                              u'?'), pat.get_formatted_dob('%Y-%m-%d'),
             gmDateTime.pydt_strftime(gmDateTime.pydt_now_here(),
                                      format='%Y-%m-%d',
                                      encoding=u'utf8'), pat.ID,
             _cfg.get(option=u'client_version'), u' / '.join([
                 u'%s = %s (%s)' % (g['tag'], g['label'], g['l10n_label'])
                 for g in pat.gender_list
             ])))
        cd_inf_file.close()

        # README
        readme_fname = os.path.join(base_dir, u'README')
        readme_file = io.open(readme_fname, mode=u'wt', encoding=u'utf8')
        readme_file.write(
            _README %
            (pat.get_description_gender(with_nickname=False) + u', ' +
             _(u'born') + u' ' + pat.get_formatted_dob('%Y %B %d')))
        readme_file.close()

        # patient demographics as GDT/XML/VCF
        pat.export_as_gdt(filename=os.path.join(base_dir, u'patient.gdt'))
        pat.export_as_xml_linuxmednews(
            filename=os.path.join(base_dir, u'patient.xml'))
        pat.export_as_vcard(filename=os.path.join(base_dir, u'patient.vcf'))

        # praxis VCF
        shutil.move(prax.vcf, os.path.join(base_dir, u'praxis.vcf'))

        return base_dir
示例#23
0
	def _on_save_items_button_pressed(self, event):
		event.Skip()

		items = self.__get_items_to_work_on(_('Saving entries'))
		if items is None:
			return

		pat = gmPerson.gmCurrentPatient()
		dlg = cExportAreaSaveAsDlg(self, -1, patient = pat, item_count = len(items))
		choice = dlg.ShowModal()
		path = dlg._LBL_directory.Label
		generate_metadata = dlg._CHBOX_generate_metadata.IsChecked()
		use_subdir = dlg._CHBOX_use_subdirectory.IsChecked()
		encrypt = dlg._CHBOX_encrypt.IsChecked()
		dlg.DestroyLater()
		if choice == wx.ID_CANCEL:
			return

		if choice == wx.ID_SAVE:
			create_archive = True
		elif choice == wx.ID_OK:
			create_archive = False
		else:
			raise Exception('invalid return')

		if create_archive:
			export_dir = path
			zip_file = self.__export_as_zip (
				gmTools.coalesce(encrypt, _('Saving entries as encrypted ZIP archive'), _('Saving entries as unencrypted ZIP archive')),
				items = items,
				encrypt = encrypt
			)
			if zip_file is None:
				gmDispatcher.send(signal = 'statustext', msg = _('Cannot save: aborted or error.'))
				return
			gmTools.mkdir(export_dir)
			final_zip_file = shutil.move(zip_file, export_dir)
			gmDispatcher.send(signal = 'statustext', msg = _('Saved entries into [%s]') % final_zip_file)
			target = final_zip_file
		else:
			export_dir = self.__export_as_files (
				gmTools.coalesce(encrypt, _('Saving entries as encrypted files'), _('Saving entries as unencrypted files')),
				base_dir = path,
				items = items,
				encrypt = encrypt,
				with_metadata = generate_metadata
			)
			if export_dir is None:
				gmDispatcher.send(signal = 'statustext', msg = _('Cannot save: aborted or error.'))
				return
			gmDispatcher.send(signal = 'statustext', msg = _('Saved entries into [%s]') % export_dir)
			target = export_dir

		self.__save_soap_note(soap = _('Saved from export area to [%s]:\n - %s') % (
			target,
			'\n - '.join([ i['description'] for i in items ])
		))
		self.__browse_patient_data(export_dir)

		# remove_entries ?

		return True
示例#24
0
def split_hl7_file(filename, target_dir=None, encoding='utf8'):
	"""Multi-step processing of HL7 files.

	- input can be multi-MSH / multi-PID / partially malformed HL7
	- tries to fix oddities
	- splits by MSH
	- splits by PID into <target_dir>

	- needs write permissions in dir_of(filename)
	- moves HL7 files which were successfully split up into dir_of(filename)/done/

	- returns (True|False, list_of_PID_files)
	"""
	local_log_name = gmTools.get_unique_filename (
		prefix = gmTools.fname_stem(filename) + '-',
		suffix = '.split.log'
	)
	local_logger = logging.FileHandler(local_log_name)
	local_logger.setLevel(logging.DEBUG)
	root_logger = logging.getLogger('')
	root_logger.addHandler(local_logger)
	_log.info('splitting HL7 file: %s', filename)
	_log.debug('log file: %s', local_log_name)

	# sanity checks/setup
	try:
		open(filename).close()
		orig_dir = os.path.split(filename)[0]
		done_dir = os.path.join(orig_dir, 'done')
		gmTools.mkdir(done_dir)
		error_dir = os.path.join(orig_dir, 'failed')
		gmTools.mkdir(error_dir)
		work_filename = gmTools.get_unique_filename(prefix = gmTools.fname_stem(filename) + '-', suffix = '.hl7')
		if target_dir is None:
			target_dir = os.path.join(orig_dir, 'PID')
		_log.debug('target dir: %s', target_dir)
		gmTools.mkdir(target_dir)
	except Exception:
		_log.exception('cannot setup splitting environment')
		root_logger.removeHandler(local_logger)
		return False, None

	# split
	target_names = []
	try:
		shutil.copy(filename, work_filename)
		fixed_filename = __fix_malformed_hl7_file(work_filename, encoding = encoding)
		MSH_fnames = __split_hl7_file_by_MSH(fixed_filename, encoding)
		PID_fnames = []
		for MSH_fname in MSH_fnames:
			PID_fnames.extend(__split_MSH_by_PID(MSH_fname))
		for PID_fname in PID_fnames:
			shutil.move(PID_fname, target_dir)
			target_names.append(os.path.join(target_dir, os.path.split(PID_fname)[1]))
	except Exception:
		_log.exception('cannot split HL7 file')
		for target_name in target_names:
			try: os.remove(target_name)
			except Exception: pass
		root_logger.removeHandler(local_logger)
		shutil.move(local_log_name, error_dir)
		return False, None

	_log.info('successfully split')
	root_logger.removeHandler(local_logger)
	try:
		shutil.move(filename, done_dir)
		shutil.move(local_log_name, done_dir)
	except shutil.Error:
		_log.exception('cannot move hl7 file or log file to holding area')
	return True, target_names
示例#25
0
文件: gmCrypto.py 项目: ncqgm/gnumed
def create_encrypted_zip_archive_from_dir(source_dir, comment=None, overwrite=True, passphrase=None, verbose=False):
	"""Use 7z to create an encrypted ZIP archive of a directory.

	<source_dir>		will be included into the archive
	<comment>			included as a file containing the comment
	<overwrite>			remove existing archive before creation, avoiding
						*updating* of those, and thereby including unintended data
	<passphrase>		minimum length of 5

	The resulting zip archive will always be named
	"datawrapper.zip" for confidentiality reasons. If callers
	want another name they will have to shutil.move() the zip
	file themselves. This archive will be compressed and
	AES256 encrypted with the given passphrase. Therefore,
	the result will not decrypt with earlier versions of
	unzip software. On Windows, 7z oder WinZip are needed.

	The zip format does not support header encryption thereby
	allowing attackers to gain knowledge of patient details
	by observing the names of files and directories inside
	the encrypted archive.

	To reduce that attack surface, GNUmed will create
	_another_ zip archive inside "datawrapper.zip", which
	eventually wraps up the patient data as "data.zip". That
	archive is not compressed and not encrypted, and can thus
	be unpacked with any old unzipper.

	Note that GNUmed does NOT remember the passphrase for
	you. You will have to take care of that yourself, and
	possibly also safely hand over the passphrase to any
	receivers of the zip archive.
	"""
	if len(passphrase) < 5:
		_log.error('<passphrase> must be at least 5 characters/signs/digits')
		return None
	gmLog2.add_word2hide(passphrase)

	source_dir = os.path.abspath(source_dir)
	if not os.path.isdir(source_dir):
		_log.error('<source_dir> does not exist or is not a directory: %s', source_dir)
		return False

	for cmd in ['7z', '7z.exe']:
		found, binary = gmShellAPI.detect_external_binary(binary = cmd)
		if found:
			break
	if not found:
		_log.warning('no 7z binary found')
		return None

	sandbox_dir = gmTools.mk_sandbox_dir()
	archive_path_inner = os.path.join(sandbox_dir, 'data')
	if not gmTools.mkdir(archive_path_inner):
		_log.error('cannot create scratch space for inner achive: %s', archive_path_inner)
	archive_fname_inner = 'data.zip'
	archive_name_inner = os.path.join(archive_path_inner, archive_fname_inner)
	archive_path_outer = gmTools.gmPaths().tmp_dir
	archive_fname_outer = 'datawrapper.zip'
	archive_name_outer = os.path.join(archive_path_outer, archive_fname_outer)
	# remove existing archives so they don't get *updated* rather than newly created
	if overwrite:
		if not gmTools.remove_file(archive_name_inner, force = True):
			_log.error('cannot remove existing archive [%s]', archive_name_inner)
			return False

		if not gmTools.remove_file(archive_name_outer, force = True):
			_log.error('cannot remove existing archive [%s]', archive_name_outer)
			return False

	# 7z does not support ZIP comments so create a text file holding the comment
	if comment is not None:
		tmp, fname = os.path.split(source_dir.rstrip(os.sep))
		comment_filename = os.path.join(sandbox_dir, '000-%s-comment.txt' % fname)
		with open(comment_filename, mode = 'wt', encoding = 'utf8', errors = 'replace') as comment_file:
			comment_file.write(comment)

	# create inner (data) archive: uncompressed, unencrypted, similar to a tar archive
	args = [
		binary,
		'a',				# create archive
		'-sas',				# be smart about archive name extension
		'-bd',				# no progress indicator
		'-mx0',				# no compression (only store files)
		'-mcu=on',			# UTF8 filenames
		'-l',				# store content of links, not links
		'-scsUTF-8',		# console charset
		'-tzip'				# force ZIP format
	]
	if verbose:
		args.append('-bb3')
		args.append('-bt')
	else:
		args.append('-bb1')
	args.append(archive_name_inner)
	args.append(source_dir)
	if comment is not None:
		args.append(comment_filename)
	success, exit_code, stdout = gmShellAPI.run_process(cmd_line = args, encoding = 'utf8', verbose = verbose)
	if not success:
		_log.error('cannot create inner archive')
		return None

	# create "decompress instructions" file
	instructions_filename = os.path.join(archive_path_inner, '000-on_Windows-open_with-WinZip_or_7z_tools')
	open(instructions_filename, mode = 'wt').close()

	# create outer (wrapper) archive: compressed, encrypted
	args = [
		binary,
		'a',					# create archive
		'-sas',					# be smart about archive name extension
		'-bd',					# no progress indicator
		'-mx9',					# best available zip compression ratio
		'-mcu=on',				# UTF8 filenames
		'-l',					# store content of links, not links
		'-scsUTF-8',			# console charset
		'-tzip',				# force ZIP format
		'-mem=AES256',			# force useful encryption
		'-p%s' % passphrase		# set passphrase
	]
	if verbose:
		args.append('-bb3')
		args.append('-bt')
	else:
		args.append('-bb1')
	args.append(archive_name_outer)
	args.append(archive_path_inner)
	success, exit_code, stdout = gmShellAPI.run_process(cmd_line = args, encoding = 'utf8', verbose = verbose)
	if success:
		return archive_name_outer
	_log.error('cannot create outer archive')
	return None