diff --git a/src/unitgrade.egg-info/PKG-INFO b/src/unitgrade.egg-info/PKG-INFO
index 86e5e03652e3a2d6d1329b6265018ffbf0427c78..ec77f8e1bb796d9f2c49ff2783010c2b85ad9f19 100644
--- a/src/unitgrade.egg-info/PKG-INFO
+++ b/src/unitgrade.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: unitgrade
-Version: 0.1.30.9
+Version: 0.1.30.15
 Summary: A student homework/exam evaluation framework build on pythons unittest framework.
 Home-page: https://lab.compute.dtu.dk/tuhe/unitgrade
 Author: Tue Herlau
diff --git a/src/unitgrade/framework.py b/src/unitgrade/framework.py
index 50755736017777ab8b6e53f750eaebd1d0665345..b84c10f3410c3ceb8e2e4ef793b90cc326d27b5b 100644
--- a/src/unitgrade/framework.py
+++ b/src/unitgrade/framework.py
@@ -1,3 +1,4 @@
+from threading import Thread
 import importnb
 import numpy as np
 import sys
@@ -16,6 +17,7 @@ from unittest.case import TestCase
 from unitgrade.runners import UTextResult
 from unitgrade.utils import gprint, Capturing2, Capturing
 from unitgrade.artifacts import StdCapturing
+from unitgrade.utils import DKPupDB
 
 
 colorama.init(autoreset=True)  # auto resets your settings after every output
@@ -25,7 +27,6 @@ def setup_dir_by_class(C, base_dir):
     name = C.__class__.__name__
     return base_dir, name
 
-
 _DASHBOARD_COMPLETED_MESSAGE = "Dashboard> Evaluation completed."
 
 # Consolidate this code.
@@ -35,7 +36,6 @@ class classmethod_dashboard(classmethod):
             if not cls._generate_artifacts:
                 f(cls)
                 return
-            from unitgrade.utils import DKPupDB
             db = DKPupDB(cls._artifact_file_for_setUpClass())
             r = np.random.randint(1000 * 1000)
             db.set('run_id', r)
@@ -71,6 +71,7 @@ class classmethod_dashboard(classmethod):
                 sys.stdout = _stdout
                 sys.stderr = _stderr
                 std_capture.close()
+
         super().__init__(dashboard_wrap)
 
 class Report:
@@ -103,8 +104,10 @@ class Report:
         return inspect.getfile(type(self))
 
     def _artifact_file(self):
-        """ File for the artifacts DB (thread safe). This file is optinal. Note that it is a pupdb database file.
-        Note the file is shared between all sub-questions. """
+        """ File for the artifacts DB (thread safe). This file is optional. Note that it is a pupdb database file.
+        Note the file is shared between all sub-questions.
+        TODO: Describe what is actually in this file.
+        """
         return os.path.join(os.path.dirname(self._file()), "unitgrade_data/main_config_"+ os.path.basename(self._file()[:-3]) + ".artifacts.pkl")
 
     def _manifest_file(self):
