diff --git a/fedora-openqa-schedule.py b/scheduler/fedora-openqa-schedule.py
rename from fedora-openqa-schedule.py
rename to scheduler/fedora-openqa-schedule.py
diff --git a/fedora_openqa_schedule/__init__.py b/scheduler/fedora_openqa_schedule/__init__.py
rename from fedora_openqa_schedule/__init__.py
rename to scheduler/fedora_openqa_schedule/__init__.py
diff --git a/fedora_openqa_schedule/cli.py b/scheduler/fedora_openqa_schedule/cli.py
rename from fedora_openqa_schedule/cli.py
rename to scheduler/fedora_openqa_schedule/cli.py
--- a/fedora_openqa_schedule/cli.py
+++ b/scheduler/fedora_openqa_schedule/cli.py
@@ -28,13 +28,6 @@
 import logging
 import sys
 
-# External dependencies
-# We can at least find images and run OpenQA jobs without wikitcms
-try:
-    import wikitcms.wiki
-except ImportError:
-    wikitcms = None
-
 # Internal dependencies
 import fedora_openqa_schedule.schedule as schedule
 import fedora_openqa_schedule.report as report
@@ -44,13 +37,13 @@
 
 ### SUB-COMMAND METHODS
 
-def command_compose(args, wiki):
+def command_compose(args, wiki_url):
     """run OpenQA on a specified compose, optionally reporting
     results if a matching wikitcms ValidationEvent is found by
     relval/wikitcms.
     """
     try:
