From 6595a07cfc9d6df4267bc0eadbc340d08d4844b9 Mon Sep 17 00:00:00 2001
From: František Zatloukal <fzatlouk@redhat.com>
Date: Nov 28 2018 09:04:13 +0000
Subject: [PATCH 1/6] Make code Python 3 compatible


---

diff --git a/execdb/__init__.py b/execdb/__init__.py
index 848ff62..74a34dd 100644
--- a/execdb/__init__.py
+++ b/execdb/__init__.py
@@ -18,8 +18,8 @@
 #    Josef Skladanka <jskladan@redhat.com>
 
 from flask import Flask, render_template
-from flask.ext.login import LoginManager
-from flask.ext.sqlalchemy import SQLAlchemy
+from flask_login import LoginManager
+from flask_sqlalchemy import SQLAlchemy
 
 import logging
 import os
@@ -69,7 +69,7 @@ def setup_logging():
     root_logger.setLevel(logging.DEBUG)
 
     if app.config['STREAM_LOGGING']:
-        print "doing stream logging"
+        print("doing stream logging")
         stream_handler = logging.StreamHandler()
         stream_handler.setLevel(loglevel)
         stream_handler.setFormatter(formatter)
@@ -77,7 +77,7 @@ def setup_logging():
         app.logger.addHandler(stream_handler)
 
     if app.config['SYSLOG_LOGGING']:
-        print "doing syslog logging"
+        print("doing syslog logging")
         syslog_handler = logging.handlers.SysLogHandler(
             address='/dev/log',
             facility=logging.handlers.SysLogHandler.LOG_LOCAL4)
@@ -87,7 +87,7 @@ def setup_logging():
         app.logger.addHandler(syslog_handler)
 
     if app.config['FILE_LOGGING'] and app.config['LOGFILE']:
-        print "doing file logging to %s" % app.config['LOGFILE']
+        print("doing file logging to %s" % app.config['LOGFILE'])
         file_handler = logging.handlers.RotatingFileHandler(
             app.config['LOGFILE'],
             maxBytes=500000,
diff --git a/execdb/cli.py b/execdb/cli.py
index 8e1e1ca..5ad8771 100644
--- a/execdb/cli.py
+++ b/execdb/cli.py
@@ -50,7 +50,7 @@ def get_alembic_config():
 
 
 def upgrade_db(*args):
-    print "Upgrading Database to Latest Revision"
+    print("Upgrading Database to Latest Revision")
     alembic_cfg = get_alembic_config()
     al_command.upgrade(alembic_cfg, "head")
 
@@ -64,20 +64,20 @@ def init_alembic(*args):
     current_rev = context.get_current_revision()
 
     if not current_rev:
-        print "Initializing alembic"
-        print " - Setting the current version to the first revision"
+        print("Initializing alembic")
+        print(" - Setting the current version to the first revision")
         al_command.stamp(alembic_cfg, "1cefaba53e0")
     else:
-        print "Alembic already initialized"
+        print("Alembic already initialized")
 
 
 def initialize_db(destructive):
     alembic_cfg = get_alembic_config()
 
-    print "Initializing database"
+    print("Initializing database")
 
     if destructive:
-        print " - Dropping all tables"
+        print(" - Dropping all tables")
         db.drop_all()
 
     # check whether the table 'job' exists
@@ -85,9 +85,9 @@ def initialize_db(destructive):
     insp = reflection.Inspector.from_engine(db.engine)
     table_names = insp.get_table_names()
     if 'job' not in table_names and 'Job' not in table_names:
-        print " - Creating tables"
+        print(" - Creating tables")
         db.create_all()
-        print " - Stamping alembic's current version to 'head'"
+        print(" - Stamping alembic's current version to 'head'")
         al_command.stamp(alembic_cfg, "head")
 
     # check to see if the db has already been initialized by checking for an
@@ -95,18 +95,18 @@ def initialize_db(destructive):
     context = MigrationContext.configure(db.engine.connect())
     current_rev = context.get_current_revision()
     if current_rev:
-        print " - Database is currently at rev %s" % current_rev
+        print(" - Database is currently at rev %s" % current_rev)
         upgrade_db(destructive)
     else:
-        print "WARN: You need to have your db stamped with an alembic revision"
-        print "      Run 'init_alembic' sub-command first."
+        print("WARN: You need to have your db stamped with an alembic revision")
+        print("      Run 'init_alembic' sub-command first.")
 
 
 def mock_data(destructive):
-    print "Populating tables with mock-data"
+    print("Populating tables with mock-data")
 
     if destructive or not db.session.query(User).count():
-        print " - User"
+        print(" - User")
         data_users = [('admin', 'admin'), ('user', 'user')]
 
         for d in data_users:
@@ -115,7 +115,7 @@ def mock_data(destructive):
 
         db.session.commit()
     else:
-        print " - skipped User"
+        print(" - skipped User")
 
 def mock_data_live(destructive):
     import time
@@ -168,9 +168,9 @@ def main():
     (options, args) = parser.parse_args()
 
     if len(args) != 1 or args[0] not in possible_commands:
-        print usage
-        print
-        print 'Please use one of the following commands: %s' % str(possible_commands)
+        print(usage)
+        print("\n")
+        print('Please use one of the following commands: %s' % str(possible_commands))
         sys.exit(1)
 
     command = {
@@ -181,8 +181,8 @@ def main():
         'init_alembic': init_alembic,
     }[args[0]]
     if not options.destructive:
-        print "Proceeding in non-destructive mode. To perform destructive "\
-              "steps use -d option."
+        print("Proceeding in non-destructive mode. To perform destructive "\
+              "steps use -d option.")
 
     command(options.destructive)
 
diff --git a/execdb/controllers/admin.py b/execdb/controllers/admin.py
index 6a97990..f665a37 100644
--- a/execdb/controllers/admin.py
+++ b/execdb/controllers/admin.py
@@ -18,7 +18,7 @@
 #    Josef Skladanka <jskladan@redhat.com>
 
 from flask import Blueprint, render_template, flash, url_for
-from flask.ext.login import login_required
+from flask_login import login_required
 
 
 admin = Blueprint('admin', __name__)
diff --git a/execdb/controllers/login_page.py b/execdb/controllers/login_page.py
index 8e3b2a1..6ec24b4 100644
--- a/execdb/controllers/login_page.py
+++ b/execdb/controllers/login_page.py
@@ -18,10 +18,10 @@
 #    Josef Skladanka <jskladan@redhat.com>
 
 from flask import Blueprint, render_template, redirect, flash, url_for, request
-from flask.ext.wtf import Form
+from flask_wtf import Form
 from wtforms import TextField, PasswordField, HiddenField, RadioField
 from wtforms.validators import Required
-from flask.ext.login import login_user, logout_user, login_required, current_user, AnonymousUserMixin
+from flask_login import login_user, logout_user, login_required, current_user, AnonymousUserMixin
 
 
 from execdb import app, login_manager
diff --git a/execdb/controllers/main.py b/execdb/controllers/main.py
index f4abcde..623e888 100644
--- a/execdb/controllers/main.py
+++ b/execdb/controllers/main.py
@@ -21,7 +21,7 @@ from flask import Blueprint, render_template, request, jsonify
 import werkzeug.exceptions
 from sqlalchemy.orm import exc as orm_exc
 
-from flask.ext.restful import reqparse
+from flask_restful import reqparse
 from werkzeug.exceptions import HTTPException
 from werkzeug.exceptions import BadRequest as JSONBadRequest
 
diff --git a/execdb/models/user.py b/execdb/models/user.py
index e80a347..a5baf16 100644
--- a/execdb/models/user.py
+++ b/execdb/models/user.py
@@ -18,7 +18,7 @@
 #    Josef Skladanka <jskladan@redhat.com>
 
 from execdb import db
-from flask.ext.login import UserMixin
+from flask_login import UserMixin
 from werkzeug.security import generate_password_hash, check_password_hash
 
 
diff --git a/run_cli.py b/run_cli.py
index 5344eaf..5402245 100644
--- a/run_cli.py
+++ b/run_cli.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 #
 # Copyright 2014, Red Hat, Inc
 #
diff --git a/runapp.py b/runapp.py
index 316313f..45964af 100644
--- a/runapp.py
+++ b/runapp.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 #
 # runapp.py - script to facilitate running the execdb app from the CLI
 #

From 345b3feeec4d15d828eedce7bd643a5f14f4ced1 Mon Sep 17 00:00:00 2001
From: František Zatloukal <fzatlouk@redhat.com>
Date: Nov 28 2018 09:05:08 +0000
Subject: [PATCH 2/6] Bump to 0.0.11


---

diff --git a/execdb.spec b/execdb.spec
index 2bc4755..250e37c 100644
--- a/execdb.spec
+++ b/execdb.spec
@@ -1,6 +1,6 @@
 Name:           execdb
 # NOTE: if you update version, *make sure* to also update `execdb/__init__.py`
-Version:        0.0.10
+Version:        0.0.11
 Release:        1%{?dist}
 Summary:        Execution status database for Taskotron
 
@@ -10,24 +10,16 @@ Source0:        https://qa.fedoraproject.org/releases/%{name}/%{name}-%{version}
 
 BuildArch:      noarch
 
-%if 0%{?fedora} <= 27
-Requires:       python-alembic
-Requires:       python-flask
-Requires:       python-flask-sqlalchemy
-Requires:       python-flask-wtf
-Requires:       python-flask-login
-%else
-Requires:       python2-alembic
-Requires:       python2-flask
-Requires:       python2-flask-sqlalchemy
-Requires:       python2-flask-wtf
-Requires:       python2-flask-login
-%endif
-Requires:       python2-flask-restful
-Requires:       python2-six
-
-BuildRequires:  python2-devel
-BuildRequires:  python2-setuptools
+Requires:       python3-alembic
+Requires:       python3-flask
+Requires:       python3-flask-sqlalchemy
+Requires:       python3-flask-wtf
+Requires:       python3-flask-login
+Requires:       python3-flask-restful
+Requires:       python3-six
+
+BuildRequires:  python3-devel
+BuildRequires:  python3-setuptools
 
 %description
 ExecDB is a database that stores the execution status of jobs running
@@ -43,10 +35,10 @@ started and finished, and some of their properties.
 rm -f %{buildroot}%{_sysconfdir}/execdb/*.py{c,o}
 
 %build
-%py2_build
+%py3_build
 
 %install
-%py2_install
+%py3_install
 
 # apache and wsgi settings
 install -d %{buildroot}%{_datadir}/execdb/conf
@@ -64,16 +56,22 @@ install -p -m 0644 conf/settings.py.example %{buildroot}%{_sysconfdir}/execdb/se
 %files
 %doc README.md
 %license LICENSE
-%{python2_sitelib}/execdb
-%{python2_sitelib}/*.egg-info
+%{python3_sitelib}/execdb
+%{python3_sitelib}/*.egg-info
+
+%{_bindir}/execdb
 
-%attr(755,root,root) %{_bindir}/execdb
 %dir %{_sysconfdir}/execdb
 %config(noreplace) %{_sysconfdir}/execdb/settings.py
+
 %dir %{_datadir}/execdb
 %{_datadir}/execdb/*
 
 %changelog
+* Wed Nov 28 2018 Frantisek Zatloukal <fzatlouk@redhat.com> - 0.0.11-1
+- Switch to Python 3
+- Drop Fedora 27
+
 * Fri Apr 27 2018 Frantisek Zatloukal <fzatlouk@redhat.com> - 0.0.10-1
 - API
 - Fix show_job template to use right path for api
diff --git a/execdb/__init__.py b/execdb/__init__.py
index 74a34dd..405db7b 100644
--- a/execdb/__init__.py
+++ b/execdb/__init__.py
@@ -26,7 +26,7 @@ import os
 
 
 # the version as used in setup.py
-__version__ = "0.0.10"
+__version__ = "0.0.11"
 
 
 # Flask App
diff --git a/init_db.sh b/init_db.sh
index 4d371ce..da99bf8 100755
--- a/init_db.sh
+++ b/init_db.sh
@@ -2,7 +2,7 @@
 # this is a simple script to aid in the setup of a new db
 
 # init db
-python run_cli.py init_db ${@}
+python3 run_cli.py init_db ${@}
 
 # insert mock data
-python run_cli.py mock_data ${@}
+python3 run_cli.py mock_data ${@}

From f32197064b2bb0572e263d92b57bdbaf1840d50d Mon Sep 17 00:00:00 2001
From: František Zatloukal <fzatlouk@redhat.com>
Date: Nov 28 2018 09:05:55 +0000
Subject: [PATCH 3/6] Makefile: Use generic Makefile provided by qa-make


Project can be found here: https://pagure.io/fedora-qa/qa-make

---

diff --git a/Makefile b/Makefile
index c319993..28fde3b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,35 +1,70 @@
-#
-# Copyright 2013, Red Hat, Inc.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-
-# general variables
-VENV=test_env
-SRC=execdb
+# Copyright 2018, Red Hat, Inc.
+# License: GPL-2.0+ <http://spdx.org/licenses/GPL-2.0+>
+# See the LICENSE file for more details on Licensing
+
+#######################################################################
+#      _____            _        _ _           _   _                  #
+#     / ____|          | |      (_) |         | | (_)                 #
+#    | |     ___  _ __ | |_ _ __ _| |__  _   _| |_ _ _ __   __ _      #
+#    | |    / _ \| '_ \| __| '__| | '_ \| | | | __| | '_ \ / _` |     #
+#    | |___| (_) | | | | |_| |  | | |_) | |_| | |_| | | | | (_| |     #
+#     \_____\___/|_| |_|\__|_|  |_|_.__/ \__,_|\__|_|_| |_|\__, |     #
+#                                                           __/ |     #
+#     If you want to add/fix anything here, please create  |___/      #
+#     PR at qa-make https://pagure.io/fedora-qa/qa-make               #
+#                                                                     #
+#######################################################################
+
+# Allows to print variables, eg. make print-SRC
+print-%  : ; @echo $* = $($*)
+
+# Get variables from Makefile.cfg
+SRC=$(shell grep -s SRC Makefile.cfg | sed 's/SRC=//')
+VENV=$(shell grep -s VENV Makefile.cfg | sed 's/VENV=//')
+MODULENAME=$(shell grep -s MODULENAME Makefile.cfg | sed 's/MODULENAME=//')
+
+# Try to detect SRC in case we didn't find Makefile.cfg
+ifeq ($(SRC),)
+SRC=$(shell rpmspec -q --queryformat="%{NAME}\n" *.spec | head -1)
+SPECNUM=$(shell ls -1 *.spec | wc -l)
+ifneq ($(SPECNUM),1)
+$(error Make sure you have either one spec file in the directory or configure it in Makefile.cfg)
+endif
+endif
 
 # Variables used for packaging
 SPECFILE=$(SRC).spec
 BASEARCH:=$(shell uname -i)
 DIST:=$(shell rpm --eval '%{dist}')
-VERSION:=$(shell rpmspec -q --queryformat="%{VERSION}\n" $(SPECFILE) | uniq)
-RELEASE:=$(subst $(DIST),,$(shell rpmspec -q --queryformat="%{RELEASE}\n" $(SPECFILE) | uniq))
+TARGETVER:=$(shell lsb_release -r |grep -o '[0-9]*')
+TARGETDIST:=fc$(TARGETVER)
+VERSION:=$(shell rpmspec -q --queryformat="%{VERSION}\n" $(SPECFILE) | head -1)
+RELEASE:=$(shell rpmspec -q --queryformat="%{RELEASE}\n" $(SPECFILE) | head -1 | sed 's/$(DIST)/\.$(TARGETDIST)/g')
 NVR:=$(SRC)-$(VERSION)-$(RELEASE)
 GITBRANCH:=$(shell git rev-parse --abbrev-ref HEAD)
-TARGETDIST:=fc25
-BUILDTARGET=fedora-25-x86_64
+BUILDTARGET:=fedora-$(TARGETVER)-x86_64
+KOJITARGET:=$(shell echo $(TARGETDIST) | sed 's/c//' | sed 's/el/epel-/')
+
+.PHONY: update-makefile
+update-makefile:
+	curl --fail https://pagure.io/fedora-qa/qa-make/raw/master/f/Makefile -o Makefile.new
+	if ! cmp Makefile Makefile.new ; then mv Makefile.new Makefile ; fi
+
+.PHONY: test
+.ONESHELL: test
+test: $(VENV)
+	set -e
+	source $(VENV)/bin/activate;
+	TEST='true' py.test --cov-report=term-missing --cov $(MODULENAME);
+	deactivate
+
+.PHONY: test-ci
+.ONESHELL: test-ci
+test-ci: $(VENV)
+	set -e
+	source $(VENV)/bin/activate
+	TEST='true' py.test --cov-report=xml --cov $(MODULENAME)
+	deactivate
 
 .PHONY: pylint
 pylint:
@@ -49,7 +84,7 @@ docs:
 .PHONY: clean
 clean:
 	rm -rf dist
-	rm -rf execdb.egg-info
+	rm -rf $(SRC).egg-info
 	rm -rf build
 	rm -f pep8.out
 	rm -f pylint.out
@@ -60,20 +95,22 @@ archive: $(SRC)-$(VERSION).tar.gz
 .PHONY: $(SRC)-$(VERSION).tar.gz
 $(SRC)-$(VERSION).tar.gz:
 	git archive $(GITBRANCH) --prefix=$(SRC)-$(VERSION)/ | gzip -c9 > $@
+	mkdir -p build/$(VERSION)-$(RELEASE)
+	mv $(SRC)-$(VERSION).tar.gz build/$(VERSION)-$(RELEASE)/
 
-.PHONY: mocksrpm
-mocksrpm: archive
-	mock -r $(BUILDTARGET) --buildsrpm --spec $(SPECFILE) --sources .
-	cp /var/lib/mock/$(BUILDTARGET)/result/$(NVR).$(TARGETDIST).src.rpm .
+.PHONY: srpm
+srpm: archive
+	mock -r $(BUILDTARGET) --buildsrpm --spec $(SPECFILE) --sources build/$(VERSION)-$(RELEASE)/
+	cp /var/lib/mock/$(BUILDTARGET)/result/$(NVR).src.rpm build/$(VERSION)-$(RELEASE)/
 
-.PHONY: mockbuild
-mockbuild: mocksrpm
-	mock -r $(BUILDTARGET) --no-clean --rebuild $(NVR).$(TARGETDIST).src.rpm
-	cp /var/lib/mock/$(BUILDTARGET)/result/$(NVR).$(TARGETDIST).noarch.rpm .
+.PHONY: build
+build: srpm
+	mock -r $(BUILDTARGET) --no-clean --rebuild build/$(VERSION)-$(RELEASE)/$(NVR).src.rpm
+	cp /var/lib/mock/$(BUILDTARGET)/result/*.rpm build/$(VERSION)-$(RELEASE)/
 
-#.PHONY: kojibuild
-#kojibuild: mocksrpm
-#	koji build --scratch dist-6E-epel-testing-candidate $(NVR).$(TARGETDIST).src.rpm
+.PHONY: scratch
+scratch: srpm
+	koji build --scratch $(KOJITARGET) build/$(VERSION)-$(RELEASE)/$(NVR).src.rpm
 
 .PHONY: nvr
 nvr:
@@ -87,7 +124,10 @@ cleanvenv:
 virtualenv: $(VENV)
 
 .PHONY: $(VENV)
+.ONESHELL: $(VENV)
 $(VENV):
-	virtualenv $(VENV)
-	sh -c "set -e; . $(VENV)/bin/activate; pip install -r requirements.txt; \
-	       deactivate"
+	virtualenv --system-site-packages $(VENV)
+	set -e
+	source $(VENV)/bin/activate
+	pip install -r requirements.txt
+	deactivate

From bfd961695ffd98b35c422181fd71f1c07354b060 Mon Sep 17 00:00:00 2001
From: František Zatloukal <fzatlouk@redhat.com>
Date: Jan 22 2019 11:36:47 +0000
Subject: [PATCH 4/6] Fix FlaskWTFDeprecationWarning


"flask_wtf.Form" has been renamed to "FlaskForm" and will be removed in 1.0.

---

diff --git a/execdb/controllers/login_page.py b/execdb/controllers/login_page.py
index 6ec24b4..508ee0f 100644
--- a/execdb/controllers/login_page.py
+++ b/execdb/controllers/login_page.py
@@ -18,7 +18,7 @@
 #    Josef Skladanka <jskladan@redhat.com>
 
 from flask import Blueprint, render_template, redirect, flash, url_for, request
-from flask_wtf import Form
+from flask_wtf import FlaskForm as Form
 from wtforms import TextField, PasswordField, HiddenField, RadioField
 from wtforms.validators import Required
 from flask_login import login_user, logout_user, login_required, current_user, AnonymousUserMixin

From 5b510d3747184330de316ce7b35f143c3b438984 Mon Sep 17 00:00:00 2001
From: František Zatloukal <fzatlouk@redhat.com>
Date: Jan 22 2019 12:14:45 +0000
Subject: [PATCH 5/6] Support also old-style flask_wtf import


It needs to work also on F27 for a little longer, so add simple
try/except to prefer new import, but fall back to the old one if needed

---

diff --git a/execdb/controllers/login_page.py b/execdb/controllers/login_page.py
index 508ee0f..3c84bec 100644
--- a/execdb/controllers/login_page.py
+++ b/execdb/controllers/login_page.py
@@ -18,7 +18,10 @@
 #    Josef Skladanka <jskladan@redhat.com>
 
 from flask import Blueprint, render_template, redirect, flash, url_for, request
-from flask_wtf import FlaskForm as Form
+try:
+    from flask_wtf import FlaskForm as Form
+except ImportError:
+    from flask_wtf import Form
 from wtforms import TextField, PasswordField, HiddenField, RadioField
 from wtforms.validators import Required
 from flask_login import login_user, logout_user, login_required, current_user, AnonymousUserMixin

From 7fcaeae3ec1f3ff4500b9a6af78e1a5753b8ae81 Mon Sep 17 00:00:00 2001
From: František Zatloukal <fzatlouk@redhat.com>
Date: Jan 22 2019 14:54:33 +0000
Subject: [PATCH 6/6] Add missing Requires: psycopg2, wtforms


---

diff --git a/execdb.spec b/execdb.spec
index 250e37c..303446b 100644
--- a/execdb.spec
+++ b/execdb.spec
@@ -16,6 +16,8 @@ Requires:       python3-flask-sqlalchemy
 Requires:       python3-flask-wtf
 Requires:       python3-flask-login
 Requires:       python3-flask-restful
+Requires:       python3-psycopg2
+Requires:       python3-wtforms
 Requires:       python3-six
 
 BuildRequires:  python3-devel