@@ -112,6 +115,7 @@ class Report:
         The manifest is the file we append all artifact hashes to so we can check results at some later time.
         file is plaintext, and can be deleted.
         """
+        # print("_file", self._file())
         return os.path.join(os.path.dirname(self._file()), "unitgrade_data/token_" + os.path.basename(self._file()[:-3]) + ".manifest")
 
     def _is_run_in_grade_mode(self):
@@ -162,9 +166,17 @@ class Report:
             q._setup_answers_mode = True
             # q._generate_artifacts = False # Disable artifact generation when the report is being set up.
 
+        # Ensure that the lock file exists.
+        if self.url is not None:
+            if not os.path.dirname(self._get_remote_lock_file()):
+                os.makedirs(os.path.dirname(self._get_remote_lock_file()))
+            if not os.path.isdir(d_ := os.path.dirname(self._get_remote_lock_file())):
+                os.makedirs(d_)
+            with open(self._get_remote_lock_file(), 'w') as f:
+                f.write("If this file is present, we will not synchronize this directory with a remote (using report.url).\nThis is a very good idea during development, but the lock-file should be disabled (in gitignore) for the students.")
+
         from unitgrade import evaluate_report_student
         evaluate_report_student(self, unmute=verbose, noprogress=not verbose, generate_artifacts=False) # Disable artifact generation.
-
         # self.main()  # Run all tests in class just to get that out of the way...
         report_cache = {}
         for q, _ in self.questions:
@@ -179,7 +191,7 @@ class Report:
             for q, _ in self.questions:
                 q._with_coverage = False
 
-        # report_cache is saved on a per-question basis.
+            # report_cache is saved on a per-question basis.
         # it could also contain additional information such as runtime metadata etc. This may not be appropriate to store with the invidivual questions(?).
         # In this case, the function should be re-defined.
         return report_cache
@@ -189,29 +201,50 @@ class Report:
             q._cache = payloads[q.__qualname__]
         self._config = payloads['config']
 
+    def _get_remote_lock_file(self):
+        return os.path.join(os.path.dirname( self._artifact_file() ), "dont_check_remote.lock")
+
     def _check_remote_versions(self):
-        if self.url is None:
+        if self.url is None: # No url, no problem.
+            return
+
+        if os.path.isfile(self._get_remote_lock_file() ):
+            print("Since the file", self._get_remote_lock_file(), "was present I will not compare the files on this computer with git")
+            print("i.e., I am assuming this is being run on the teachers computer. Remember to put the file in .gitignore for the students!")
             return
+
+        if self._file().endswith("_complete.py"):
+            print("Unitgrade> You are trying to check the remote version of a *_tests_complete.py-file, and you will potentially overwrite part of this file.")
+            print("Unitgrade> Please add a unitgrade_data/note_remote_check.lock - file to this directory (and put it in the .gitignore) to avoid data loss.")
+            print(self._file())
+            raise Exception("Unitgrade> You are trying to check the remote version of a *_tests_complete.py-file, and you will potentially overwrite part of this file.")
+
+        print("CHECKING THE REMOTE VERSION. ")
+
         url = self.url
         if not url.endswith("/"):
             url += "/"
-        snapshot_file = os.path.dirname(self._file()) + "/unitgrade_data/.snapshot"
-        if os.path.isfile(snapshot_file):
-            with open(snapshot_file, 'r') as f:
-                t = f.read()
-                if (time.time() - float(t)) < self._remote_check_cooldown_seconds:
-                    return
+
+        db = DKPupDB("check_on_remote")
+
+        if 'last_check_time' in db:
+            # with open(snapshot_file, 'r') as f:
+            t = db['last_check_time']
+            if (time.time() - t) < self._remote_check_cooldown_seconds:
+                return
 
         if self.url.startswith("https://gitlab"):
             # Try to turn url into a 'raw' format.
             # "https://gitlab.compute.dtu.dk/tuhe/unitgrade_private/-/raw/master/examples/autolab_example_py_upload/instructor/cs102_autolab/report2_test.py?inline=false"
             # url = self.url
             url = url.replace("-/tree", "-/raw")
+            url = url.replace("-/blob", "-/raw")
             # print(url)
             # "https://gitlab.compute.dtu.dk/tuhe/unitgrade_private/-/tree/master/examples/autolab_example_py_upload/instructor/cs102_autolab"
             # "https://gitlab.compute.dtu.dk/tuhe/unitgrade_private/-/raw/master/examples/autolab_example_py_upload/instructor/report2_test.py?inline=false"
             # "https://gitlab.compute.dtu.dk/tuhe/unitgrade_private/-/raw/master/examples/autolab_example_py_upload/instructor/cs102_autolab/report2_test.py?inline=false"
             raw_url = urllib.parse.urljoin(url, os.path.basename(self._file()) + "?inline=false")
+            # raw_url = url
             # print("Is this file run in local mode?", self._is_run_in_grade_mode())
             if self._is_run_in_grade_mode():
                 remote_source = requests.get(raw_url).text
@@ -220,62 +253,73 @@ class Report:
                 if local_source != remote_source:
                     print("\nThe local version of this report is not identical to the remote version which can be found at")
                     print(self.url)
-                    print("The most likely reason for this is that the remote version was updated by the teacher due to some issue.")
-                    print("You should check if there was an announcement and update the test to the most recent version; most likely")
-                    print("This can be done by running the command")
-                    print("> git pull")
+                    print("The most likely reason for this is that the remote version was updated by the teacher due to an issue.")
                     print("You can find the most recent code here:")
                     print(self.url)
-                    raise Exception(f"Version of grade script does not match the remote version. Please update using git pull")
+                    raise Exception(f"Version of grade script does not match the remote version. Please update your grade script.")
             else:
                 text = requests.get(raw_url).text
                 node = ast.parse(text)
                 classes = [n for n in node.body if isinstance(n, ast.ClassDef) if n.name == self.__class__.__name__][0]
+                version_remote = None
                 for b in classes.body:
                     # print(b.)
                     if b.targets[0].id == "version":
-                        # print(b)
-                        # print(b.value)
                         version_remote = b.value.value
                         break
-                if version_remote != self.version:
-                    print("\nThe version of this report", self.version, "does not match the version of the report on git", version_remote)
+
+                if version_remote is not None and version_remote != self.version:
+                    print("\nThe version of this report", self.version, "does not match the version of the report on git:", version_remote)
                     print("The most likely reason for this is that the remote version was updated by the teacher due to some issue.")
-                    print("You should check if there was an announcement and update the test to the most recent version; most likely")
-                    print("This can be done by running the command")
-                    print("> git pull")
-                    print("You can find the most recent code here:")
+                    print("What I am going to do is to download the correct version from git so you are up to date. ")
+
+                    print("You should check if there was an announcement and update the test to the most recent version. This can be done by downloading the files in")
                     print(self.url)
-                    raise Exception(f"Version of test on remote is {version_remote}, which is different than this version of the test {self.version}. Please update your test to the most recent version.")
-
-                for (q,_) in self.questions:
-                    qq = q(skip_remote_check=True)
-                    cfile = q._cache_file()
-
-                    relpath = os.path.relpath(cfile, os.path.dirname(self._file()))
-                    relpath = relpath.replace("\\", "/")
-                    raw_url = urllib.parse.urljoin(url, relpath + "?inline=false")
-                    # requests.get(raw_url)
-
-                    with open(cfile, 'rb') as f:
-                        b1 = f.read()
-
-                    b2 = requests.get(raw_url).content
-                    if b1 != b2:
-                        print("\nQuestion ", qq.title, "relies on the data file", cfile)
-                        print("However, it appears that this file is missing or in a different version than the most recent found here:")
-                        print(self.url)
-                        print("The most likely reason for this is that the remote version was updated by the teacher due to some issue.")
-                        print("You should check if there was an announcement and update the test to the most recent version; most likely")
-                        print("This can be done by simply running the command")
-                        print("> git pull")
-                        print("to avoid running bad tests against good code, the program will now stop. Please update and good luck!")
-                        raise Exception("The data file for the question", qq.title, "did not match remote source found on git. The test will therefore automatically fail. Please update your test/data files.")
-
-                t = time.time()
+                    print("and putting them in the corresponding folder on your computer.")
+                    with open(self._file(), "w") as f:
+                        f.write(text)
+
+                    raise Exception(f"Version of test on remote is {version_remote}, which is different than this version of the test {self.version}. I have manually updated your tests.")
+
+                for (q, _) in self.questions:
+                    if issubclass(q, UTestCase):
+                        qq = q(skip_remote_check=True)
+                        cfile = q._cache_file()
+                        if not os.path.isdir(d_ := os.path.dirname(cfile)):
+                            os.makedirs(d_)  # The unitgrade_data directory does not exist so we create it.
+
+                        relpath = os.path.relpath(cfile, os.path.dirname(self._file()))
+                        relpath = relpath.replace("\\", "/")
+                        raw_url = urllib.parse.urljoin(url, relpath + "?inline=false")
+
+                        if os.path.isfile(cfile):
+                            with open(cfile, 'rb') as f:
+                                b1 = f.read()
+                        else:
+                            b1 = bytes() # No pkl file exists. We set it to the empty string.
+
+                        b2 = requests.get(raw_url).content
+                        if b1 != b2:
+                            print("\nQuestion ", qq.title, "relies on the data file", cfile)
+                            print("However, it appears that this file is missing or in a different version than the most recent found here:")
+                            print(self.url)
+                            print("The most likely reason for this is that the remote version was updated by the teacher.")
+                            print("I will now try to download the file automatically, WCGW?")
+                            with open(cfile, 'wb') as f:
+                                f.write(b2)
+                            print("Local data file updated successfully.")
+                            # print("You should check if there was an announcement and update the test to the most recent version; most likely")
+                            # print("This can be done by simply running the command")
+                            # print("> git pull")
+                            # print("to avoid running bad tests against good code, the program will now stop. Please update and good luck!")
+                            # raise Exception("The data file for the question", qq.title, "did not match remote source found on git. The test will therefore automatically fail. Please update your test/data files.")
+
+                # t = time.time()
                 if os.path.isdir(os.path.dirname(self._file()) + "/unitgrade_data"):
-                    with open(snapshot_file, 'w') as f:
-                        f.write(f"{t}")
+                    db['last_check_time'] = time.time()
+
+                    # with open(snapshot_file, 'w') as f:
+                    #     f.write(f"{t}")
 
 def get_hints(ss):
     """ Extract all blocks of the forms:
