diff --git a/setup.py b/setup.py
index 7a0d08d83ee37bc234d1367a23cdb50f86d8c760..98e942edd0c359b6c8629efbecf1625d7b1ee4e0 100644
--- a/setup.py
+++ b/setup.py
@@ -15,7 +15,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
 # beamer-slider
 setuptools.setup(
     name="coursebox",
-    version="0.1.18.2",
+    version="0.1.18.3",
     author="Tue Herlau",
     author_email="tuhe@dtu.dk",
     description="A course management system currently used at DTU",
diff --git a/src/coursebox.egg-info/PKG-INFO b/src/coursebox.egg-info/PKG-INFO
index 6c31fbb9b73e78b53fe8367d14d6c9e5d6a9aec1..6c13ff80a7ee50f0c91fc71828c7d39338428983 100644
--- a/src/coursebox.egg-info/PKG-INFO
+++ b/src/coursebox.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: coursebox
-Version: 0.1.18.2
+Version: 0.1.18.3
 Summary: A course management system currently used at DTU
 Home-page: https://lab.compute.dtu.dk/tuhe/coursebox
 Author: Tue Herlau
diff --git a/src/coursebox.egg-info/SOURCES.txt b/src/coursebox.egg-info/SOURCES.txt
index 4941d7b6511f261f67a98c8796a31787b27886c4..840504141bfb28bd30dd0fbeeece732d9a012e32 100644
--- a/src/coursebox.egg-info/SOURCES.txt
+++ b/src/coursebox.egg-info/SOURCES.txt
@@ -22,5 +22,7 @@ src/coursebox/material/__init__.py
 src/coursebox/material/homepage_lectures_exercises.py
 src/coursebox/material/lecture_questions.py
 src/coursebox/material/snipper.py
+src/coursebox/student_files/__init__.py
+src/coursebox/student_files/student_files.py
 src/coursebox/testing/__init__.py
 src/coursebox/testing/testing.py
\ No newline at end of file
diff --git a/src/coursebox/core/info_paths.py b/src/coursebox/core/info_paths.py
index dec085b143bec235e4baf439f7353eaf0497ada1..2bd47359137dcc3925a9815c1b02a7387e1e1ea9 100644
--- a/src/coursebox/core/info_paths.py
+++ b/src/coursebox/core/info_paths.py
@@ -21,15 +21,6 @@ def get_paths():
 
     root_02450public = root_02450public.replace("\\", "/")
     root_02450private = root_02450private.replace("\\", "/")
-
-    # if not os.path.isdir(root_02450private):
-    #     root_02450private = f'{root_02450public}/{num}private'
-    #     warn('Private repository not found at the expected location.')
-    #     warn('Using mock info from resources folder at:')
-    #     warn(root_02450private)
-    # Tue: always overwrite semester path.
-    # semester_path = root_02450private +"/resources/mock_semesters/" + semester_id()
-    # else:
     semester_path = root_02450private + "/semesters/" + semester_id()
 
     if not os.path.isdir(semester_path):
diff --git a/src/coursebox/student_files/__init__.py b/src/coursebox/student_files/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/coursebox/student_files/student_files.py b/src/coursebox/student_files/student_files.py
new file mode 100644
index 0000000000000000000000000000000000000000..2a248f53914fa43225aa2959098150eab965094f
--- /dev/null
+++ b/src/coursebox/student_files/student_files.py
@@ -0,0 +1,220 @@
+import os, shutil
+from coursebox.core.info_paths import get_paths
+from coursebox.core.info import class_information
+from snipper.snip_dir import snip_dir
+from coursebox.material.homepage_lectures_exercises import fix_shared
+from snipper.snipper_main import censor_file
+from pathlib import Path
+import fnmatch
+
+def setup_student_files(run_files=True,
+                        cut_files=True,
+                        censor_files=True,
+                        week=None, extra_dirs=None,
+                        include_solutions=None,
+                        projects=None,
+                        setup_lectures=False, strict=True,
+                        fix_shared_files=True,
+                        include_exam_examples=False, # Include the directory with exam-related examples.
+                        verbose=True,
+                        students_irlc_tools=None, # Destination dir for the irlc folder (the one we really want to deploy).
+                        midterms=None, # Can be a list: [0, 1]
+                        include_docs=False, # Include the documentation directory, i.e. 02465public/pythontools/docs.
+                        convert_svg=True, # Whether to convert SVG file when deploying. Leave to true, unless we are doing CI/CD in which case inkscape may not be installed.
+                        package="cp",
+                        fix_shared_files_func=None,
+                        ):
+
+    if midterms is None:
+        midterms = []
+    from coursebox.core import info_paths
+
+    week = [week] if isinstance(week, int) else week
+    if verbose:
+        print("toolbox running. Fixing student files by copying to student dir")
+    paths = get_paths()
+    if include_solutions is None:
+        include_solutions = []
+
+    if projects is None:
+        projects = []
+
+    public_irlc_tools = paths['02450public'] + "/src/cp"
+
+    if students_irlc_tools is None:
+        if censor_files:
+            students_irlc_tools = paths['02450students'] + "/cp"
+        else:
+            students_irlc_tools = paths['02450students'] + "_complete/cp"
+
+    if not os.path.isdir(students_irlc_tools):
+        os.makedirs(students_irlc_tools)
+
+    init_dest = students_irlc_tools+"/__init__.py"
+    shutil.copy(public_irlc_tools + "/__init__.py", init_dest)
+    censor_file(init_dest)
+
+    # Check for exclusion mask.
+    exclude = info_paths.core_conf.get('student_files', {}).get('exclude', [])
+
+    exclude += [f'tests_week{w if w >= 10 else f"0{w}"}.py' for w in range(0,14) if w not in week]
+    exclude = exclude + ["**/" + ex for ex in exclude]
+    if not include_exam_examples:
+        exclude += ['exam_tabular_examples']
+    inclusion = [] # 'ex09old/cache']
+    hws = []
+    for m in midterms:
+        # Copy the midterm from the exam directory into the public repository. This will ensure the midterm is later propagated to the student directory.
+        # The midterm should always be distributed as a .zip file: This is the only legal distribution mechanism.
+        # The midterm in the student directory does not need to be super-duper up to date. This is not the file which is distributed 'officially'.
+        if verbose:
+            print(m)
+
+    if extra_dirs is None:
+        extra_dirs = ['utils', 'tests', 'exam/exam2023spring'] # 'assignments',
+        for m in midterms:
+            if m == 0:
+                extra_dirs += ['exam/midterm2023a']
+            if m == 1:
+                extra_dirs += ['exam/midterm2023b']
+
+        # if include_exam_examples:
+        #     extra_dirs += ['exam_tabular_examples']
+        if setup_lectures:
+            extra_dirs += [
+                # 'lectures/chapter0pythonC',
+                # 'lectures/chapter0pythonA',
+                # 'lectures/chapter0pythonB',
+                # 'lectures/chapter1',
+                'lectures',
+                'workshop'
+            ]
+
+        import coursebox.core.info
+
+        for id in projects:
+            # if setup_projects:
+            if 'projects_all' in info_paths.core_conf:
+                p = info_paths.core_conf['projects_all'][id]['class']
+                extra_dirs += [os.path.basename(os.path.dirname(p.mfile()))]
+            else:
+
+                if id <= 3:
+                    extra_dirs += [f'project{id}'] # , 'project1', 'project2', 'project3']
+                else:
+                    extra_dirs.append(f'project3i')
+
+    if week is not None:
+        for w in week:
+            ex = str(w)
+            ex = "ex" + ("0" + ex if len(ex) < 2 else ex)
+            base = public_irlc_tools +"/"+ex
+            if not os.path.isdir(base):
+                continue
+            d = {'base': base,
+                 'out': students_irlc_tools +"/"+ex,
+                 'exclusion': exclude,
+                 'inclusion': inclusion,
+                 'include_solutions': w in include_solutions,
+                 'week': w}
+            hws.append(d)
+
+        weekn = 101
+        for weekn, d in enumerate(extra_dirs):
+            dutil = {'base': public_irlc_tools + "/" + d,
+                 'out': students_irlc_tools + "/" + d,
+                 'exclusion': exclude,
+                 'inclusion': inclusion,
+                'include_solutions': False,
+                 'week': f'{weekn+100}'}
+            hws.append(dutil)
+        weekn += 1
+
+    else:
+        raise Exception("Specify a week")
+
+    if len(hws) >  0:
+        info = class_information(verbose=verbose)
+        # print("references") # Bad idea because of the very long output.
+        # print(info['references']['commands'])
+    else:
+        info = None
+    for hw in hws:
+        n = fix_hw(paths=paths, info=info, hw=hw, out= hw['out'], output_dir=paths['shared'] +"/output", run_files=run_files, cut_files=cut_files,
+                   package_base_dir=os.path.dirname(students_irlc_tools), censor_files=censor_files, strict=strict,
+                   include_solutions=hw.get('include_solutions', False),
+                   verbose=verbose)
+
+        with open(paths['shared'] + f"/output/lines_{hw['week']}.txt", 'w') as f:
+            f.write(str(n))
+
+
+    if fix_shared_files:
+        if verbose:
+            print("> Homework fixed, copying shared files...")
+        fix_shared_files_func(paths=paths, compile_templates=False, verbose=verbose, dosvg=convert_svg)
+
+    if verbose:
+        print("> Removing excluded files from students gitlab directory...")
+    # for f in exclude + ["lectures"]:
+    for l in list(Path(students_irlc_tools).glob('**/*')):
+        if not os.path.exists(l):  # May have been removed as part of a subtree
+            continue
+        m = [ ex for ex in exclude if fnmatch.fnmatch(l, ex)]
+        if len(m) > 0:
+            if os.path.isdir(l):
+                shutil.rmtree(l)
+            else:
+                os.remove(l)
+
+    shutil.copy(public_irlc_tools +"/../requirements.txt", students_irlc_tools +"/../requirements.txt")
+    # shutil.copy(public_irlc_tools +"/../requirements_conda.txt", students_irlc_tools +"/../requirements_conda.txt")
+    # Don't think we need this anymore. Why copy docs if they cannot be compiled anyway? Perhaps for the complete docs?
+    if include_docs:
+        if os.path.isdir(students_irlc_tools + "/../docs"):
+            shutil.rmtree(students_irlc_tools + "/../docs")
+        # if not censor_files:
+        shutil.copytree(public_irlc_tools + "/../docs", students_irlc_tools + "/../docs")
+        # Fix reference to output.
+        snip_dir(public_irlc_tools + "/../docs/source", students_irlc_tools + "/../docs/source", run_files=False, cut_files=False, censor_files=False, references=info['references'], verbose=verbose)
+    if verbose:
+        print("> All student file stuff completed.")
+
+
+
+def fix_hw(paths, info, hw, out, output_dir, run_files=False, cut_files=False, censor_files=True,
+           include_solutions=False,
+           package_base_dir=None,  # When runing files using #!o, this specify the base directory of the  package the files resides in. Can be None.
+           verbose=True,
+           **kwargs):
+
+    n, cutouts = snip_dir(source_dir=hw['base'], dest_dir=out, output_dir=output_dir, exclude=hw['exclusion'], references=info['references'],
+                 run_files=run_files, cut_files=cut_files, license_head=info.get('code_copyright', None),
+                 censor_files=censor_files,verbose=verbose,package_base_dir=package_base_dir)
+
+    if include_solutions:
+        wk = hw['base'].split("/")[-1]
+        sp = paths['02450students'] + "/solutions/"
+        if not os.path.exists(sp):
+            os.mkdir(sp)
+        sp = sp + wk
+        if os.path.isdir(sp):
+            shutil.rmtree(sp)
+        # if not os.path.exists(sp):
+        os.mkdir(sp)
+
+        for f, cut in cutouts.items():
+            if len(cut) > 0:
+                fname = os.path.basename(f)
+                sols = []
+                stext = ["\n".join(lines) for lines in cut]
+                for i, sol in enumerate(stext):
+                    sols.append((sol,))
+                    sout = sp + f"/{os.path.basename(fname)[:-3]}_TODO_{i + 1}.py"
+                    wsol = True
+                    print(sout, "(published)" if wsol else "")
+                    if wsol:
+                        with open(sout, "w") as f:
+                            f.write(sol)
+
+    return n