diff --git a/local/apcomp297r-cpu.yml.erb b/local/apcomp297r-cpu.yml.erb
new file mode 100644
index 0000000..3a2bcc7
--- /dev/null
+++ b/local/apcomp297r-cpu.yml.erb
@@ -0,0 +1,144 @@
+# Jupyter app user-facing configuration form file (default sub-app)
+
+# This config file is both an actual configuration file for an actual sample
+# application, and a starting point for configuring a new sub-app. To make a
+# custom application launch for a course, make a copy of this file in this same
+# folder ("local" folder under the repository root) and customize the form
+# config to your liking. There are notes along the way to help you understand
+# what options you can make configurable to users. See the docs page from Open
+# OnDemand for more details:
+# https://osc.github.io/ood-documentation/develop/how-tos/app-development/interactive/form.html
+<%-
+# Specify admin groups by the full name of the group in the production HKey environment.
+adminGroups = [
+ "ondemand-admins-1025174" # HUIT OOD admin group, prod environment
+]
+# Specify course groups by Canvas course ID, which you can find in a url:
+# canvas.harvard.edu/courses/000000
+enabledGroups = [
+ # Cannot have multiple enabled groups if a spack installation in the course
+ # shared folder is in use. This is because the course installation uses the
+ # course shared folder, which is not accessible to users outside of that
+ # course.
+ "162018" #APCOMP 297R
+]
+
+def arrays_have_common_element(array1, array2)
+ # Use the `&` operator to get the intersection of the two arrays
+ # If the intersection is not empty, return true, otherwise false
+ !(array1 & array2).empty?
+end
+
+userGroups = OodSupport::User.new.groups.sort_by(&:id).map(&:name)
+# First check if the user is in an admin group
+if arrays_have_common_element(userGroups, adminGroups)
+ cluster="*"
+else
+ # If the user is not in an admin group, check if they're in an authorized Canvas group
+ userCanvasGroups = userGroups.flat_map{ |str| str.scan(/^canvas(\d+)-\d+/) }.flatten
+
+ # Check if the groups that the user is in match any of the courses that should
+ # have access to this app.
+
+ if arrays_have_common_element(userCanvasGroups, enabledGroups)
+ cluster="*"
+ else
+ cluster="disable_this_app"
+ end
+end
+-%>
+---
+
+# Cluster will be set to either "*" (allowing the app to run on any cluster) or
+# "disable_this_app", which will disable the app by only allowing it to run on
+# a non-existant cluster.
+cluster: "<%= cluster %>"
+
+title: "Jupyter Lab - APCOMP 297R (CPU)"
+description: |
+ This app is configured for CPU access for APCOMP 297R.
+
+ This app will launch a Jupyter Notebook server on one or more nodes. This
+ configuration uses [Spack](https://spack.io/) to load a
+ [Mamba](https://mamba.readthedocs.io/en/latest/index.html) environment to then load Jupyter Lab and was built for the course APCOMP 297R.
+
+ The app launches outside of a container, so it has access to slurm commands, but
+ since the app is running as a slurm job, the `srun` command will use the
+ resources of the current job, rather than starting a new job. That is, unless
+ you first run an `salloc` command to allocate a new interactive session.
+
+# Do not cache the application form
+# The intent is to revert to the default Number of CPUs and Hours each time.
+cacheable: false
+
+# Define attributes that are set up by this configuration. If an attribute is
+# set up with a static value here, it will be assigned that value in the job,
+# and will not appear in the form. If the attribute is configured with a widget
+# and/or other options, and it is configured in the form section below, it will
+# appear to users as a configurable option.
+attributes:
+ # Canvas course ID, used to reference a course shared folder when a spack
+ # installation in the course shared folder is in use.
+ course: "162018" #APCOMP 297R
+
+ # Spack environment to activate, regardless of which Spack installation is in use.
+ spack: "mamba"
+
+ # mamba environment to activate from inside the Spack environment
+ mamba: "cs1090b-cpu" # Assuming this mamba environment is built and already created in the spack mamba environment
+
+ # Which queue to submit to. Usually `general` is the best choice, unless this
+ # app is configured for a specific EC2 instance type, like an instance type
+ # with GPU resources.
+ bc_queue: "general"
+
+ # How many times can you say environment before the word loses all meaning?
+ # In this case, this form option toggles between using the global Spack
+ # installation and a spack installation in the course shared folder. If the
+ # course has no intention of managing their own Spack environments, this can
+ # be hard-coded to "global".
+ environment: "global"
+
+
+ # How many GPU GRES resources to include in the job submission.
+ custom_num_gpus: 0
+
+ # How many CPU cores to include in the slurm job submission
+ custom_num_cores:
+ widget: "number_field"
+ label: "Number of CPUs"
+ value: 1
+ min: 1
+ max: 4
+ step: 1
+
+ # How long will the job run for? Configure min, max, and step to set limits
+ # on how long app sessions can run.
+ bc_num_hours:
+ value: 2
+ min: 1
+ max: 6 # 6 hours max
+ step: 1
+
+ # Any extra command line arguments to feed to the `jupyter notebook ...`
+ # command that launches the Jupyter notebook within the batch job
+ extra_jupyter_args: ""
+
+# All of the attributes that make up the Dashboard form (in respective order),
+# and made available to the submit configuration file and the template ERB
+# files
+#
+# @note You typically do not need to modify this unless you want to add a new
+# configurable value
+# @note If an attribute listed below is hard-coded above in the `attributes`
+# option, then it will not appear in the form page that the user sees in the
+# Dashboard
+form:
+ - course
+ - spack
+ - mamba
+ - environment
+ - bc_queue
+ - extra_jupyter_args
+ - bc_num_hours
+ - custom_num_cores
diff --git a/local/apcomp297r-gpu.yml.erb b/local/apcomp297r-gpu.yml.erb
new file mode 100644
index 0000000..81c3b1a
--- /dev/null
+++ b/local/apcomp297r-gpu.yml.erb
@@ -0,0 +1,136 @@
+# Jupyter app user-facing configuration form file (default sub-app)
+
+# This config file is both an actual configuration file for an actual sample
+# application, and a starting point for configuring a new sub-app. To make a
+# custom application launch for a course, make a copy of this file in this same
+# folder ("local" folder under the repository root) and customize the form
+# config to your liking. There are notes along the way to help you understand
+# what options you can make configurable to users. See the docs page from Open
+# OnDemand for more details:
+# https://osc.github.io/ood-documentation/develop/how-tos/app-development/interactive/form.html
+<%-
+# Specify admin groups by the full name of the group in the production HKey environment.
+adminGroups = [
+ "ondemand-admins-1025174" # HUIT OOD admin group, prod environment
+]
+# Specify course groups by Canvas course ID, which you can find in a url:
+# canvas.harvard.edu/courses/000000
+enabledGroups = [
+ # Cannot have multiple enabled groups if a spack installation in the course
+ # shared folder is in use. This is because the course installation uses the
+ # course shared folder, which is not accessible to users outside of that
+ # course.
+ "162018" #APCOMP 297R
+]
+
+def arrays_have_common_element(array1, array2)
+ # Use the `&` operator to get the intersection of the two arrays
+ # If the intersection is not empty, return true, otherwise false
+ !(array1 & array2).empty?
+end
+
+userGroups = OodSupport::User.new.groups.sort_by(&:id).map(&:name)
+# First check if the user is in an admin group
+if arrays_have_common_element(userGroups, adminGroups)
+ cluster="*"
+else
+ # If the user is not in an admin group, check if they're in an authorized Canvas group
+ userCanvasGroups = userGroups.flat_map{ |str| str.scan(/^canvas(\d+)-\d+/) }.flatten
+
+ # Check if the groups that the user is in match any of the courses that should
+ # have access to this app.
+
+ if arrays_have_common_element(userCanvasGroups, enabledGroups)
+ cluster="*"
+ else
+ cluster="disable_this_app"
+ end
+end
+-%>
+---
+
+# Cluster will be set to either "*" (allowing the app to run on any cluster) or
+# "disable_this_app", which will disable the app by only allowing it to run on
+# a non-existant cluster.
+cluster: "<%= cluster %>"
+
+title: "Jupyter Lab - APCOMP 297R (GPU)"
+description: |
+ This app is configured for GPU access for APCOMP 297R.
+
+ This app will launch a Jupyter Notebook server on one or more nodes. This
+ configuration uses [Spack](https://spack.io/) to load a
+ [Mamba](https://mamba.readthedocs.io/en/latest/index.html) environment to then load Jupyter Lab and was built for the course BST 236.
+
+ The app launches outside of a container, so it has access to slurm commands, but
+ since the app is running as a slurm job, the `srun` command will use the
+ resources of the current job, rather than starting a new job. That is, unless
+ you first run an `salloc` command to allocate a new interactive session.
+
+# Do not cache the application form
+# The intent is to revert to the default Number of CPUs and Hours each time.
+cacheable: false
+
+# Define attributes that are set up by this configuration. If an attribute is
+# set up with a static value here, it will be assigned that value in the job,
+# and will not appear in the form. If the attribute is configured with a widget
+# and/or other options, and it is configured in the form section below, it will
+# appear to users as a configurable option.
+attributes:
+ # Canvas course ID, used to reference a course shared folder when a spack
+ # installation in the course shared folder is in use.
+ course: "162018"
+
+ # Spack environment to activate, regardless of which Spack installation is in use.
+ spack: "mamba-gpu"
+
+ # mamba environment to activate from inside the Spack environment
+ mamba: "cs1090b-gpu"
+
+ # Which queue to submit to. Usually `general` is the best choice, unless this
+ # app is configured for a specific EC2 instance type, like an instance type
+ # with GPU resources.
+ bc_queue: "gpu"
+
+ # How many times can you say environment before the word loses all meaning?
+ # In this case, this form option toggles between using the global Spack
+ # installation and a spack installation in the course shared folder. If the
+ # course has no intention of managing their own Spack environments, this can
+ # be hard-coded to "global".
+ environment: "global"
+
+
+ # How many CPU cores to include in the slurm job submission
+ custom_num_cores: 8 # For Stage Testing: 8, For Production: 12
+ # How many GPU GRES resources to include in the job submission.
+ custom_num_gpus: 1
+
+ bc_num_hours:
+ value: 2
+ min: 1
+ max: 6 # 6 hours max
+ step: 1
+
+ # Any extra command line arguments to feed to the `jupyter notebook ...`
+ # command that launches the Jupyter notebook within the batch job
+ extra_jupyter_args: ""
+
+# All of the attributes that make up the Dashboard form (in respective order),
+# and made available to the submit configuration file and the template ERB
+# files
+#
+# @note You typically do not need to modify this unless you want to add a new
+# configurable value
+# @note If an attribute listed below is hard-coded above in the `attributes`
+# option, then it will not appear in the form page that the user sees in the
+# Dashboard
+form:
+ - course
+ - spack
+ - mamba
+ - environment
+ - bc_queue
+ - extra_jupyter_args
+ - bc_num_hours
+ - custom_num_cores
+ - custom_num_gpus