@@ -305,21 +349,7 @@ def get_hints(ss):
     except Exception as e:
         print("bad hints", ss, e)
 
-from threading import Thread
-class WandUpload(Thread):
-    # - What do you want to know? What might be of help?
-    # - What errors occur
-    # - How many times each test is run, and how many times it fails
-    # - What kind of errors occur in the tests
-    # - timestamps
-    # For each test, track the number of runs and the different errors
-    # For each test, track which errors many have in common.
-
-    def run(self):
 
-        pass
-
-    pass
 
 class UTestCase(unittest.TestCase):
     # a = 234
@@ -363,10 +393,10 @@ class UTestCase(unittest.TestCase):
         if not self._generate_artifacts:
             return super().run(result)
 
-        print(result)
+        # print(result)
         mute = False
         if isinstance(result, UTextResult):
-            print(result.show_errors_in_grade_mode)
+            # print(result.show_errors_in_grade_mode)
             mute = not result.show_errors_in_grade_mode
         else:
             pass
@@ -376,7 +406,7 @@ class UTestCase(unittest.TestCase):
         from unitgrade.utils import DKPupDB
         self._error_fed_during_run = [] # Initialize this to be empty.
 
-        db = DKPupDB(self._artifact_file(), register_ephemeral=True)
+        db = DKPupDB(self._testcase_artifact_file(), register_ephemeral=True)
         db.set("state", "running")
         db.set('run_id', np.random.randint(1000*1000))
         db.set('coverage_files_changed', None)