-        (ff_rel, jobs) = schedule.jobs_from_compose(
+        (build, jobs) = schedule.jobs_from_compose(
             args.release, args.milestone, args.compose, arches=args.arch, force=args.force,
             wait=args.wait)
 
@@ -68,8 +61,7 @@
         sys.exit(1)
 
     if jobs:
-        build = '_'.join((ff_rel.release, ff_rel.milestone, ff_rel.compose))
-        report.wait_and_report(build=build, wiki=wiki, report=args.submit)
+        report.wait_and_report(wiki_url, build=build, do_report=args.submit)
     else:
         msg = "No jobs run!"
         if not args.force:
@@ -81,25 +73,24 @@
     logger.debug("finished")
     sys.exit()
 
-def command_current(args, wiki):
+def command_current(args, wiki_url):
     """run OpenQA for current release validation event, if we have
     not already done it.
     """
     logger.info("running on current release")
     try:
-        (ff_rel, jobs) = schedule.jobs_from_current(wiki)
+        (build, jobs) = schedule.jobs_from_current(wiki_url)
     except schedule.TriggerException as e:
         logger.debug("No jobs run: %s", e)
         sys.exit(1)
     # wait for jobs to finish and display results
     if jobs:
         logger.info("waiting for jobs")
-        build = '_'.join((ff_rel.release, ff_rel.milestone, ff_rel.compose))
-        report.wait_and_report(build=build, wiki=wiki, report=args.submit)
+        report.wait_and_report(wiki_url, build=build, do_report=args.submit)
     logger.debug("finished")
     sys.exit()
 
-def command_report(args, wiki):
+def command_report(args, wiki_url):
     """Map a list of openQA job IDs and/or builds to Wikitcms test
     results, and either display the ResTups for inspection or report
     the results to the wiki.
@@ -109,11 +100,11 @@
     try:
         if jobs:
             report.wait_and_report(
-                job_ids=jobs, wiki=wiki, report=args.submit)
+                wiki_url, job_ids=jobs, do_report=args.submit)
         if builds:
             for build in builds:
                 report.wait_and_report(
-                    build=build, wiki=wiki, report=args.submit)
+                    wiki_url, build=build, do_report=args.submit)
     except report.LoginError:
         # Module logs for us, no need to dupe it.
         sys.exit(1)
@@ -133,20 +124,12 @@
     else:
         logging.basicConfig(level=loglevel)
 
-    wiki = None
+    wiki_url = "fedoraproject.org"
     if args.test:
         logger.debug("using test wiki")
-        if wikitcms:
-            wiki = wikitcms.wiki.Wiki(('https', 'stg.fedoraproject.org'), '/w/')
-        else:
-            logger.warning("wikitcms not found, reporting to wiki disabled")
-    else:
-        if wikitcms:
-            wiki = wikitcms.wiki.Wiki(('https', 'fedoraproject.org'), '/w/')
-        else:
-            logger.warning("wikitcms not found, reporting to wiki disabled")
+        wiki_url = "stg.fedoraproject.org"
 
-    args.func(args, wiki)
+    args.func(args, wiki_url)
 
 def parse_args():
     """Parse arguments with argparse."""
diff --git a/fedora_openqa_schedule/conf_test_suites.py b/scheduler/fedora_openqa_schedule/conf_test_suites.py
rename from fedora_openqa_schedule/conf_test_suites.py
rename to scheduler/fedora_openqa_schedule/conf_test_suites.py
diff --git a/fedora_openqa_schedule/config.py b/scheduler/fedora_openqa_schedule/config.py
rename from fedora_openqa_schedule/config.py
rename to scheduler/fedora_openqa_schedule/config.py
diff --git a/fedora_openqa_schedule/report.py b/scheduler/fedora_openqa_schedule/report.py
rename from fedora_openqa_schedule/report.py
rename to scheduler/fedora_openqa_schedule/report.py
--- a/fedora_openqa_schedule/report.py
+++ b/scheduler/fedora_openqa_schedule/report.py
@@ -69,20 +69,25 @@
     imagetype = imagetype.replace('dvd', 'offline')
     if payload.lower() == 'generic':
         payload = 'Server'
-    if job['settings'].has_key('UEFI'):
+    if 'UEFI' in job['settings']:
         uefi = 'UEFI'
         bootmethod = 'x86 UEFI'
     else:
         uefi = arch
         bootmethod = 'x86 BIOS'
 
-    for key in uniqueres:
-        uniqueres[key] = uniqueres[key].replace('$RUNARCH_OR_UEFI$', uefi)
-        uniqueres[key] = uniqueres[key].replace('$FS$', fs)
-        uniqueres[key] = uniqueres[key].replace('$RUNARCH$', arch)
-        uniqueres[key] = uniqueres[key].replace('$BOOTMETHOD$', bootmethod)
-        uniqueres[key] = uniqueres[key].replace('$PAYLOAD$', payload)
-        uniqueres[key] = uniqueres[key].replace('$IMAGETYPE$', imagetype)
+    changed = {}
+    for key, value in uniqueres.iteritems():
+        value = value.replace('$RUNARCH_OR_UEFI$', uefi)
+        value = value.replace('$FS$', fs)
+        value = value.replace('$RUNARCH$', arch)
+        value = value.replace('$BOOTMETHOD$', bootmethod)
+        value = value.replace('$PAYLOAD$', payload)
+        value = value.replace('$IMAGETYPE$', imagetype)
+        changed[key] = value
+
+    return changed
+
 
 def get_passed_testcases(jobs):
     """Given an iterable of job dicts - any waiting, filtering and so
@@ -96,7 +101,7 @@
             testsuite = job['settings']['TEST']
             # There usually ought to be an entry in TESTSUITES for all
             # tests, but just in case someone messed up, let's be safe
-            if not testsuite in conf_test_suites.TESTSUITES:
+            if testsuite not in conf_test_suites.TESTSUITES:
                 logger.warning("No TESTSUITES entry found for test {0}!".format(testsuite))
                 continue
             for testcase in conf_test_suites.TESTSUITES[testsuite]:
@@ -107,9 +112,9 @@
                 # should be filed. We do a deepcopy here because otherwise the dict in TESTCASES
                 # itself is modified in-place and other jobs for the same testcase (but a different
                 # environment, section or testname) will read the modified values and be messed up.
-                uniqueres = copy.deepcopy(conf_test_suites.TESTCASES[testcase])
+
                 # replace $FOO$ values in uniqueres
-                _uniqueres_replacements(job, uniqueres)
+                uniqueres = _uniqueres_replacements(job, conf_test_suites.TESTCASES[testcase])
                 result = ResTuple(
                     testtype=uniqueres['type'], release=release, milestone=milestone,
                     compose=compose, testcase=testcase, section=uniqueres.get('section'),
@@ -119,10 +124,11 @@
 
     return sorted(list(passed_testcases), key=attrgetter('testcase'))
 
-def wait_and_report(job_ids=None, build=None, wiki=None, report=None, waittime=None):
+
+def wait_and_report(wiki_url, job_ids=None, build=None, do_report=None, waittime=None):
     """Find some openQA jobs and report the results to Wikitcms (via
     report_results). Either job_ids (an iterable of IDs) or build (an
-    openQA BUILD string) is required. wiki and report are passed to
+    openQA BUILD string) is required. wiki_url and do_report are passed to
     report_results and documented there. waittime (int) is how long to
     wait for jobs to complete before giving up; if 'build' is used,
     will also wait for jobs to appear. If jobs are not present and
@@ -141,24 +147,24 @@
     client = OpenQA_Client()
     # iterate_jobs can work on jobs or build; whichever is set will be used
     for joblist in client.iterate_jobs(jobs=job_ids, build=build, waittime=waittime):
-        report_results(joblist, wiki=wiki, report=report)
+        report_results(joblist, wiki_url, do_report=do_report)
+
 
-def report_results(jobs, wiki=None, report=None):
+def report_results(jobs, wiki_url, do_report=None):
     """Report results from openQA jobs to Wikitcms. jobs is an
-    iterable of openQA job dicts. If report is False, will just print
-    out the python-wikitcms ResTups for inspection. If report is None,
+    iterable of openQA job dicts. If do_report is False, will just print
+    out the python-wikitcms ResTups for inspection. If do_report is None,
     will read the setting from the config file; if no config file is
     present, default is True.
     """
     passed_testcases = get_passed_testcases(jobs)
     logger.info("passed testcases: %s", passed_testcases)
-    if report is None:
-        report = CONFIG.getboolean('report', 'submit')
+    if do_report is None:
+        do_report = CONFIG.getboolean('report', 'submit')
 
-    if report:
+    if do_report:
         logger.info("reporting test passes")
-        if not wiki:
-            wiki = Wiki()
+        wiki = Wiki(('https', wiki_url), '/w/')
         if not wiki.logged_in:
             wiki.login()
         if not wiki.logged_in:
@@ -170,6 +176,9 @@
         for dupe in dupes:
             tmpl = "already reported result for test %s, env %s! Will not report dupe."
             logger.info(tmpl, dupe.testcase, dupe.env)
+        for insuff in insuffs:
+            tmpl = "insufficient data for test %s, env %s! Will not report."
+            logger.info(tmpl, insuff.testcase, insuff.env)
 
     else:
         logger.warning("no reporting is done")
diff --git a/fedora_openqa_schedule/schedule.py b/scheduler/fedora_openqa_schedule/schedule.py
rename from fedora_openqa_schedule/schedule.py
rename to scheduler/fedora_openqa_schedule/schedule.py
--- a/fedora_openqa_schedule/schedule.py
+++ b/scheduler/fedora_openqa_schedule/schedule.py
@@ -32,10 +32,12 @@
 # External dependencies
 import fedfind.helpers
 import fedfind.release
+import wikitcms.wiki
 from openqa_client.client import OpenQA_Client
 from six.moves import configparser
 from six.moves.urllib.request import urlopen
 
+
 # Internal dependencies
 from fedora_openqa_schedule.config import CONFIG
 
@@ -51,31 +53,6 @@
     pass
 
 
-def read_last(arches=None):
-    """Read last tested version from file.
-    If no architectures were provided, load them from config file.
-    """
-    persistent = CONFIG.get('schedule', 'persistent')
-    if not arches:
-        # split arches by words (works for both space- and comma-separated values)
-        arches = re.split(r'\W+', CONFIG.get('schedule', 'arches').strip())
-    logger.debug("reading latest checked version from %s", persistent)
-    result = {}
-    try:
-        f = open(persistent, "r")
-        json_raw = f.read()
-        f.close()
-        json_parsed = json.loads(json_raw)
-    except IOError:
-        logger.warning("cannot read file %s", persistent)
-        return result, {}
-
-    for arch in arches:
-        result[arch] = json_parsed.get(arch, None)
-        logger.info("latest version for %s: %s", arch, result[arch])
-    return result, json_parsed
-
-
 def download_image(image, iso_path=None):
     """Download a given image with a name that should be unique,
     optionally specifying destination directory.
@@ -168,47 +145,27 @@
     return output["ids"]
 
 
-def jobs_from_current(wiki, force=False, arches=None):
+def jobs_from_current(wiki_url, force=False, arches=None):
     """Schedule jobs against the 'current' release validation event
     (according to wikitcms) if we have not already. Returns the job
     list. If force is False, for each ISO, jobs will only be scheduled
     if there are no existing, non-cancelled jobs for the same ISO and
     flavor. If not set, load list of arches from config file.
     """
-    if not wiki:
-        logger.warning("python-wikitcms is required for current validation event discovery.")
-        return ([], None)
     # if arches weren't specified as argument, parse them from config file
     if not arches:
         arches = re.split(r'\W+', CONFIG.get('schedule', 'arches').strip())
-    last_versions, json_parsed = read_last()
-    currev = wiki.current_event
-    logger.info("current event: %s", currev.version)
-    runarches = []
-    for arch in arches:
-        last_version = last_versions.get(arch, None)
-        if last_version and last_version >= currev.sortname:
-            logger.info("skipped: %s: %s is newer or equal to %s", arch, last_version, currev.sortname)
-        else:
-            runarches.append(arch)
-            logger.debug("%s will be tested in version %s", arch, currev.sortname)
-            json_parsed[arch] = currev.sortname
 
-    jobs = []
+    wiki = wikitcms.wiki.Wiki(('https', wiki_url), '/w/')
 
-    if not runarches:
-        raise TriggerException("Skipped all arches, nothing to do.")
+    currev = wiki.current_event
+    logger.info("current event: %s", currev.version)
 
-    jobs = jobs_from_fedfind(currev.ff_release, arches=runarches, force=force)
+    jobs = _jobs_from_fedfind(currev.ff_release, arches=arches, force=force)
     logger.info("jobs_from_current: planned jobs: %s", ' '.join(str(j) for j in jobs))
 
-    # write info about latest versions
-    f = open(CONFIG.get('schedule', 'persistent'), "w")
-    f.write(json.dumps(json_parsed))
-    f.close()
-    logger.debug("written info about newest version")
-
-    return (currev.ff_release, jobs)
+    build = '_'.join((currev.ff_release.release, currev.ff_release.milestone, currev.ff_release.compose))
+    return (build, jobs)
 
 
 def jobs_from_compose(release='', milestone='', compose='', arches=None, force=False, wait=None):
@@ -231,7 +188,7 @@
     before scheduling the jobs; if None, the value will be read from
     configuration (see config.py for default). Will raise WaitError if
     the compose cannot be found after the wait period. If wait is 0,
-    jobs_from_fedfind will raise TriggerException if the compose does
+    _jobs_from_fedfind will raise TriggerException if the compose does
     not exist.
     """
     # Get the fedfind release object.
@@ -250,12 +207,14 @@
         except fedfind.exceptions.WaitError as err:
             raise WaitError(err)
 
-    jobs = jobs_from_fedfind(ff_release, arches=arches, force=force)
+    jobs = _jobs_from_fedfind(ff_release, arches=arches, force=force)
     logger.info("jobs_from_compose: planned jobs: %s", ' '.join(str(j) for j in jobs))
-    return (ff_release, jobs)
+
+    build = '_'.join((ff_release.release, ff_release.milestone, ff_release.compose))
+    return (build, jobs)
 
 
-def jobs_from_fedfind(ff_release, force=False, arches=None, imagetypes=None, payloads=None):
+def _jobs_from_fedfind(ff_release, force=False, arches=None, imagetypes=None, payloads=None):
     """Given a fedfind.Release object, find the ISOs we want and run
     jobs on them. arches is an iterable of arches to run on, if not
     specified, we'll use value from config file. If force is False,
diff --git a/schedule.conf.sample b/scheduler/schedule.conf.sample
rename from schedule.conf.sample
rename to scheduler/schedule.conf.sample
diff --git a/setup.py b/scheduler/setup.py
rename from setup.py
rename to scheduler/setup.py

