[med-svn] [Git][med-team/gnumed-server][upstream] New upstream version 22.16
Andreas Tille (@tille)
gitlab at salsa.debian.org
Sun Aug 29 14:26:06 BST 2021
Andreas Tille pushed to branch upstream at Debian Med / gnumed-server
Commits:
1f27a818 by Andreas Tille at 2021-08-29T15:22:08+02:00
New upstream version 22.16
- - - - -
18 changed files:
- server/bootstrap/bootstrap_gm_db_system.py
- server/bootstrap/fixup_db-v22.conf
- server/bootstrap/update_db-v21_v22.conf
- server/bootstrap/upgrade-db.sh
- server/doc/schema/gnumed-entire_schema.html
- server/gm-backup.sh
- server/gm-restore.sh
- server/gm-zip+sign_backups.sh
- server/pycommon/gmBackendListener.py
- server/pycommon/gmConnectionPool.py
- server/pycommon/gmDateTime.py
- server/pycommon/gmI18N.py
- server/pycommon/gmNetworkTools.py
- server/pycommon/gmPG2.py
- server/pycommon/gmTools.py
- server/sql/v21-v22/dynamic/v22-clin-remove_old_empty_encounters.sql
- + server/sql/v21-v22/fixups/v22-clin-remove_old_empty_encounters-fixup.sql
- server/sql/v21-v22/fixups/v22-release_notes-fixup.sql
Changes:
=====================================
server/bootstrap/bootstrap_gm_db_system.py
=====================================
@@ -39,6 +39,9 @@ __license__ = "GPL v2 or later"
# standard library
import sys
+if sys.hexversion < 0x3000000:
+ sys.exit('This code must be run with Python 3.')
+
import os.path
import fileinput
import os
@@ -81,7 +84,11 @@ sys.path.insert(0, local_python_base_dir)
# GNUmed imports
-from Gnumed.pycommon import gmLog2
+try:
+ from Gnumed.pycommon import gmLog2
+except ImportError:
+ print("""Please make sure the GNUmed Python modules are in the Python path !""")
+ raise
from Gnumed.pycommon import gmCfg2
from Gnumed.pycommon import gmPsql
from Gnumed.pycommon import gmPG2
@@ -1902,7 +1909,7 @@ sys.exit(0)
# pipe.tochild.close()
# result = pipe.wait()
-# print result
+# print(result)
# read any leftovers
# pipe.fromchild.flush()
=====================================
server/bootstrap/fixup_db-v22.conf
=====================================
@@ -33,6 +33,7 @@ v22-i18n-lang_funcs-fixup.sql
v22-clin-v_candidate_diagnoses-fixup.sql
v22-dem-v_message_inbox-fixup.sql
v22-add_generic_covid_vaccine.sql
+v22-clin-remove_old_empty_encounters-fixup.sql
v22-release_notes-fixup.sql
$schema$
=====================================
server/bootstrap/update_db-v21_v22.conf
=====================================
@@ -165,6 +165,7 @@ v22-i18n-lang_funcs-fixup.sql
v22-clin-v_candidate_diagnoses-fixup.sql
v22-dem-v_message_inbox-fixup.sql
v22-add_generic_covid_vaccine.sql
+v22-clin-remove_old_empty_encounters-fixup.sql
v22-release_notes-fixup.sql
$schema$
=====================================
server/bootstrap/upgrade-db.sh
=====================================
@@ -85,7 +85,6 @@ function echo_msg () {
}
-
# Darwin/MacOSX ?
# (MacOSX cannot "su -c" ...)
SYSTEM=`uname -s`
@@ -110,7 +109,6 @@ if test "${SYSTEM}" != "Darwin" ; then
fi ;
-
# show what we do
echo_msg ""
echo_msg "==========================================================="
@@ -131,7 +129,7 @@ echo_msg "==========================================================="
-# better safe than sorry
+# check for existing target database
if test "${SYSTEM}" != "Darwin" ; then
# Does TARGET database exist ?
VER_EXISTS=`su -c "psql -l ${PORT_DEF}" -l postgres | grep gnumed_v${NEXT_VER}`
@@ -153,12 +151,35 @@ if test "${SYSTEM}" != "Darwin" ; then
echo "Upgrading aborted by user."
exit 1
fi
+ echo ""
fi
fi
+# check disk space
+DATA_DIR=$(su -c "psql --no-align --tuples-only --quiet -c \"SELECT setting FROM pg_settings WHERE name = 'data_directory' \" " -l postgres)
+BYTES_FREE=$(df --block-size=1 --output=avail ${DATA_DIR} | grep --only-matching -E '[[:digit:]]+')
+DB_SIZE=$(su -c "psql --no-align --tuples-only --quiet -c \"SELECT pg_database_size('gnumed_v22') \" | grep --only-matching -E '[[:digit:]]+' " -l postgres) #"
+if [ ${DB_SIZE} -gt ${BYTES_FREE} ] ; then
+ echo ""
+ echo "WARNING: Disk space may be insufficient"
+ echo "WARNING:"
+ echo "WARNING: Data directory : ${DATA_DIR}"
+ echo "WARNING: Data directory : ${DB_SIZE}"
+ echo "WARNING: Free disk space: ${BYTES_FREE}"
+ echo "WARNING:"
+ echo ""
+ echo "Continue upgrading (may fail) ? "
+ echo ""
+ read -e -p "[y / N]: "
+ if test "${REPLY}" != "y" ; then
+ echo "Upgrading aborted by user."
+ exit 1
+ fi
+fi
+
-# eventually attempt the upgrade
+# either backup or verify checksums
echo_msg ""
if test "$SKIP_BACKUP" != "no-backup" ; then
echo_msg "1) creating backup of the database that is to be upgraded ..."
@@ -218,6 +239,7 @@ else
fi ;
+# eventually attempt the upgrade
echo_msg ""
echo_msg "2) upgrading to new database ..."
# fixup for schema hash function
=====================================
server/doc/schema/gnumed-entire_schema.html
=====================================
@@ -112,7 +112,7 @@
<body>
<!-- Primary Index -->
- <p><br><br>Dumped on 2021-01-23</p>
+ <p><br><br>Dumped on 2021-08-02</p>
<h1><a name="index">Index of database - gnumed_v22</a></h1>
<ul>
@@ -103018,6 +103018,9 @@ BEGIN
DELETE FROM clin.encounter WHERE
clin.encounter.fk_patient = _pk_identity
AND
+ -- is this the active encounter somewhere ?
+ (SELECT pg_try_advisory_lock('clin.encounter'::regclass::oid::int, clin.encounter.pk))
+ AND
age(clin.encounter.last_affirmed) > _usable_minimum_encounter_age
AND
NOT EXISTS (SELECT 1 FROM clin.clin_root_item WHERE fk_encounter = clin.encounter.pk)
@@ -103036,7 +103039,7 @@ BEGIN
AND
NOT EXISTS (SELECT 1 FROM clin.suppressed_hint WHERE fk_encounter = clin.encounter.pk)
;
- return true;
+ RETURN TRUE;
END;</pre>
<hr>
@@ -116594,25 +116597,174 @@ SELECT all_msgs.received_when
FROM (
(
(
+ SELECT mi.modified_when AS received_when
+ ,
+ COALESCE
+ (
+ (
+ SELECT staff.short_alias
+
+ FROM dem.staff
+
+ WHERE (staff.db_user = mi.modified_by)
+ )
+ , (
+ ('<'::text ||
+ (mi.modified_by)::text
+ ) || '>'::text
+ )
+ ) AS modified_by
+ ,
+
+ (
+ SELECT staff.short_alias
+
+ FROM dem.staff
+
+ WHERE (staff.pk = mi.fk_staff)
+ ) AS provider
+ ,
+ mi.importance
+ ,
+ vit.category
+ ,
+ vit.l10n_category
+ ,
+ vit.type
+ ,
+ vit.l10n_type
+ ,
+ mi.comment
+ ,
+ mi.ufk_context AS pk_context
+ ,
+ mi.data
+ ,
+ mi.pk AS pk_inbox_message
+ ,
+ mi.fk_staff AS pk_staff
+ ,
+ vit.pk_category
+ ,
+ mi.fk_inbox_item_type AS pk_type
+ ,
+ mi.fk_patient AS pk_patient
+ ,
+ false AS is_virtual
+ ,
+ mi.due_date
+ ,
+ mi.expiry_date
+ ,
+ CASE
+ WHEN
+ (mi.due_date IS NULL) THEN false
+ WHEN
+ (mi.due_date > now
+ ()
+ ) THEN false
+ WHEN
+ (mi.expiry_date IS NULL) THEN true
+ WHEN
+ (mi.expiry_date < now
+ ()
+ ) THEN false
+ ELSE true
+ END AS is_overdue
+ ,
+ CASE
+ WHEN
+ (mi.expiry_date IS NULL) THEN false
+ WHEN
+ (mi.expiry_date > now
+ ()
+ ) THEN false
+ ELSE true
+ END AS is_expired
+ ,
+ CASE
+ WHEN
+ (mi.due_date IS NULL) THEN NULL::interval
+ WHEN
+ (mi.due_date > now
+ ()
+ ) THEN
(
+ (mi.due_date)::timestamp with time zone - now
+ ()
+ )
+ ELSE
+ (now
+ () -
+ (mi.due_date)::timestamp with time zone
+ )
+ END AS interval_due
+ ,
+ gm.xid2int
+ (mi.xmin) AS xmin_message_inbox
+
+ FROM dem.message_inbox mi
+ ,
+ dem.v_inbox_item_type vit
+
+ WHERE (mi.fk_inbox_item_type = vit.pk_type)
+
+ UNION ALL
+
+ SELECT v_unreviewed_docs_inbox.received_when
+ ,
+ v_unreviewed_docs_inbox.modified_by
+ ,
+ v_unreviewed_docs_inbox.provider
+ ,
+ v_unreviewed_docs_inbox.importance
+ ,
+ v_unreviewed_docs_inbox.category
+ ,
+ v_unreviewed_docs_inbox.l10n_category
+ ,
+ v_unreviewed_docs_inbox.type
+ ,
+ v_unreviewed_docs_inbox.l10n_type
+ ,
+ v_unreviewed_docs_inbox.comment
+ ,
+ v_unreviewed_docs_inbox.pk_context
+ ,
+ v_unreviewed_docs_inbox.data
+ ,
+ v_unreviewed_docs_inbox.pk_inbox_message
+ ,
+ v_unreviewed_docs_inbox.pk_staff
+ ,
+ v_unreviewed_docs_inbox.pk_category
+ ,
+ v_unreviewed_docs_inbox.pk_type
+ ,
+ v_unreviewed_docs_inbox.pk_patient
+ ,
+ v_unreviewed_docs_inbox.is_virtual
+ ,
+ v_unreviewed_docs_inbox.due_date
+ ,
+ v_unreviewed_docs_inbox.expiry_date
+ ,
+ v_unreviewed_docs_inbox.is_overdue
+ ,
+ v_unreviewed_docs_inbox.is_expired
+ ,
+ v_unreviewed_docs_inbox.interval_due
+ ,
+ v_unreviewed_docs_inbox.xmin_message_inbox
+
+ FROM blobs.v_unreviewed_docs_inbox
+
+ UNION ALL (
- SELECT mi.modified_when AS received_when
+ SELECT now
+ () AS received_when
,
- COALESCE
- (
- (
- SELECT staff.short_alias
-
- FROM dem.staff
-
- WHERE (staff.db_user = mi.modified_by)
- )
- , (
- ('<'::text ||
- (mi.modified_by)::text
- ) || '>'::text
- )
- ) AS modified_by
+ vtr.modified_by
,
(
@@ -116620,174 +116772,132 @@ FROM (
FROM dem.staff
- WHERE (staff.pk = mi.fk_staff)
+ WHERE (staff.pk = vtr.pk_intended_reviewer)
) AS provider
,
- mi.importance
- ,
- vit.category
- ,
- vit.l10n_category
- ,
- vit.type
- ,
- vit.l10n_type
- ,
- mi.comment
- ,
- mi.ufk_context AS pk_context
- ,
- mi.data
- ,
- mi.pk AS pk_inbox_message
+ 1 AS importance
,
- mi.fk_staff AS pk_staff
+ 'clinical'::text AS category
,
- vit.pk_category
+ _
+ ('clinical'::text) AS l10n_category
,
- mi.fk_inbox_item_type AS pk_type
+ 'review results'::text AS type
,
- mi.fk_patient AS pk_patient
+ _
+ ('review results'::text) AS l10n_type
,
- false AS is_virtual
- ,
- mi.due_date
- ,
- mi.expiry_date
- ,
- CASE
- WHEN
- (mi.due_date IS NULL) THEN false
- WHEN
- (mi.due_date > now
- ()
- ) THEN false
- WHEN
- (mi.expiry_date IS NULL) THEN true
- WHEN
- (mi.expiry_date < now
- ()
- ) THEN false
- ELSE true
- END AS is_overdue
- ,
- CASE
- WHEN
- (mi.expiry_date IS NULL) THEN false
- WHEN
- (mi.expiry_date > now
- ()
- ) THEN false
- ELSE true
- END AS is_expired
- ,
- CASE
- WHEN
- (mi.due_date IS NULL) THEN NULL::interval
- WHEN
- (mi.due_date > now
- ()
- ) THEN
+
(
- (mi.due_date)::timestamp with time zone - now
- ()
+ SELECT (
+ (
+ (
+ (
+ (_
+ ('unreviewed (abnormal) results for patient'::text
+ ) || ' ['::text
+ ) || dn.lastnames
+ ) ||
+ ', '::text
+ ) || dn.firstnames
+ ) || ']'::text
)
- ELSE
- (now
- () -
- (mi.due_date)::timestamp with time zone
+
+ FROM dem.names dn
+
+ WHERE (
+ (dn.id_identity = vtr.pk_patient)
+ AND (dn.active IS TRUE)
)
- END AS interval_due
- ,
- gm.xid2int
- (mi.xmin) AS xmin_message_inbox
+ ) AS comment
+ ,
+ NULL::integer[] AS pk_context
+ ,
+ NULL::text AS data
+ ,
+ NULL::integer AS pk_inbox_message
+ ,
+ vtr.pk_intended_reviewer AS pk_staff
+ ,
+
+ (
+ SELECT v_inbox_item_type.pk_category
+
+ FROM dem.v_inbox_item_type
+
+ WHERE (v_inbox_item_type.type = 'review results'::text)
+ ) AS pk_category
+ ,
+
+ (
+ SELECT v_inbox_item_type.pk_type
+
+ FROM dem.v_inbox_item_type
+
+ WHERE (v_inbox_item_type.type = 'review results'::text)
+ ) AS pk_type
+ ,
+ vtr.pk_patient
+ ,
+ true AS is_virtual
+ ,
+
+ (now
+ () - '01:00:00'::interval
+ ) AS due_date
+ ,
+ NULL::timestamp with time zone AS expiry_date
+ ,
+ true AS is_overdue
+ ,
+ false AS is_expired
+ ,
+ '01:00:00'::interval AS interval_due
+ ,
+ NULL::integer AS xmin_message_inbox
- FROM dem.message_inbox mi
- ,
- dem.v_inbox_item_type vit
+ FROM clin.v_test_results vtr
- WHERE (mi.fk_inbox_item_type = vit.pk_type)
-
- UNION ALL
-
- SELECT v_unreviewed_docs_inbox.received_when
- ,
- v_unreviewed_docs_inbox.modified_by
- ,
- v_unreviewed_docs_inbox.provider
- ,
- v_unreviewed_docs_inbox.importance
- ,
- v_unreviewed_docs_inbox.category
- ,
- v_unreviewed_docs_inbox.l10n_category
- ,
- v_unreviewed_docs_inbox.type
- ,
- v_unreviewed_docs_inbox.l10n_type
- ,
- v_unreviewed_docs_inbox.comment
- ,
- v_unreviewed_docs_inbox.pk_context
- ,
- v_unreviewed_docs_inbox.data
- ,
- v_unreviewed_docs_inbox.pk_inbox_message
- ,
- v_unreviewed_docs_inbox.pk_staff
- ,
- v_unreviewed_docs_inbox.pk_category
- ,
- v_unreviewed_docs_inbox.pk_type
- ,
- v_unreviewed_docs_inbox.pk_patient
- ,
- v_unreviewed_docs_inbox.is_virtual
- ,
- v_unreviewed_docs_inbox.due_date
- ,
- v_unreviewed_docs_inbox.expiry_date
- ,
- v_unreviewed_docs_inbox.is_overdue
- ,
- v_unreviewed_docs_inbox.is_expired
- ,
- v_unreviewed_docs_inbox.interval_due
- ,
- v_unreviewed_docs_inbox.xmin_message_inbox
-
- FROM blobs.v_unreviewed_docs_inbox
-
+ WHERE (
+ (vtr.reviewed IS FALSE)
+ AND (
+ (vtr.is_technically_abnormal IS TRUE)
+ OR (
+ (vtr.is_technically_abnormal IS NULL)
+ AND (vtr.abnormality_indicator IS NOT NULL)
+ )
+ )
)
+
UNION
-
+
SELECT now
() AS received_when
,
- vtr.modified_by
+ vtr.modified_by
,
-
+
(
SELECT staff.short_alias
-
+
FROM dem.staff
-
+
WHERE (staff.pk = vtr.pk_intended_reviewer)
) AS provider
,
- 0 AS importance
+ 0 AS importance
,
- 'clinical'::text AS category
+ 'clinical'::text AS category
,
- _
+ _
('clinical'::text) AS l10n_category
,
- 'review results'::text AS type
+ 'review results'::text AS type
,
- _
+ _
('review results'::text) AS l10n_type
,
-
+
(
SELECT (
(
@@ -116802,62 +116912,62 @@ FROM (
) || dn.firstnames
) || ']'::text
)
-
+
FROM dem.names dn
-
+
WHERE (
(dn.id_identity = vtr.pk_patient)
AND (dn.active IS TRUE)
)
) AS comment
,
- NULL::integer[] AS pk_context
+ NULL::integer[] AS pk_context
,
- NULL::text AS data
+ NULL::text AS data
,
- NULL::integer AS pk_inbox_message
+ NULL::integer AS pk_inbox_message
,
- vtr.pk_intended_reviewer AS pk_staff
+ vtr.pk_intended_reviewer AS pk_staff
,
-
+
(
SELECT v_inbox_item_type.pk_category
-
+
FROM dem.v_inbox_item_type
-
+
WHERE (v_inbox_item_type.type = 'review results'::text)
) AS pk_category
,
-
+
(
SELECT v_inbox_item_type.pk_type
-
+
FROM dem.v_inbox_item_type
-
+
WHERE (v_inbox_item_type.type = 'review results'::text)
) AS pk_type
,
- vtr.pk_patient
+ vtr.pk_patient
,
- true AS is_virtual
+ true AS is_virtual
,
-
+
(now
() - '01:00:00'::interval
) AS due_date
,
- NULL::timestamp with time zone AS expiry_date
+ NULL::timestamp with time zone AS expiry_date
,
- true AS is_overdue
+ true AS is_overdue
,
- false AS is_expired
+ false AS is_expired
,
- '01:00:00'::interval AS interval_due
+ '01:00:00'::interval AS interval_due
,
- NULL::integer AS xmin_message_inbox
-
+ NULL::integer AS xmin_message_inbox
+
FROM clin.v_test_results vtr
-
+
WHERE (
(vtr.reviewed IS FALSE)
AND (
@@ -116869,114 +116979,6 @@ FROM (
)
)
- UNION
-
- SELECT now
- () AS received_when
- ,
- vtr.modified_by
- ,
-
- (
- SELECT staff.short_alias
-
- FROM dem.staff
-
- WHERE (staff.pk = vtr.pk_intended_reviewer)
- ) AS provider
- ,
- 1 AS importance
- ,
- 'clinical'::text AS category
- ,
- _
- ('clinical'::text) AS l10n_category
- ,
- 'review results'::text AS type
- ,
- _
- ('review results'::text) AS l10n_type
- ,
-
- (
- SELECT (
- (
- (
- (
- (_
- ('unreviewed (abnormal) results for patient'::text
- ) || ' ['::text
- ) || dn.lastnames
- ) ||
- ', '::text
- ) || dn.firstnames
- ) || ']'::text
- )
-
- FROM dem.names dn
-
- WHERE (
- (dn.id_identity = vtr.pk_patient)
- AND (dn.active IS TRUE)
- )
- ) AS comment
- ,
- NULL::integer[] AS pk_context
- ,
- NULL::text AS data
- ,
- NULL::integer AS pk_inbox_message
- ,
- vtr.pk_intended_reviewer AS pk_staff
- ,
-
- (
- SELECT v_inbox_item_type.pk_category
-
- FROM dem.v_inbox_item_type
-
- WHERE (v_inbox_item_type.type = 'review results'::text)
- ) AS pk_category
- ,
-
- (
- SELECT v_inbox_item_type.pk_type
-
- FROM dem.v_inbox_item_type
-
- WHERE (v_inbox_item_type.type = 'review results'::text)
- ) AS pk_type
- ,
- vtr.pk_patient
- ,
- true AS is_virtual
- ,
-
- (now
- () - '01:00:00'::interval
- ) AS due_date
- ,
- NULL::timestamp with time zone AS expiry_date
- ,
- true AS is_overdue
- ,
- false AS is_expired
- ,
- '01:00:00'::interval AS interval_due
- ,
- NULL::integer AS xmin_message_inbox
-
- FROM clin.v_test_results vtr
-
- WHERE (
- (vtr.reviewed IS FALSE)
- AND (
- (vtr.is_technically_abnormal IS TRUE)
- OR (
- (vtr.is_technically_abnormal IS NULL)
- AND (vtr.abnormality_indicator IS NOT NULL)
- )
- )
)
) all_msgs
@@ -116986,13 +116988,15 @@ ON (
)
)
-JOIN dem.names d_n
+LEFT JOIN dem.names d_n
ON (
(d_i.pk = d_n.id_identity)
)
)
-WHERE (d_n.active IS TRUE);</pre>
+WHERE (d_n.active IS DISTINCT
+FROM false
+) ;</pre>
<!-- List off permissions -->
=====================================
server/gm-backup.sh
=====================================
@@ -65,7 +65,7 @@ fi
# compute host argument and backup filename
if test -z "${GM_HOST}" ; then
_PG_HOST_ARG=""
- BACKUP_BASENAME="backup-${GM_DATABASE}-${INSTANCE_OWNER}-"$(hostname)
+ BACKUP_BASENAME="backup-${GM_DATABASE}-${INSTANCE_OWNER}-$(hostname)"
else
if ping -c 3 -i 2 "${GM_HOST}" > /dev/null; then
_PG_HOST_ARG="--host=\"${GM_HOST}\""
@@ -108,7 +108,7 @@ fi
if test "${HAS_HIGHER_VER}" = "t" ; then
echo "Backing up database ${GM_DATABASE}."
echo ""
- echo "However, a newer database seems to exist:"
+ echo "However, a newer database seems to exist amongst the following:"
echo ""
sudo --user=postgres psql --no-psqlrc --list ${_PG_PORT_ARG} | grep gnumed_v
echo ""
@@ -123,13 +123,13 @@ BACKUP_FILENAME="${BACKUP_BASENAME}-${TS}"
# generate scratch dir
SCRATCH_DIR="/tmp/${BACKUP_FILENAME}"
-mkdir -p ${SCRATCH_DIR}
+mkdir -p "${SCRATCH_DIR}"
RESULT="$?"
if test "${RESULT}" != "0" ; then
echo "Cannot create backup scratch directory [${SCRATCH_DIR}] (${RESULT}). Aborting."
exit ${RESULT}
fi
-cd ${SCRATCH_DIR}
+cd "${SCRATCH_DIR}"
RESULT="$?"
if test "${RESULT}" != "0" ; then
echo "Cannot change into scratch backup directory [${SCRATCH_DIR}] (${RESULT}). Aborting."
@@ -139,13 +139,13 @@ fi
# create backup timestamp tag file
TS_FILE="${BACKUP_BASENAME}-timestamp.txt"
-echo "backup: ${TS}" > ${TS_FILE}
+echo "backup: ${TS}" > "${TS_FILE}"
# create dumps
BACKUP_DATA_DIR="${BACKUP_BASENAME}.dir"
# database
-pg_dump --verbose --format=directory --compress=0 --column-inserts --clean --if-exists --serializable-deferrable ${_PG_HOST_ARG} ${_PG_PORT_ARG} --username="${GM_DBO}" -f "${BACKUP_DATA_DIR}" "${GM_DATABASE}" 2> /dev/null
+pg_dump --verbose --format=directory --compress=0 --column-inserts --clean --if-exists --serializable-deferrable ${_PG_HOST_ARG} ${_PG_PORT_ARG} --username="${GM_DBO}" --file="${BACKUP_DATA_DIR}" "${GM_DATABASE}" 2> /dev/null
RESULT="$?"
if test "${RESULT}" != "0" ; then
echo "Cannot dump database content into [${BACKUP_DATA_DIR}] (${RESULT}). Aborting."
@@ -153,10 +153,7 @@ if test "${RESULT}" != "0" ; then
fi
# roles
ROLES_FILE="${BACKUP_BASENAME}-roles.sql"
-# -r -> -g for older versions
-#ROLES=`psql --no-psqlrc --no-align --tuples-only --dbname="${GM_DATABASE}" ${_PG_HOST_ARG} ${_PG_PORT_ARG} --username="${GM_DBO}" --command="select gm.get_users('${GM_DATABASE}');"`
-ROLES=$(psql --no-psqlrc --no-align --tuples-only --dbname="${GM_DATABASE}" ${_PG_HOST_ARG} ${_PG_PORT_ARG} --username="${GM_DBO}" --command="select gm.get_users('${GM_DATABASE}');")
-#"
+ROLES=$(psql --no-psqlrc --no-align --tuples-only --dbname="${GM_DATABASE}" ${_PG_HOST_ARG} ${_PG_PORT_ARG} --username="${GM_DBO}" --command="select gm.get_users('${GM_DATABASE}');") #"
RESULT="$?"
if test "${RESULT}" != "0" ; then
echo "Cannot list database roles (${RESULT}). Aborting."
@@ -181,6 +178,7 @@ fi
echo "-- Below is the result of 'pg_dumpall --roles-only':"
echo "-- -----------------------------------------------------"
} > "${ROLES_FILE}" 2> /dev/null
+# -r -> -g for older versions
sudo --user=postgres pg_dumpall --verbose --roles-only ${_PG_HOST_ARG} ${_PG_PORT_ARG} --username=postgres >> "${ROLES_FILE}" 2> /dev/null
RESULT="$?"
if test "${RESULT}" != "0" ; then
@@ -214,7 +212,7 @@ fi
# move "untested" tar archive to final directory
# so the compression script can pick it up if needed
-mv --force "${TAR_UNTESTED}" "${BACKUP_DIR}/"
+mv --force "${TAR_UNTESTED}" "${BACKUP_DIR}"/
RESULT="$?"
if test "${RESULT}" != "0" ; then
echo "cannot move TAR archive: ${TAR_UNTESTED} => ${BACKUP_DIR}/"
=====================================
server/gm-restore.sh
=====================================
@@ -23,7 +23,7 @@ set -o pipefail
BACKUP="$1"
-if test -z ${BACKUP} ; then
+if test -z "${BACKUP}" ; then
echo "====================================================="
echo "usage: $0 <backup file>"
echo ""
@@ -36,9 +36,9 @@ fi
echo ""
echo "==> Trying to restore a GNUmed backup ..."
echo " backup file: ${BACKUP}"
-if test ! -r ${BACKUP} ; then
+if test ! -r "${BACKUP}" ; then
echo " ERROR: Cannot access backup file. Aborting."
- echo " "`ls -al ${BACKUP}`
+ echo " $(ls -al "${BACKUP}")"
exit 1
fi
@@ -50,23 +50,23 @@ if [ -r ${CONF} ] ; then
. ${CONF}
else
echo " ERROR: Cannot read configuration file ${CONF}. Aborting."
- echo " "`ls -al ${CONF}`
+ echo " $(ls -al ${CONF})"
exit 1
fi
-mkdir -p ${LOG_BASE}
-TS=`date +%Y-%m-%d_%H-%M-%S`
+mkdir -p "${LOG_BASE}"
+TS=$(date +%Y-%m-%d_%H-%M-%S)
LOG="${LOG_BASE}/restore-${TS}.log"
echo " log: ${LOG}"
-echo "restore: ${TS}" &> ${LOG}
-chmod 0666 ${LOG}
+echo "restore: ${TS}" &> "${LOG}"
+chmod 0666 "${LOG}"
if [[ "$BACKUP" =~ .*\.bz2 ]] ; then
echo ""
echo "==> Testing backup file integrity ..."
- bzip2 -tv $BACKUP &>> ${LOG}
+ bzip2 -tv "$BACKUP" &>> "${LOG}"
if test $? -ne 0 ; then
echo " ERROR: Integrity check failed. Aborting."
echo ""
@@ -82,21 +82,22 @@ echo ""
echo "==> Setting up workspace ..."
WORK_DIR="${WORK_DIR_BASE}/gm-restore_${TS}/"
echo " work dir: ${WORK_DIR}"
-mkdir -p -v ${WORK_DIR} &>> ${LOG}
+mkdir -p -v "${WORK_DIR}" &>> "${LOG}"
if test $? -ne 0 ; then
echo " ERROR: Cannot create workspace. Aborting."
echo " log: ${LOG}"
exit 1
fi
-chmod +rx ${WORK_DIR}
+chmod +rx "${WORK_DIR}"
+ln -s "${LOG}" "${WORK_DIR}"
echo ""
echo "==> Creating copy of backup file ..."
-cp -v ${BACKUP} ${WORK_DIR} &>> ${LOG}
+cp -v "${BACKUP}" "${WORK_DIR}" &>> "${LOG}"
if test $? -ne 0 ; then
echo " ERROR: Cannot copy backup file. Aborting."
- echo " "`ls -al ${BACKUP}`
+ echo " $(ls -al "${BACKUP}")"
echo " log: ${LOG}"
exit 1
fi
@@ -104,67 +105,64 @@ fi
echo ""
echo "==> Unpacking backup file ..."
-BACKUP=${WORK_DIR}`basename ${BACKUP}`
+BACKUP=${WORK_DIR}$(basename "${BACKUP}")
if [[ "${BACKUP}" =~ .*\.bz2 ]] ; then
echo " => Decompressing (from bzip2) ..."
- bunzip2 -v ${BACKUP} &>> ${LOG}
+ bunzip2 -v "${BACKUP}" &>> "${LOG}"
if test $? -ne 0 ; then
echo " ERROR: Cannot decompress bzip2 backup file. Aborting."
- echo " pwd: "`pwd`
+ echo " pwd: $(pwd)"
echo " file: ${BACKUP}"
echo " log: ${LOG}"
exit 1
fi
- BACKUP=${WORK_DIR}`basename ${BACKUP} .bz2`
+ BACKUP=${WORK_DIR}$(basename "${BACKUP}" .bz2)
fi
echo " => Extracting (from tarball) ..."
-tar -C ${WORK_DIR} -xvf ${BACKUP} &>> ${LOG}
+tar -C "${WORK_DIR}" -xvf "${BACKUP}" &>> "${LOG}"
if test $? -ne 0 ; then
echo " ERROR: Cannot unpack tarball backup file. Aborting."
- echo " pwd: "`pwd`
+ echo " pwd: $(pwd)"
echo " file: ${BACKUP}"
echo " log: ${LOG}"
exit 1
fi
-BACKUP=${WORK_DIR}`basename ${BACKUP} .tar`
-rm ${BACKUP}.tar &>> ${LOG}
+rm "${BACKUP}" &>> "${LOG}"
+BACKUP="$(ls -1 -d "${WORK_DIR}"*.dir)"
+BACKUP="${WORK_DIR}$(basename "${BACKUP}" .dir)"
+echo "PG backup in dir: ${BACKUP}" &>> "${LOG}"
echo ""
echo "==> Checking target database status ..."
-TARGET_DB=`pg_restore --create --schema-only ${BACKUP}.dir | head -n 40 | grep -i 'create database gnumed_v' | cut -f 3 -d ' '`
+TARGET_DB=$(pg_restore --create --schema-only --file=- "${BACKUP}".dir | head -n 40 | grep -i 'create database gnumed_v' | cut -f 3 -d ' ')
ERROR="$?"
-echo "${TARGET_DB}" &>> ${LOG}
-#if test ${ERROR} -ne 0 ; then
-# echo "exit code: ${ERROR}" &>> ${LOG}
-# echo " ERROR: Cannot determine target database from backup file. Aborting."
-# echo " log: ${LOG}"
-# exit 1
-#fi
-if test -z ${TARGET_DB} ; then
+echo "${TARGET_DB}" &>> "${LOG}"
+if test -z "${TARGET_DB}" ; then
echo " ERROR: Backup does not create target database ${TARGET_DB}. Aborting."
echo " log: ${LOG}"
exit 1
fi
echo " db: ${TARGET_DB}"
-if test `sudo -u postgres psql -l -p ${GM_PORT} | grep ${TARGET_DB} | wc -l` -ne 0 ; then
+if test $(sudo -u postgres psql -l -p "${GM_PORT}" | grep --count "${TARGET_DB}") -ne 0 ; then
echo ""
echo " Target database ${TARGET_DB} already exists."
echo ""
echo " Restoring will OVERWRITE the existing database."
echo " Are you really positively sure ?"
echo ""
- read -e -p " [yes / NO]: "
+ read -e -r -p " [yes / NO]: "
if test "${REPLY}" != "yes" ; then
- echo "${REPLY} (!='yes' => aborted by user)" &>> ${LOG}
+ echo "${REPLY} (!='yes' => aborted by user)" &>> "${LOG}"
echo " Database restore aborted by user."
echo " log: ${LOG}"
exit 1
fi
- echo "user requested drop of existing database ${TARGET_DB}" &>> ${LOG}
- sudo -u postgres dropdb -e -i --if-exists ${TARGET_DB} &>> ${LOG}
+ echo "user requested drop of existing database ${TARGET_DB}" &>> "${LOG}"
+ # shellcheck disable=SC2024
+ sudo -u postgres dropdb -e -i --if-exists "${TARGET_DB}" &>> "${LOG}"
fi
@@ -178,21 +176,23 @@ echo " Remember that in PostgreSQL scripts the comment marker is \"--\"."
echo ""
echo " There are more instructions inside the file."
echo ""
-read -e -p " Press <ENTER> to start editing."
-editor ${BACKUP}-roles.sql
+read -e -r -p " Press <ENTER> to start editing."
+editor "${BACKUP}"-roles.sql
echo ""
echo "==> Setting data file permissions ..."
-chmod -c +r ${BACKUP}-roles.sql &>> ${LOG}
-chown -c postgres ${BACKUP}-roles.sql &>> ${LOG}
-chmod -cR +r ${BACKUP}.dir &>> ${LOG}
-chown -cR postgres ${BACKUP}.dir &>> ${LOG}
+{ chmod -c +r "${BACKUP}-roles.sql" ;
+ chown -c postgres "${BACKUP}-roles.sql" ;
+ chmod -cR +rx "${BACKUP}.dir" ;
+ chown -cR postgres "${BACKUP}.dir" ;
+} &>> "${LOG}"
echo ""
echo "==> Restoring GNUmed roles ..."
-sudo -u postgres psql -e -E -p ${GM_PORT} --single-transaction -f ${BACKUP}-roles.sql &>> ${LOG}
+# shellcheck disable=SC2024
+sudo --user=postgres psql -e -E -p ${GM_PORT} --single-transaction -f "${BACKUP}"-roles.sql &>> "${LOG}"
if test $? -ne 0 ; then
echo " ERROR: Failed to restore roles. Aborting."
echo " log: ${LOG}"
@@ -211,13 +211,16 @@ echo "==> Restoring GNUmed database ${TARGET_DB} ..."
# cannot use --single-transaction because CREATE DATABASE does not work inside a transaction
# need not use --clean because --create already creates "empty" database and does not apply to things copied by createdb :-(
# need not use --if-exists because --create already creates "empty" database and it does not apply to things copied by createdb :-(
-CMD="sudo -u postgres pg_restore --verbose --create --dbname=template1 --exit-on-error -p ${GM_PORT} ${BACKUP}.dir/"
-echo "${CMD}" &>> ${LOG}
-${CMD} &>> ${LOG}
+#sudo --user=postgres pg_restore --verbose --create --exit-on-error --file=/tmp/gm-restore-test.sql -p ${GM_PORT} ${BACKUP}.dir
+CMD_PG_RESTORE="pg_restore --create --file=- ${BACKUP}.dir"
+CMD_PSQL="psql --echo-errors --dbname=template1 --port=${GM_PORT} --no-psqlrc"
+CMD_SUDO="sudo --user=postgres"
+{
+ ${CMD_PG_RESTORE} | grep --ignore-case --invert-match --regexp="alter database ${TARGET_DB} set default_transaction_read_only to.*on" - | ${CMD_SUDO} ${CMD_PSQL}
+} &>> "${LOG}"
ERROR="$?"
-echo "pg_restore exit code: ${ERROR}" &>> ${LOG}
if test ${ERROR} -ne 0 ; then
- echo " ERROR: failed to restore database. Aborting."
+ echo " ERROR: failed to restore database (${ERROR}). Aborting."
echo " log: ${LOG}"
exit 1
fi
@@ -225,21 +228,21 @@ fi
echo ""
echo "==> Analyzing database ${TARGET_DB} ..."
+# shellcheck disable=SC2024
# --full doesn't make sense since there are no
# deleted rows in a freshly restored database but
# we need to update statistics to get decent performance
-sudo -u postgres vacuumdb -v -z -d ${TARGET_DB} -p ${GM_PORT} &>> ${LOG}
-sudo -u postgres vacuumdb -v -Z -d ${TARGET_DB} -p ${GM_PORT} &>> ${LOG}
+sudo --user=postgres vacuumdb -v -Z -d "${TARGET_DB}" --port=${GM_PORT} &>> "${LOG}"
# adjusting settings
-gm-adjust_db_settings ${TARGET_DB} &>> ${LOG}
+gm-adjust_db_settings "${TARGET_DB}" &>> "${LOG}"
echo ""
echo "==> Cleaning up ..."
echo " log dir: ${LOG_BASE}"
-rm --verbose --dir --recursive --one-file-system ${WORK_DIR} &>> ${LOG}
+rm --verbose --dir --recursive --one-file-system "${WORK_DIR}" &>> "${LOG}"
echo ""
=====================================
server/gm-zip+sign_backups.sh
=====================================
@@ -62,7 +62,7 @@ AGGREGATE_EXIT_CODE=0
# find any leftover, untested tar files
# and test them so they can be compressed
-for TAR_UNTESTED in ${BACKUP_BASENAME}-*.tar.untested ; do
+for TAR_UNTESTED in "${BACKUP_BASENAME}"-*.tar.untested ; do
# test
tar --extract --to-stdout --file="${TAR_UNTESTED}" > /dev/null
@@ -89,7 +89,7 @@ done
# zip up any backups
-for TAR_FINAL in ${BACKUP_BASENAME}-*.tar ; do
+for TAR_FINAL in "${BACKUP_BASENAME}"-*.tar ; do
BZ2_FINAL="${TAR_FINAL}.bz2"
BZ2_SCRATCH="${BZ2_FINAL}.partial"
=====================================
server/pycommon/gmBackendListener.py
=====================================
@@ -219,7 +219,7 @@ class gmBackendListener(gmBorg.cBorg):
self.__notifications_received += 1
if self.debug:
print(notification)
- _log.debug('#%s: %s (first param is PID of sending backend)', self.__notifications_received, notification)
+ _log.debug('#%s: %s (first param: PID of sending backend; this backend: %s)', self.__notifications_received, notification, self.backend_pid)
# decode payload
payload = notification.payload.split('::')
operation = None
=====================================
server/pycommon/gmConnectionPool.py
=====================================
@@ -649,6 +649,14 @@ def exception_is_connection_loss(exc):
# not a PG exception
return False
+ try:
+ if isinstance(exc, dbapi.errors.AdminShutdown):
+ _log.debug('indicates connection loss due to admin shutdown')
+ return True
+
+ except AttributeError: # psycopg2 2.7/2.8 transition
+ pass
+
try:
msg = '%s' % exc.args[0]
except (AttributeError, IndexError, TypeError):
@@ -667,8 +675,8 @@ def exception_is_connection_loss(exc):
('abnorm' in msg)
or
('end' in msg)
-# or
-# ('oute' in msg)
+ or
+ ('no route' in msg)
)
) or (
# InterfaceError
=====================================
server/pycommon/gmDateTime.py
=====================================
@@ -647,10 +647,11 @@ def format_interval_medically(interval=None):
This isn't mathematically correct but close enough for display.
"""
# more than 1 year ?
- if interval.days > 363:
- years, days = divmod(interval.days, 364)
+ if interval.days > 364:
+ years, days = divmod(interval.days, avg_days_per_gregorian_year)
leap_days, tmp = divmod(years, 4)
- months, day = divmod((days + leap_days), 30.33)
+ days_left_without_leap_days = days - leap_days
+ months, day = divmod((days_left_without_leap_days), 30.33)
if int(months) == 0:
return "%s%s" % (int(years), _('interval_format_tag::years::y')[-1:])
return "%s%s %s%s" % (int(years), _('interval_format_tag::years::y')[-1:], int(months), _('interval_format_tag::months::m')[-1:])
@@ -1536,22 +1537,6 @@ def str2pydt_matches(str2parse=None, patterns=None):
matches.extend(__single_char2py_dt(str2parse))
matches.extend(__explicit_offset2py_dt(str2parse))
- # no more with Python3
-# # try mxDT parsers
-# try:
-# date = mxDT.Parser.DateFromString (
-# text = str2parse,
-# formats = ('euro', 'iso', 'us', 'altus', 'altiso', 'lit', 'altlit', 'eurlit')
-# )
-# matches.append ({
-# 'data': mxdt2py_dt(date),
-# 'label': date.strftime('%Y-%m-%d')
-# })
-# except (ValueError, OverflowError):
-# pass
-# except mxDT.RangeError:
-# pass
-
# apply explicit patterns
if patterns is None:
patterns = []
@@ -1574,12 +1559,27 @@ def str2pydt_matches(str2parse=None, patterns=None):
patterns.append('%Y.%m.%d')
+ parts = str2parse.split(maxsplit = 1)
+ hour = 11
+ minute = 11
+ second = 11
+ if len(parts) > 1:
+ for pattern in ['%H:%M', '%H:%M:%S']:
+ try:
+ date = pyDT.datetime.strptime(parts[1], pattern)
+ hour = date.hour
+ minute = date.minute
+ second = date.second
+ break
+ except ValueError:
+ # C-level overflow
+ continue
for pattern in patterns:
try:
- date = pyDT.datetime.strptime(str2parse, pattern).replace (
- hour = 11,
- minute = 11,
- second = 11,
+ date = pyDT.datetime.strptime(parts[0], pattern).replace (
+ hour = hour,
+ minute = minute,
+ second = second,
tzinfo = gmCurrentLocalTimezone
)
matches.append ({
@@ -1590,6 +1590,7 @@ def str2pydt_matches(str2parse=None, patterns=None):
# C-level overflow
continue
+
return matches
#===========================================================================
@@ -1943,65 +1944,50 @@ def str2fuzzy_timestamp_matches(str2parse=None, default_time=None, patterns=None
} for m in __explicit_offset2py_dt(str2parse)
])
- # reactivate, once mxDT becomes available on Py3k
-# # try mxDT parsers
-# try:
-# # date ?
-# date_only = mxDT.Parser.DateFromString (
-# text = str2parse,
-# formats = ('euro', 'iso', 'us', 'altus', 'altiso', 'lit', 'altlit', 'eurlit')
-# )
-# # time, too ?
-# time_part = mxDT.Parser.TimeFromString(text = str2parse)
-# datetime = date_only + time_part
-# if datetime == date_only:
-# accuracy = acc_days
-# if isinstance(default_time, mxDT.DateTimeDeltaType):
-# datetime = date_only + default_time
-# accuracy = acc_minutes
-# else:
-# accuracy = acc_subseconds
-# fts = cFuzzyTimestamp (
-# timestamp = datetime,
-# accuracy = accuracy
-# )
-# matches.append ({
-# 'data': fts,
-# 'label': fts.format_accurately()
-# })
-# except ValueError:
-# pass
-# except mxDT.RangeError:
-# pass
-
if patterns is None:
patterns = []
patterns.extend([
- ['%Y-%m-%d', acc_days],
- ['%y-%m-%d', acc_days],
- ['%Y/%m/%d', acc_days],
- ['%y/%m/%d', acc_days],
-
- ['%d-%m-%Y', acc_days],
- ['%d-%m-%y', acc_days],
- ['%d/%m/%Y', acc_days],
- ['%d/%m/%y', acc_days],
- ['%d.%m.%Y', acc_days],
-
- ['%m-%d-%Y', acc_days],
- ['%m-%d-%y', acc_days],
- ['%m/%d/%Y', acc_days],
- ['%m/%d/%y', acc_days]
+ '%Y-%m-%d',
+ '%y-%m-%d',
+ '%Y/%m/%d',
+ '%y/%m/%d',
+ '%d-%m-%Y',
+ '%d-%m-%y',
+ '%d/%m/%Y',
+ '%d/%m/%y',
+ '%d.%m.%Y',
+ '%m-%d-%Y',
+ '%m-%d-%y',
+ '%m/%d/%Y',
+ '%m/%d/%y'
])
+
+ parts = str2parse.split(maxsplit = 1)
+ hour = 11
+ minute = 11
+ second = 11
+ acc = acc_days
+ if len(parts) > 1:
+ for pattern in ['%H:%M', '%H:%M:%S']:
+ try:
+ date = pyDT.datetime.strptime(parts[1], pattern)
+ hour = date.hour
+ minute = date.minute
+ second = date.second
+ acc = acc_minutes
+ break
+ except ValueError:
+ # C-level overflow
+ continue
for pattern in patterns:
try:
- ts = pyDT.datetime.strptime(str2parse, pattern[0]).replace (
- hour = 11,
- minute = 11,
- second = 11,
+ ts = pyDT.datetime.strptime(parts[0], pattern).replace (
+ hour = hour,
+ minute = minute,
+ second = second,
tzinfo = gmCurrentLocalTimezone
)
- fts = cFuzzyTimestamp(timestamp = ts, accuracy = pattern[1])
+ fts = cFuzzyTimestamp(timestamp = ts, accuracy = acc)
matches.append ({
'data': fts,
'label': fts.format_accurately()
@@ -2009,7 +1995,6 @@ def str2fuzzy_timestamp_matches(str2parse=None, default_time=None, patterns=None
except ValueError:
# C-level overflow
continue
-
return matches
#===========================================================================
@@ -2234,10 +2219,9 @@ if __name__ == '__main__':
pyDT.timedelta(days = 366),
pyDT.timedelta(days = 367),
pyDT.timedelta(days = 400),
- pyDT.timedelta(weeks = 52 * 30),
- pyDT.timedelta(weeks = 52 * 79, days = 33)
+ pyDT.timedelta(weeks = 53 * 30),
+ pyDT.timedelta(weeks = 53 * 79, days = 33)
]
-
idx = 1
for intv in intervals:
print ('%s) %s -> %s' % (idx, intv, format_interval_medically(intv)))
@@ -2424,7 +2408,7 @@ if __name__ == '__main__':
init()
#test_date_time()
- #test_str2fuzzy_timestamp_matches()
+ test_str2fuzzy_timestamp_matches()
#test_get_date_of_weekday_in_week_of_date()
#test_cFuzzyTimeStamp()
#test_get_pydt()
@@ -2434,6 +2418,6 @@ if __name__ == '__main__':
#test_str2pydt()
#test_pydt_strftime()
#test_calculate_apparent_age()
- test_is_leap_year()
+ #test_is_leap_year()
#===========================================================================
=====================================
server/pycommon/gmI18N.py
=====================================
@@ -237,10 +237,11 @@ def activate_locale():
_log.exception('Windows does not support locale.LC_ALL')
except Exception:
_log.exception('error activating user-default locale')
+ _log.log_stack_trace()
__log_locale_settings('locale settings after activating user-default locale')
# assume en_EN if we did not find any locale settings
if loc in [None, 'C']:
- _log.error('the current system locale is still [None] or [C], assuming [en_EN]')
+ _log.error('the current system locale is still [None] or [C], falling back to [en_EN]')
system_locale = "en_EN"
else:
system_locale = loc
=====================================
server/pycommon/gmNetworkTools.py
=====================================
@@ -326,7 +326,7 @@ def check_for_update(url=None, current_branch=None, current_version=None, consid
)
msg += '\n\n'
- msg += _('Details are found on <http://wiki.gnumed.de>.\n')
+ msg += _('Details are found on <http://www.gnumed.de>.\n')
msg += '\n'
msg += _('Version information loaded from:\n\n %s') % url
=====================================
server/pycommon/gmPG2.py
=====================================
@@ -916,7 +916,7 @@ def lock_row(link_obj=None, table=None, pk=None, exclusive=False):
cmd = """SELECT pg_try_advisory_lock('%s'::regclass::oid::int, %s)""" % (table, pk)
else:
cmd = """SELECT pg_try_advisory_lock_shared('%s'::regclass::oid::int, %s)""" % (table, pk)
- rows, idx = run_rw_queries(link_obj = link_obj, queries = [{'cmd': cmd}], get_col_idx = False, return_data = True)
+ rows, idx = run_ro_queries(link_obj = link_obj, queries = [{'cmd': cmd}], get_col_idx = False)
if rows[0][0]:
return True
@@ -934,7 +934,7 @@ def unlock_row(link_obj=None, table=None, pk=None, exclusive=False):
cmd = "SELECT pg_advisory_unlock('%s'::regclass::oid::int, %s)" % (table, pk)
else:
cmd = "SELECT pg_advisory_unlock_shared('%s'::regclass::oid::int, %s)" % (table, pk)
- rows, idx = run_rw_queries(link_obj = link_obj, queries = [{'cmd': cmd}], get_col_idx = False, return_data = True)
+ rows, idx = run_ro_queries(link_obj = link_obj, queries = [{'cmd': cmd}], get_col_idx = False)
if rows[0][0]:
return True
@@ -943,7 +943,7 @@ def unlock_row(link_obj=None, table=None, pk=None, exclusive=False):
#------------------------------------------------------------------------
def row_is_locked(table=None, pk=None):
- """Looks at pk_locks
+ """Looks at pg_locks.
- does not take into account locks other than 'advisory', however
"""
@@ -959,6 +959,7 @@ def row_is_locked(table=None, pk=None):
if rows[0][0]:
_log.debug('row is locked: [%s] [%s]', table, pk)
return True
+
_log.debug('row is NOT locked: [%s] [%s]', table, pk)
return False
@@ -2505,7 +2506,7 @@ SELECT to_timestamp (foofoo,'YYMMDD.HH24MI') FROM (
row_is_locked(table = 'dem.identity', pk = 12)
- print("1st connection:")
+ print("on 1st connection:")
print(" locked:", row_is_locked(table = 'dem.identity', pk = 12))
print(" 1st shared lock succeeded:", lock_row(table = 'dem.identity', pk = 12, exclusive = False))
print(" locked:", row_is_locked(table = 'dem.identity', pk = 12))
@@ -2517,20 +2518,20 @@ SELECT to_timestamp (foofoo,'YYMMDD.HH24MI') FROM (
print(" `-> unlock succeeded:", unlock_row(table = 'dem.identity', pk = 12, exclusive = True))
print(" locked:", row_is_locked(table = 'dem.identity', pk = 12))
- print("2nd connection:")
+ print("on 2nd connection:")
conn = get_raw_connection(readonly=True)
print(" shared lock should succeed:", lock_row(link_obj = conn, table = 'dem.identity', pk = 12, exclusive = False))
print(" `-> unlock succeeded:", unlock_row(link_obj = conn, table = 'dem.identity', pk = 12, exclusive = False))
print(" locked:", row_is_locked(table = 'dem.identity', pk = 12))
- print(" exclusive lock succeeded ?", lock_row(link_obj = conn, table = 'dem.identity', pk = 12, exclusive = True), "(should fail)")
+ print(" exclusive lock succeeded:", lock_row(link_obj = conn, table = 'dem.identity', pk = 12, exclusive = True), "(should be False)")
print(" locked:", row_is_locked(table = 'dem.identity', pk = 12))
- print("1st connection:")
+ print("on 1st connection again:")
print(" unlock succeeded:", unlock_row(table = 'dem.identity', pk = 12, exclusive = False))
print(" locked:", row_is_locked(table = 'dem.identity', pk = 12))
- print("2nd connection:")
- print(" exclusive lock should succeed", lock_row(link_obj = conn, table = 'dem.identity', pk = 12, exclusive = True))
+ print("on 2nd connection:")
+ print(" exclusive lock should succeed:", lock_row(link_obj = conn, table = 'dem.identity', pk = 12, exclusive = True))
print(" locked:", row_is_locked(table = 'dem.identity', pk = 12))
print(" shared lock should succeed:", lock_row(link_obj = conn, table = 'dem.identity', pk = 12, exclusive = False))
print(" `-> unlock succeeded:", unlock_row(link_obj = conn, table = 'dem.identity', pk = 12, exclusive = False))
@@ -2619,9 +2620,9 @@ SELECT to_timestamp (foofoo,'YYMMDD.HH24MI') FROM (
#test_run_query()
#test_schema_exists()
#test_get_foreign_key_names()
- #test_row_locks()
+ test_row_locks()
#test_faulty_SQL()
#test_log_settings()
- test_get_db_fingerprint()
+ #test_get_db_fingerprint()
# ======================================================================
=====================================
server/pycommon/gmTools.py
=====================================
@@ -1310,38 +1310,48 @@ def xml_escape_string(text=None):
return xml_tools.escape(text)
#---------------------------------------------------------------------------
-def tex_escape_string(text=None, replace_known_unicode=True, replace_eol=False, keep_visual_eol=False):
+def tex_escape_string(text:str=None, replace_known_unicode:bool=True, replace_eol:bool=False, keep_visual_eol:bool=False) -> str:
"""Check for special TeX characters and transform them.
- replace_eol:
- replaces "\n" with "\\newline"
- keep_visual_eol:
- replaces "\n" with "\\newline \n" such that
+ Args:
+ text: plain (unicode) text to escape for LaTeX processing,
+ note that any valid LaTeX code contained within will be
+ escaped, too
+ replace_eol: replaces "\n" with "\\newline{}"
+ keep_visual_eol: replaces "\n" with "\\newline{}%\n" such that
both LaTeX will know to place a line break
at this point as well as the visual formatting
is preserved in the LaTeX source (think multi-
row table cells)
+
+ Returns:
"""
- text = text.replace('\\', '\\textbackslash') # requires \usepackage{textcomp} in LaTeX source
- text = text.replace('^', '\\textasciicircum')
- text = text.replace('~', '\\textasciitilde')
-
- text = text.replace('{', '\\{')
- text = text.replace('}', '\\}')
- text = text.replace('%', '\\%')
- text = text.replace('&', '\\&')
- text = text.replace('#', '\\#')
- text = text.replace('$', '\\$')
- text = text.replace('_', '\\_')
+ # must happen first
+ text = text.replace('{', '-----{{{{{-----')
+ text = text.replace('}', '-----}}}}}-----')
+
+ text = text.replace('\\', '\\textbackslash{}') # requires \usepackage{textcomp} in LaTeX source
+
+ text = text.replace('-----{{{{{-----', '\\{{}')
+ text = text.replace('-----}}}}}-----', '\\}{}')
+
+ text = text.replace('^', '\\textasciicircum{}')
+ text = text.replace('~', '\\textasciitilde{}')
+
+ text = text.replace('%', '\\%{}')
+ text = text.replace('&', '\\&{}')
+ text = text.replace('#', '\\#{}')
+ text = text.replace('$', '\\${}')
+ text = text.replace('_', '\\_{}')
if replace_eol:
if keep_visual_eol:
- text = text.replace('\n', '\\newline \n')
+ text = text.replace('\n', '\\newline{}%\n')
else:
- text = text.replace('\n', '\\newline ')
+ text = text.replace('\n', '\\newline{}')
if replace_known_unicode:
# this should NOT be replaced for Xe(La)Tex
- text = text.replace(u_euro, '\\EUR') # requires \usepackage{textcomp} in LaTeX source
+ text = text.replace(u_euro, '\\euro{}') # requires \usepackage[official]{eurosym} in LaTeX source
text = text.replace(u_sum, '$\\Sigma$')
return text
@@ -2467,7 +2477,7 @@ second line\n
#test_xml_escape()
#test_strip_trailing_empty_lines()
#test_fname_stem()
- #test_tex_escape()
+ test_tex_escape()
#test_rst2latex_snippet()
#test_dir_is_empty()
#test_compare_dicts()
@@ -2482,7 +2492,7 @@ second line\n
#test_enumerate_optical_writers()
#test_copy_tree_content()
#test_mk_sandbox_dir()
- test_make_table_from_dicts()
+ #test_make_table_from_dicts()
#test_decorate_window_title()
#===========================================================================
=====================================
server/sql/v21-v22/dynamic/v22-clin-remove_old_empty_encounters.sql
=====================================
@@ -46,6 +46,9 @@ BEGIN
DELETE FROM clin.encounter WHERE
clin.encounter.fk_patient = _pk_identity
AND
+ -- is this the active encounter somewhere ?
+ (SELECT pg_try_advisory_lock(''clin.encounter''::regclass::oid::int, clin.encounter.pk))
+ AND
age(clin.encounter.last_affirmed) > _usable_minimum_encounter_age
AND
NOT EXISTS (SELECT 1 FROM clin.clin_root_item WHERE fk_encounter = clin.encounter.pk)
@@ -65,11 +68,11 @@ BEGIN
NOT EXISTS (SELECT 1 FROM clin.suppressed_hint WHERE fk_encounter = clin.encounter.pk)
;
- return true;
+ RETURN TRUE;
END;';
comment on function clin.remove_old_empty_encounters(integer, interval) is
'Remove empty encounters older than a definable minimum age from a patient.';
-- --------------------------------------------------------------
-select gm.log_script_insertion('v22-clin-remove_old_empty_encounters.sql', '22.0');
+select gm.log_script_insertion('v22-clin-remove_old_empty_encounters.sql', '22.16');
=====================================
server/sql/v21-v22/fixups/v22-clin-remove_old_empty_encounters-fixup.sql
=====================================
@@ -0,0 +1,78 @@
+-- ==============================================================
+-- GNUmed database schema change script
+--
+-- License: GPL v2 or later
+-- Author: karsten.hilbert at gmx.net
+--
+-- ==============================================================
+-- force terminate + exit(3) on errors if non-interactive
+\set ON_ERROR_STOP 1
+
+--set default_transaction_read_only to off;
+set check_function_bodies to on;
+
+-- --------------------------------------------------------------
+create or replace function clin.remove_old_empty_encounters(integer, interval)
+ returns boolean
+ language 'plpgsql'
+ security definer
+ as '
+DECLARE
+ _pk_identity alias for $1;
+ _defined_minimum_encounter_age alias for $2;
+ _usable_minimum_encounter_age interval;
+ _no_of_encounters integer;
+BEGIN
+ -- does person exist ?
+ perform 1 from dem.identity where pk = _pk_identity;
+ if not found then
+ raise exception ''clin.remove_old_empty_encounters(person=%, min_age=%): person [%] does not exist'', _pk_identity, _defined_minimum_encounter_age, _pk_identity;
+ return false;
+ end if;
+
+ SELECT count(1) INTO STRICT _no_of_encounters
+ FROM clin.encounter WHERE fk_patient = _pk_identity;
+ IF _no_of_encounters < 2 THEN
+ raise notice ''clin.remove_old_empty_encounters(person=%, min_age=%): there are less than 2 encounters for this patient'', _pk_identity, _defined_minimum_encounter_age;
+ return false;
+ END IF;
+
+ if _defined_minimum_encounter_age < ''3 days''::interval then
+ _usable_minimum_encounter_age := ''3 days''::interval;
+ else
+ _usable_minimum_encounter_age := _defined_minimum_encounter_age;
+ end if;
+
+ DELETE FROM clin.encounter WHERE
+ clin.encounter.fk_patient = _pk_identity
+ AND
+ -- is this the active encounter somewhere ?
+ (SELECT pg_try_advisory_lock(''clin.encounter''::regclass::oid::int, clin.encounter.pk))
+ AND
+ age(clin.encounter.last_affirmed) > _usable_minimum_encounter_age
+ AND
+ NOT EXISTS (SELECT 1 FROM clin.clin_root_item WHERE fk_encounter = clin.encounter.pk)
+ AND
+ NOT EXISTS (SELECT 1 FROM blobs.doc_med WHERE fk_encounter = clin.encounter.pk)
+ AND
+ NOT EXISTS (SELECT 1 FROM clin.episode WHERE fk_encounter = clin.encounter.pk)
+ AND
+ NOT EXISTS (SELECT 1 FROM clin.health_issue WHERE fk_encounter = clin.encounter.pk)
+ AND
+ NOT EXISTS (SELECT 1 FROM clin.allergy_state WHERE fk_encounter = clin.encounter.pk)
+ AND
+ NOT EXISTS (SELECT 1 FROM bill.bill_item WHERE fk_encounter = clin.encounter.pk)
+ AND
+ NOT EXISTS (SELECT 1 FROM clin.external_care WHERE fk_encounter = clin.encounter.pk)
+ AND
+ NOT EXISTS (SELECT 1 FROM clin.suppressed_hint WHERE fk_encounter = clin.encounter.pk)
+ ;
+
+ RETURN TRUE;
+END;';
+
+comment on function clin.remove_old_empty_encounters(integer, interval) is
+ 'Remove empty encounters older than a definable minimum age from a patient.';
+
+-- --------------------------------------------------------------
+select gm.log_script_insertion('v22-clin-remove_old_empty_encounters-fixup.sql', '22.16');
=====================================
server/sql/v21-v22/fixups/v22-release_notes-fixup.sql
=====================================
@@ -17,22 +17,38 @@ INSERT INTO dem.message_inbox (
) VALUES (
(select pk from dem.staff where db_user = 'any-doc'),
(select pk_type from dem.v_inbox_item_type where type = 'memo' and category = 'administrative'),
- 'Release Notes for GNUmed 1.8.5 (database v22.15)',
- 'GNUmed 1.8.5 Release Notes:
+ 'Release Notes for GNUmed 1.8.6 (database v22.16)',
+ 'GNUmed 1.8.6 Release Notes:
- 1.8.5
+ 1.8.6
-FIX: image conversion w/o target extension [thanks ...]
-FIX: SVG icon [thanks freddii]
-FIX: lab: tab "most-recent": [x]ing "show missing" throws exception
-FIX: log: exception on inaccessible attributes
+FIX: unlocking encounters (missing import)
+FIX: row locking code
+FIX: metadata: screenshots URL in appdata.xml
+FIX: GUI: screenshot function [thanks The-o]
+FIX: medical interval formatting [thanks brulefa]
+FIX: date/time input: time parsing
+FIX: date/time input: function key to weekday mapping
+FIX: encounters: do not crash if current encounter is deleted
+FIX: paperwork: fix emr_journal placeholder LaTeX escaping [thanks Marc]
-NEW: add CoVid-2019 vaccines
+IMPROVED: desktop entry file [thanks freddii]
+IMPROVED: patient switch: encounter checking
+IMPROVED: pat overview: problems display
+IMPROVED: DB: connection loss detection
+IMPROVED: measurement EA: previous result formatting
+IMPROVED: DB: login problem logging
+IMPROVED: top pane: show age of results in tooltip
+IMPROVED: paperwork: LaTeX formatting of text
+IMPROVED: EMR: extend search to select demographics [thanks brulefa]
+IMPROVED: startup: bash code [thanks shellcheck]
- 22.15
+ 22.16
-FIX: message inbox view
+IMPROVED: bootstrapper: check disk space
+
+FIX: check encounter lock in clin.remove_old_empty_encounters()
');
-- --------------------------------------------------------------
-select gm.log_script_insertion('v22-release_notes-fixup.sql', '22.15 at 1.8.5');
+select gm.log_script_insertion('v22-release_notes-fixup.sql', '22.16 at 1.8.6');
View it on GitLab: https://salsa.debian.org/med-team/gnumed-server/-/commit/1f27a818b40b211c3fff88179ed413b1f0faae3f
--
View it on GitLab: https://salsa.debian.org/med-team/gnumed-server/-/commit/1f27a818b40b211c3fff88179ed413b1f0faae3f
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20210829/a2e7a02d/attachment-0001.htm>
More information about the debian-med-commit
mailing list