@@ -390,13 +420,9 @@ class UTestCase(unittest.TestCase):
         try:
             # Run this unittest and record all of the output.
             # This is probably where we should hijack the stdout output and save it -- after all, this is where the test is actually run.
-            # sys.stdout = stdout_capture
             sys.stderr = std_capture.dummy_stderr
             sys.stdout = std_capture.dummy_stdout
 
-            # db.get('stdout')
-            # db.get('stderr')
-            # db.get("history")
             result_ = TestCase.run(self, result)
             from werkzeug.debug.tbtools import DebugTraceback, _process_traceback
 
@@ -462,7 +488,7 @@ class UTestCase(unittest.TestCase):
             data = self.cov.get_data()
             base, _, _ = self._report._import_base_relative()
             for file in data.measured_files():
-                print(file)
+                # print(file)
                 file = os.path.normpath(file)
                 root = Path(base)
                 child = Path(file)
@@ -486,7 +512,7 @@ class UTestCase(unittest.TestCase):
                         l = ll-1
                         # print(l, lines2[l])
                         if l < len(lines2) and lines2[l].strip() == garb:
-                            print("Got one.")
+                            # print("Got one.")
                             rel = os.path.relpath(child, root)
                             cc = self._covcache
                             j = 0
@@ -500,7 +526,7 @@ class UTestCase(unittest.TestCase):
                             if rel not in cc:
                                 cc[rel] = {}
                             cc[rel][fun] = (l, "\n".join(comments))
-                            print("found", rel, fun)
+                            # print("found", rel, fun)
                             # print(file, ll)
                             self._cache_put((self.cache_id(), 'coverage'), self._covcache)
 
@@ -552,14 +578,10 @@ class UTestCase(unittest.TestCase):
 
         self._load_cache()
         self._assert_cache_index = 0
-        # Perhaps do a sanity check here to see if the cache is up to date? To do that, we must make sure the
-        # cache exists locally.
-        # Find the report class this class is defined within.
         if skip_remote_check:
             return
         import importlib, inspect
         found_reports = []
-
         good_module_name = self.__module__
         try:
             importlib.import_module(good_module_name)
@@ -580,8 +602,14 @@ class UTestCase(unittest.TestCase):
             raise Exception("This question is a member of multiple reports. That should not be the case -- don't get too creative.")
         if len(found_reports) > 0:
             report = found_reports[0]
-            report()._check_remote_versions()
-
+            try:
+                r_ = report()
+                if not r_._is_run_in_grade_mode(): # Disable url request handling during evaluation.
+                    r_._check_remote_versions()
+            except Exception as e:
+                print("Unitgrade> Warning, I tried to compare with the remote source for this report but was unable to do so.")
+                print(e)
+                print("Unitgrade> The exception was", e)
 
     def _ensure_cache_exists(self):
         if not hasattr(self.__class__, '_cache') or self.__class__._cache == None:
