From ff36c5ad029764de790ee995d5083d47610e3555 Mon Sep 17 00:00:00 2001 From: sax Date: Wed, 19 Feb 2020 08:41:42 -0500 Subject: [PATCH 01/14] updates CHANGES --- CHANGES | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGES b/CHANGES index 3d33a0696..991fe914a 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,17 @@ ---- * fixes #18276 - Add `Achievement in reporting period` field to the prp/datareport endpoint + +2.10.3 +------ +* fixes Intervention.frs + + +2.10.2 +------ +* fixes DataReportLoader + + 2.10.1 ------ * use poetry instead of pipenv From e25e1cc90f2441ade7ed22ceb01b6ddb67d853a4 Mon Sep 17 00:00:00 2001 From: ntrncic Date: Fri, 21 Feb 2020 10:40:26 -0500 Subject: [PATCH 02/14] add chars length to field --- src/etools_datamart/apps/mart/data/models/intervention.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/etools_datamart/apps/mart/data/models/intervention.py b/src/etools_datamart/apps/mart/data/models/intervention.py index 6b5ab33e6..aeb5a936b 100644 --- a/src/etools_datamart/apps/mart/data/models/intervention.py +++ b/src/etools_datamart/apps/mart/data/models/intervention.py @@ -72,7 +72,7 @@ class InterventionAbstract(models.Model): partner_signatory_first_name = models.CharField(max_length=64, null=True) partner_signatory_last_name = models.CharField(max_length=64, null=True) partner_signatory_phone = models.CharField(max_length=64, null=True) - partner_signatory_title = models.CharField(max_length=64, null=True) + partner_signatory_title = models.CharField(max_length=100, null=True) partner_source_id = models.IntegerField(blank=True, null=True) partner_type = models.CharField(max_length=64, null=True) partner_vendor_number = models.CharField(max_length=100, blank=True, null=True) From 1b3228394a17f415043b3f68e81f9c51d5aaa010 Mon Sep 17 00:00:00 2001 From: ntrncic Date: Fri, 21 Feb 2020 14:25:19 -0500 Subject: [PATCH 03/14] add chars length to field --- docker/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Makefile b/docker/Makefile index 4b5c2b4bd..81d424dd5 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -4,7 +4,7 @@ DATABASE_URL_ETOOLS?= DEVELOP?=1 DOCKER_PASS?= DOCKER_USER?= -TARGET?=2.11 +TARGET?=2.11.1a PUSH_BASE?=1 BASE?=$(shell echo "${TARGET}" | sed "s/\([0-9]*\)\.\([0-9]*\)\.\(.*\)/\1.\2/g" ) #BASE?=$(shell echo "${TARGET}" | sed "s/\([0-9]*\)\.\([0-9]*\)\.\(.*\)/\1.\2/g" | echo "`xargs`xx" ) From b8bee7b021789544c995440a1fc0375e49c820d1 Mon Sep 17 00:00:00 2001 From: Domenico DiNicola Date: Fri, 21 Feb 2020 14:27:09 -0500 Subject: [PATCH 04/14] add migration --- .gitignore | 1 + .../migrations/0111_auto_20200221_1925.py | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/etools_datamart/apps/mart/data/migrations/0111_auto_20200221_1925.py diff --git a/.gitignore b/.gitignore index b797096ed..92a7218f8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ .coverage .DS_Store .env +.envrc .idea .pydevproject .tox diff --git a/src/etools_datamart/apps/mart/data/migrations/0111_auto_20200221_1925.py b/src/etools_datamart/apps/mart/data/migrations/0111_auto_20200221_1925.py new file mode 100644 index 000000000..9b45f9b56 --- /dev/null +++ b/src/etools_datamart/apps/mart/data/migrations/0111_auto_20200221_1925.py @@ -0,0 +1,28 @@ +# Generated by Django 2.2.9 on 2020-02-21 19:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('data', '0110_auto_20191223_0126'), + ] + + operations = [ + migrations.AlterField( + model_name='intervention', + name='partner_signatory_title', + field=models.CharField(max_length=100, null=True), + ), + migrations.AlterField( + model_name='interventionbudget', + name='partner_signatory_title', + field=models.CharField(max_length=100, null=True), + ), + migrations.AlterField( + model_name='interventionbylocation', + name='partner_signatory_title', + field=models.CharField(max_length=100, null=True), + ), + ] From 73406835813618d0305a5c6e747979a4149fb94d Mon Sep 17 00:00:00 2001 From: sax Date: Wed, 25 Mar 2020 18:19:13 +0100 Subject: [PATCH 05/14] docker-composer --- .bumpversion.cfg | 1 + db/Makefile | 40 +++++++++++++--------- db/README.md | 28 ++++++++++++++++ db/load_db_data.sh | 2 +- docker-compose.yml | 83 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 137 insertions(+), 17 deletions(-) create mode 100644 db/README.md create mode 100644 docker-compose.yml diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 744a13b31..b3e45fc01 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -27,3 +27,4 @@ values = [bumpversion:file:pyproject.toml] +[bumpversion:file:docker-compose.yml] diff --git a/db/Makefile b/db/Makefile index a7f08bf05..dbe704321 100644 --- a/db/Makefile +++ b/db/Makefile @@ -1,39 +1,47 @@ +DB_DUMP_LOCATION?=/tmp/psql_data/db2.bz2 +DOCKER_IMAGE=unicef/etools-db:dev2 +VOLUME_ETOOLS_DATA?=/Users/Shared/data/storage/etools-db/data +VOLUME_ETOOLS_BACKUP?=/Users/Shared/data/storage/etools-db/dumps + build: docker build \ - -t unicef/etools-db:dev \ + -t ${DOCKER_IMAGE} \ -f Dockerfile . clean: - docker rmi unicef/etools-db:dev + docker rmi ${DOCKER_IMAGE} init: docker run --rm -it \ - -e DB_DUMP_LOCATION=/tmp/psql_data/db2.bz2 \ - -v /data/storage/etools-db/dumps:/tmp/psql_data/ \ - -v /data/storage/etools-db/data:/var/lib/postgresql/data \ - unicef/etools-db:dev + -e DB_DUMP_LOCATION=${DB_DUMP_LOCATION} \ + -v ${VOLUME_ETOOLS_BACKUP}:/tmp/psql_data/ \ + -v ${VOLUME_ETOOLS_DATA}:/var/lib/postgresql/data \ + ${DOCKER_IMAGE} restore: docker run --rm -it \ - -e DB_DUMP_LOCATION=/tmp/psql_data/db2.bz2 \ - -v /data/storage/etools-db/dumps:/tmp/psql_data/ \ - -v /data/storage/etools-db/data:/var/lib/postgresql/data \ - unicef/etools-db:dev \ + -e DB_DUMP_LOCATION=${DB_DUMP_LOCATION} \ + -v ${VOLUME_ETOOLS_BACKUP}:/tmp/psql_data/ \ + -v ${VOLUME_ETOOLS_DATA}:/var/lib/postgresql/data \ + ${DOCKER_IMAGE} \ /usr/local/bin/restore-db.sh run: docker run --rm \ -p 15432:5432 -it \ - -v /data/storage/etools-db/data:/var/lib/postgresql/data \ - unicef/etools-db:dev + -v ${VOLUME_ETOOLS_DATA}:/var/lib/postgresql/data \ + ${DOCKER_IMAGE} + +stop: + docker stop ${DOCKER_IMAGE} shell: docker run --rm \ -it \ - -e DB_DUMP_LOCATION=/tmp/psql_data/db2.bz2 \ - -v /data/storage/etools-db/dumps:/tmp/psql_data/ \ - -v /data/storage/etools-db/data:/var/lib/postgresql/data \ - unicef/etools-db:dev \ + -e DB_DUMP_LOCATION=${DB_DUMP_LOCATION} \ + -v ${VOLUME_ETOOLS_BACKUP}:/tmp/psql_data/ \ + -v ${VOLUME_ETOOLS_DATA}:/var/lib/postgresql/data \ + ${DOCKER_IMAGE} \ /bin/bash diff --git a/db/README.md b/db/README.md new file mode 100644 index 000000000..01cfd6c5d --- /dev/null +++ b/db/README.md @@ -0,0 +1,28 @@ +## Datamart eTools database + +This directory contains files and scripts needed to: + +- extract data from eTools backup +- create .sql files for test data +- builds docker image for the eTools test database + +- keep database updated to eTools schema + +**Read Makefile to check configuration variables. Consider your changes reading this file** + +### Build initial eTools test database : + +- get a .bz2 backup file from etools and name it `db2.bz2` +- copy somewhere and set `VOLUME_BACKUP` environment variable. + (it must point to the directory) +- run `make build init` + +### Run migrations: + +To keep test database updated without the need to rebuild it from a backup, you can run migrations +using a eTools docker image against the test database. +Simply run: + + - make run + - DATABASE_URL_ETOOLS=postgis://postgres:@127.0.0.1:5432/etools migrate-etools.sh` + - make stop diff --git a/db/load_db_data.sh b/db/load_db_data.sh index 92bc67ff0..7dc45f154 100755 --- a/db/load_db_data.sh +++ b/db/load_db_data.sh @@ -2,7 +2,7 @@ set -e - +echo "Loding db data" # Perform all actions as $POSTGRES_USER export PGUSER="$POSTGRES_USER" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..71d79b325 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,83 @@ +version: '2' +services: +# proxy: +# build: ./proxy +# ports: +# - 8000:8000 +# depends_on: +# - datamart +# - flower + +# base: +# build: +# context: . +# dockerfile: ./docker/Dockerfile.alpine.base +# args: +# VERSION: "x.x" + +# datamart: +# build: +# context: . +# dockerfile: ./docker/Dockerfile.alpine +# args: +# BASE_IMAGE: "2.11-base" +# environment: &environment +# AUTOCREATE_USERS: "admin,123" +# BASE_IMAGE: "compose" +# CACHE_URL: "redis://redis:6379/1" +# CACHE_URL_LOCK: "redis://redis:6379/1" +# CACHE_URL_API: "redis://redis:6379/1" +# CACHE_URL_TEMPLATE: "redis://redis:6379/1" +# CELERY_BROKER_URL: "redis://redis:6379/1" +# CELERY_RESULT_BACKEND: "redis://redis:6379/1" +## CELERY_BROKER_URL: "amqp://guest:guest@rabbitmq/datamart" +## CELERY_RESULT_BACKEND: "amqp://guest:guest@rabbitmq/datamart" +# CSRF_COOKIE_SECURE: 0 +# DATABASE_URL: postgis://postgres:@db:5432/etools_datamart +# DATABASE_URL_ETOOLS: postgis://postgres:@etools:5432/etools +# DEBUG: 0 +# SECRET_KEY: kuhfjhgfuytfuytfuygfuytdfuydfuygdfuygdfuytf +# SECURE_BROWSER_XSS_FILTER: 0 +# SECURE_CONTENT_TYPE_NOSNIFF: 0 +# SECURE_FRAME_DENY: 0 +# SECURE_HSTS_PRELOAD: 0 +# SECURE_HSTS_SECONDS: 0 +# SECURE_SSL_REDIRECT: 0 +# SESSION_COOKIE_HTTPONLY: 0 +# SESSION_COOKIE_SECURE: 0 +# STATIC_ROOT: /var/datamart/static/ +# STATIC_URL: /dm-static/ +# command: datamart +# ports: +# - 9999:8000 +# depends_on: +# - db +# - etools +# - redis + + db: + image: mdillon/postgis:9.6 + environment: + POSTGRES_PASSWORD: + POSTGRES_USER: postgres + POSTGRES_DB: etools_datamart + volumes: + - "$PWD/~build/db:/var/lib/postgresql/data" + ports: + - 25432:5432 + + etools: + image: mdillon/postgis:9.6 + ports: + - "15432:5432" + shm_size: '1gb' + environment: + POSTGRES_PASSWORD: + POSTGRES_USER: postgres + volumes: + - ${VOLUME_ETOOLS_DATA}:/var/lib/postgresql/data + + + redis: + image: redis:alpine + From ac8bcf81c0aa1dcc398daf74d4efab57ee89cab7 Mon Sep 17 00:00:00 2001 From: Greg Reinbach Date: Thu, 2 Apr 2020 10:03:32 -0400 Subject: [PATCH 06/14] Add disaggregation to PRP DataReport endpoint Replacing disaggregation ids with matching value --- src/etools_datamart/apps/mart/prp/models.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/etools_datamart/apps/mart/prp/models.py b/src/etools_datamart/apps/mart/prp/models.py index 2ed9d6472..f60ee8a0c 100644 --- a/src/etools_datamart/apps/mart/prp/models.py +++ b/src/etools_datamart/apps/mart/prp/models.py @@ -55,7 +55,7 @@ from etools_datamart.apps.etl.exceptions import MaxRecordsException, RequiredIsMissing, RequiredIsRunning from etools_datamart.apps.etl.loader import BaseLoader, EtlResult, logger, RUN_UNKNOWN -from etools_datamart.apps.sources.source_prp.models import (CoreCountry, CoreGatewaytype, +from etools_datamart.apps.sources.source_prp.models import (CoreCountry, CoreGatewaytype, IndicatorDisaggregationvalue, IndicatorIndicatorlocationdata, IndicatorIndicatorreport, IndicatorReportable, IndicatorReportablelocationgoal, UnicefLowerleveloutput, UnicefPdresultlink, @@ -302,6 +302,21 @@ def get_progress_report(self, record: IndicatorIndicatorlocationdata, values, ** pr.id, pd.title, pr.start_date, pr.end_date ) + def get_disaggregation(self, record: IndicatorIndicatorlocationdata, values, **kwargs): + # get disaggregation data and replace keys with disaggration value + disaggKeyValues = {} + for pk, value in IndicatorDisaggregationvalue.objects.values_list("external_id", "value").all(): + disaggKeyValues[pk] = value + disagg = {} + for k, v in record.disaggregation: + if len(k): + nk = [] + for i in k: + nk.append(disaggKeyValues[i]) + k = tuple(nk) + disagg[k] = v + return disagg + class DataReport(PrpDataMartModel): # | indicator_report | idl.indicator_report | @@ -400,6 +415,7 @@ class DataReport(PrpDataMartModel): # total_cumulative_progress | reportable.total["c"] | total_cumulative_progress = models.CharField(max_length=2048, blank=True, null=True) achievement_in_reporting_period = models.CharField(max_length=2048, blank=True, null=True) + disaggregation = models.TextField(blank=True, null=True) loader = DataReportLoader() @@ -450,5 +466,5 @@ class Options: 'total_cumulative_progress_in_location': 'N/A', 'total_cumulative_progress': '-', 'achievement_in_reporting_period': '-', - + 'disaggregation': '-', } From 5bbe41ae923192ffc4c1ec554f421004cd627646 Mon Sep 17 00:00:00 2001 From: Greg Reinbach Date: Thu, 2 Apr 2020 10:08:13 -0400 Subject: [PATCH 07/14] Correct disaggregation field type in prp datareport --- src/etools_datamart/apps/mart/prp/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/etools_datamart/apps/mart/prp/models.py b/src/etools_datamart/apps/mart/prp/models.py index f60ee8a0c..3511e9b50 100644 --- a/src/etools_datamart/apps/mart/prp/models.py +++ b/src/etools_datamart/apps/mart/prp/models.py @@ -415,7 +415,7 @@ class DataReport(PrpDataMartModel): # total_cumulative_progress | reportable.total["c"] | total_cumulative_progress = models.CharField(max_length=2048, blank=True, null=True) achievement_in_reporting_period = models.CharField(max_length=2048, blank=True, null=True) - disaggregation = models.TextField(blank=True, null=True) + disaggregation = JSONField(blank=True, null=True) loader = DataReportLoader() From c40cf340b3c25b97051b57c570b09bc29d46a45c Mon Sep 17 00:00:00 2001 From: Greg Reinbach Date: Thu, 2 Apr 2020 10:18:16 -0400 Subject: [PATCH 08/14] Add datareport migration for disaggregation --- .../0008_datareport_disaggregation.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/etools_datamart/apps/mart/prp/migrations/0008_datareport_disaggregation.py diff --git a/src/etools_datamart/apps/mart/prp/migrations/0008_datareport_disaggregation.py b/src/etools_datamart/apps/mart/prp/migrations/0008_datareport_disaggregation.py new file mode 100644 index 000000000..1b4fdb2b1 --- /dev/null +++ b/src/etools_datamart/apps/mart/prp/migrations/0008_datareport_disaggregation.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.11 on 2020-04-02 14:17 + +import django.contrib.postgres.fields.jsonb +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('prp', '0007_datareport_achievement_in_reporting_period'), + ] + + operations = [ + migrations.AddField( + model_name='datareport', + name='disaggregation', + field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True), + ), + ] From 42d73f174ad5da10d31dc893a1bf3f831485942f Mon Sep 17 00:00:00 2001 From: Greg Reinbach Date: Fri, 3 Apr 2020 11:10:49 -0400 Subject: [PATCH 09/14] Fix handling of keys in disaggregation loader method --- src/etools_datamart/apps/mart/prp/models.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/etools_datamart/apps/mart/prp/models.py b/src/etools_datamart/apps/mart/prp/models.py index 3511e9b50..b79cf4c72 100644 --- a/src/etools_datamart/apps/mart/prp/models.py +++ b/src/etools_datamart/apps/mart/prp/models.py @@ -45,6 +45,8 @@ Utilised budget(same conditions as above) Report # """ +from ast import literal_eval + from django.contrib.postgres.fields import JSONField from django.db import models, transaction from django.db.models import Q @@ -306,15 +308,19 @@ def get_disaggregation(self, record: IndicatorIndicatorlocationdata, values, **k # get disaggregation data and replace keys with disaggration value disaggKeyValues = {} for pk, value in IndicatorDisaggregationvalue.objects.values_list("external_id", "value").all(): - disaggKeyValues[pk] = value + disaggKeyValues[int(pk)] = value disagg = {} - for k, v in record.disaggregation: + for k, v in record.disaggregation.items(): + k = literal_eval(k) if len(k): nk = [] for i in k: - nk.append(disaggKeyValues[i]) + try: + nk.append(disaggKeyValues[int(i)]) + except KeyError: + nk.append(int(i)) k = tuple(nk) - disagg[k] = v + disagg[str(k)] = v return disagg From ba0766b152ef0426a4508f7d8be6633baade5795 Mon Sep 17 00:00:00 2001 From: Greg Reinbach Date: Fri, 3 Apr 2020 12:30:25 -0400 Subject: [PATCH 10/14] Handle no values in IndicatorDisaggregationvalue --- src/etools_datamart/apps/mart/prp/models.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/etools_datamart/apps/mart/prp/models.py b/src/etools_datamart/apps/mart/prp/models.py index b79cf4c72..da32b57e5 100644 --- a/src/etools_datamart/apps/mart/prp/models.py +++ b/src/etools_datamart/apps/mart/prp/models.py @@ -308,6 +308,11 @@ def get_disaggregation(self, record: IndicatorIndicatorlocationdata, values, **k # get disaggregation data and replace keys with disaggration value disaggKeyValues = {} for pk, value in IndicatorDisaggregationvalue.objects.values_list("external_id", "value").all(): + try: + pk = int(pk) + except TypeError: + # probably a None, so we can ignore + pass disaggKeyValues[int(pk)] = value disagg = {} for k, v in record.disaggregation.items(): From 8c071c60ac1c60a60a7e647730e38d915ef40156 Mon Sep 17 00:00:00 2001 From: Greg Reinbach Date: Fri, 3 Apr 2020 12:35:30 -0400 Subject: [PATCH 11/14] Use pk, already attempted conversion to int --- src/etools_datamart/apps/mart/prp/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/etools_datamart/apps/mart/prp/models.py b/src/etools_datamart/apps/mart/prp/models.py index da32b57e5..c6ac554d4 100644 --- a/src/etools_datamart/apps/mart/prp/models.py +++ b/src/etools_datamart/apps/mart/prp/models.py @@ -313,7 +313,7 @@ def get_disaggregation(self, record: IndicatorIndicatorlocationdata, values, **k except TypeError: # probably a None, so we can ignore pass - disaggKeyValues[int(pk)] = value + disaggKeyValues[pk] = value disagg = {} for k, v in record.disaggregation.items(): k = literal_eval(k) From 54ef7edc44ad2c5f83311414c7cb0314b04fc7d4 Mon Sep 17 00:00:00 2001 From: Greg Reinbach Date: Fri, 3 Apr 2020 13:21:53 -0400 Subject: [PATCH 12/14] Update get_disaggregation method to use pk for values --- src/etools_datamart/apps/mart/prp/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/etools_datamart/apps/mart/prp/models.py b/src/etools_datamart/apps/mart/prp/models.py index c6ac554d4..8720bf7b7 100644 --- a/src/etools_datamart/apps/mart/prp/models.py +++ b/src/etools_datamart/apps/mart/prp/models.py @@ -307,7 +307,7 @@ def get_progress_report(self, record: IndicatorIndicatorlocationdata, values, ** def get_disaggregation(self, record: IndicatorIndicatorlocationdata, values, **kwargs): # get disaggregation data and replace keys with disaggration value disaggKeyValues = {} - for pk, value in IndicatorDisaggregationvalue.objects.values_list("external_id", "value").all(): + for pk, value in IndicatorDisaggregationvalue.objects.values_list("pk", "value").all(): try: pk = int(pk) except TypeError: From 54652410c12aaca874e50b9d9fe375cbcc831f78 Mon Sep 17 00:00:00 2001 From: Greg Reinbach Date: Fri, 3 Apr 2020 14:54:00 -0400 Subject: [PATCH 13/14] Add build dir to both docker and git ignore --- .dockerignore | 1 + .gitignore | 1 + 2 files changed, 2 insertions(+) diff --git a/.dockerignore b/.dockerignore index 22429a5a7..9b092d8be 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,3 +3,4 @@ db docs tests +build diff --git a/.gitignore b/.gitignore index 92a7218f8..cd2356f0b 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,4 @@ xml ~* **/dual-listbox-master/ #src/etools_datamart/apps/security/static/security/dual-listbox-master +build/ From cf5d6002b3f775696df9a7d435dbf9dc63833983 Mon Sep 17 00:00:00 2001 From: Greg Reinbach Date: Fri, 3 Apr 2020 14:54:16 -0400 Subject: [PATCH 14/14] Bump version to 2.12 --- src/etools_datamart/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/etools_datamart/__init__.py b/src/etools_datamart/__init__.py index d28a9926d..31289bde7 100644 --- a/src/etools_datamart/__init__.py +++ b/src/etools_datamart/__init__.py @@ -1,7 +1,7 @@ import warnings NAME = 'etools-datamart' -VERSION = __version__ = '2.11' +VERSION = __version__ = '2.12' __author__ = '' # UserWarning: The psycopg2 wheel package will be renamed from release 2.11;