@@ -721,9 +749,9 @@ class UTestCase(unittest.TestCase):
         # cf = os.path.dirname(inspect.getabsfile(cls)) + "/unitgrade_data/" + cls.__name__
         return file
 
-    def _artifact_file(self):
-        """ File for the artifacts DB (thread safe). This file is optinal.
-        Note the file is shared between all sub-questions. """
+    def _testcase_artifact_file(self):
+        """ As best as I can tell, this file is only used as an index (key) in the db. For historical reasons it is formatted as a .json
+        but the file will not actually be written to. """
         return os.path.join(os.path.dirname(self.__class__._cache_file()), '-'.join(self.cache_id()) + ".json")
 
     def _save_cache(self):
diff --git a/src/unitgrade/utils.py b/src/unitgrade/utils.py
index 75a7e1097b4c5e209ed1407706dadba17ea11886..e687f07a5115132d04a0f82dc7a3ed25d9a6e65a 100644
--- a/src/unitgrade/utils.py
+++ b/src/unitgrade/utils.py
@@ -287,6 +287,8 @@ def hash_string(s):
     # gfg.update(s.encode("utf-8"))
     # return gfg.digest()
 
+def hash2url(hash):
+    return hash[:16]
 
 def picklestring2dict(picklestr):
     """ Reverse of the above method: Turns the string back into a dictionary. """
@@ -335,6 +337,8 @@ def checkout_remote_results(remote_url, manifest):
     SEP = "-----------"
     remote = {ll[0]: ll[1] for ll in [l.strip().split(" ") for l in html.split(SEP)[1].strip().splitlines()] }
     # lines =
+    # print(remote_url)
+    # print(remote)
 
     mf = [m.strip().split(" ")[-1] for m in manifest.strip().splitlines()]
     a = 23
@@ -342,7 +346,7 @@ def checkout_remote_results(remote_url, manifest):
     url = None
     for hash in reversed(mf):
         if hash_string(hash) in remote:
-            url = f"{remote_url}/{os.path.dirname( remote[hash_string(hash)] )}/{hash}/index.html"
+            url = f"{remote_url}/{os.path.dirname( remote[hash_string(hash)] )}/{hash2url(hash)}/index.html"
             with urllib.request.urlopen(url) as response:
                 html = response.read().decode()
                 # print( html )
@@ -354,7 +358,6 @@ def checkout_remote_results(remote_url, manifest):
         df = dfs[0]
         # df.__format__()
         # tabular
-
         # print( df.to_string(index=False) )
         # df.as
         result = dict(html=html, df=df, score=float( df.iloc[2].to_list()[-1] ), url=url)
@@ -430,10 +433,10 @@ class DKPupDB:
         return item in self.dk[self.name_] #keys()
         # return item in self.dk
 
-if __name__ == "__main__":
-    url = "https://cp.pages.compute.dtu.dk/02002public/_static/evaluation/"
-    manifest = """    
-/home/tuhe/Documents/unitgrade_private/src/unitgrade_private/pipelines/tmp/students/cp/project0/Project0_handin_0_of_10.token 7720b41ab925098956c7db37c8292ce3a7b4ded96f4442234dee493c021fc5f7294e543de78630aaf873b756d25bf7b4fd7eb6e66cec282b54f0c35b83e9071f 
-"""
-    checkout_remote_results(url, manifest = manifest)
+# if __name__ == "__main__":
+#     url = "https://cp.pages.compute.dtu.dk/02002public/_static/evaluation/"
+#     manifest = """
+# /home/tuhe/Documents/unitgrade_private/src/unitgrade_private/pipelines/tmp/students/cp/project0/Project0_handin_0_of_10.token 7720b41ab925098956c7db37c8292ce3a7b4ded96f4442234dee493c021fc5f7294e543de78630aaf873b756d25bf7b4fd7eb6e66cec282b54f0c35b83e9071f
+# """
+#     # checkout_remote_results(url, manifest = manifest)
 
diff --git a/src/unitgrade/version.py b/src/unitgrade/version.py
index c51839a5848e9626d446f40bc4ef062f95896b90..931cb7bbf47e268fc24ebc607e4ba62bf854b722 100644
--- a/src/unitgrade/version.py
+++ b/src/unitgrade/version.py
@@ -1 +1 @@
-__version__ = "0.1.30.9"
+__version__ = "0.1.30.15"