diff --git a/.github/workflows/env_containers.yml b/.github/workflows/env_containers.yml
new file mode 100644
index 0000000000..a779808b37
--- /dev/null
+++ b/.github/workflows/env_containers.yml
@@ -0,0 +1,19 @@
+name: Build and push base grading containers
+
+on:
+ push:
+ branches: [master]
+
+ workflow_dispatch:
+
+jobs:
+ containers_build_and_push:
+ uses: INGInious/.github/.github/workflows/containers.yml@673723b119b1a92695dd5fea1520f5ae28e38abb
+ with:
+ working-directory: base-containers
+ context-path: context.yml
+ compose-path: compose.yml
+ registry: ghcr.io
+ secrets:
+ GHCR_USERNAME: ${{ secrets.GHCR_USERNAME }}
+ GHCR_TOKEN: ${{ secrets.GHCR_TOKEN }}
diff --git a/.gitignore b/.gitignore
index c6095d8153..bbe6b49733 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,3 +27,10 @@ inginious/backup
# Build files
build/lib
build/scripts-*
+
+# frontend
+inginious/frontend/themes/Makefile
+inginious/frontend/themes/test.html
+inginious/frontend/themes/package-lock.json
+inginious/frontend/themes/node_modules
+inginious/frontend/themes/codemirror_scrapper.py
diff --git a/base-containers/base/Dockerfile b/base-containers/base/Dockerfile
index 26338dc0e8..ffed1c2b0e 100644
--- a/base-containers/base/Dockerfile
+++ b/base-containers/base/Dockerfile
@@ -5,6 +5,9 @@ ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
+LABEL org.opencontainers.image.source=https://github.com/UCL-INGI/INGInious-containers
+LABEL org.opencontainers.image.description="Base INGInious grading environment."
+
LABEL org.inginious.grading.agent_version=3
# Install python, needed for scripts used in INGInious + locale support
diff --git a/base-containers/context.yml b/base-containers/context.yml
new file mode 100644
index 0000000000..b2a7d04bf2
--- /dev/null
+++ b/base-containers/context.yml
@@ -0,0 +1,5 @@
+images:
+ base:
+ default:
+ - base
+base_path: "."
diff --git a/base-containers/default/Dockerfile b/base-containers/default/Dockerfile
index 7843f3fcf8..f2500d9ed6 100644
--- a/base-containers/default/Dockerfile
+++ b/base-containers/default/Dockerfile
@@ -2,5 +2,10 @@
#inherit from the base container, which have all the needed script to launch tasks
ARG VERSION=latest
-FROM ingi/inginious-c-base:${VERSION}
+ARG REGISTRY
+FROM ${REGISTRY}/inginious/env-base:${VERSION}
+
+LABEL org.opencontainers.image.source=https://github.com/UCL-INGI/INGInious-containers
+LABEL org.opencontainers.image.description="Default INGInious grading environment."
+
LABEL org.inginious.grading.name="default"
diff --git a/doc/api_doc/inginious.frontend.rst b/doc/api_doc/inginious.frontend.rst
index 3ed34d7bba..0200483268 100644
--- a/doc/api_doc/inginious.frontend.rst
+++ b/doc/api_doc/inginious.frontend.rst
@@ -44,12 +44,12 @@ inginious.frontend.app module
:undoc-members:
:show-inheritance:
-.. _inginious.frontend.course_factory:
+.. _inginious.frontend.taskset_factory:
-inginious.frontend.course_factory module
+inginious.frontend.taskset_factory module
-------------------------------------------
-.. automodule:: inginious.frontend.course_factory
+.. automodule:: inginious.frontend.taskset_factory
:members:
:undoc-members:
:show-inheritance:
diff --git a/doc/dev_doc/extensions_doc/plugins.rst b/doc/dev_doc/extensions_doc/plugins.rst
index 7227414a33..125d0ae4ef 100644
--- a/doc/dev_doc/extensions_doc/plugins.rst
+++ b/doc/dev_doc/extensions_doc/plugins.rst
@@ -25,7 +25,7 @@ The following code adds a new page displaying ``This is a simple demo plugin`` o
return "This is a simple demo plugin"
- def init(plugin_manager, course_factory, client, plugin_config):
+ def init(plugin_manager, taskset_factory, client, plugin_config):
""" Init the plugin """
plugin_manager.add_page("/plugindemo", DemoPage.as_view('demopage'))
@@ -36,8 +36,8 @@ This method takes four arguments:
- ``plugin_manager`` which is the plugin manager singleton object. The detailed API is available at
:ref:`inginious.frontend.plugin_manager`.
-- ``course_factory`` which is the course factory singleton object, giving you abstraction to the tasks folder. The detailed
- API is available at :ref:`inginious.frontend.course_factory`.
+- ``taskset_factory`` which is the course factory singleton object, giving you abstraction to the tasks folder. The detailed
+ API is available at :ref:`inginious.frontend.taskset_factory`.
- ``client`` which is the INGInious client singleton object, giving you access to the backend features, as launching
a new job. The detailed API is available at :ref:`inginious.client.client`.
@@ -78,7 +78,7 @@ would therefore need to add a hook method. This can be done using the ``add_hook
def submission_done(submission, archive, newsub):
logging.getLogger("inginious.frontend.plugins.demo").info("Submission " + str(submission['_id']) + " done.")
- def init(plugin_manager, course_factory, client, plugin_config):
+ def init(plugin_manager, taskset_factory, client, plugin_config):
""" Init the plugin """
plugin_manager.add_hook("submission_done", submission_done)
diff --git a/doc/teacher_doc/course_description.rst b/doc/teacher_doc/course_description.rst
index 5a53565286..df1b03462f 100644
--- a/doc/teacher_doc/course_description.rst
+++ b/doc/teacher_doc/course_description.rst
@@ -25,7 +25,6 @@ file. For instance, this file, for a course with id ``courseid1``, should be pla
admins:
- demouser
name: "[DEMO] Demonstration course"
- tutors: []
groups_student_choice: false
accessible: true
registration: true
@@ -41,11 +40,6 @@ the webapp. Here are the possible fields to set:
- ``admins``
List of administrators usernames. These users will have complete administrations right on the course.
-- ``tutors``
- List of tutors usernames (restricted-rights teaching assistants). These users will have read-only rights on the
- course content. They cannot change course parameters nor tasks, cannot replay submissions or wipe the course data.
- However, they can manage the audience composition and download all the student submissions.
-
- ``accessible``
When this field is defined, the course is only visible if within the defined period.
A course is always accessible to its admins, and is only hidden to normal users,
diff --git a/inginious-autotest b/inginious-autotest
index 01c7cd2715..c1c8138630 100755
--- a/inginious-autotest
+++ b/inginious-autotest
@@ -19,7 +19,7 @@ from inginious.common.tags import Tag
from inginious.frontend.tasks import Task
from inginious.frontend.task_dispensers.toc import TableOfContents
from inginious.common.log import init_logging
-from inginious.frontend.course_factory import create_factories
+from inginious.frontend.taskset_factory import create_factories
from inginious.client.client_sync import ClientSync
from inginious.frontend.arch_helper import start_asyncio_and_zmq, create_arch
from yaml import load
@@ -36,14 +36,14 @@ def import_class(name):
return mod
-def create_client(config, course_factory, fs_provider):
+def create_client(config, taskset_factory, fs_provider):
"""
Create a new client and return it
:param config: dict for configuration
:return: a Client object
"""
zmq_context, t = start_asyncio_and_zmq(config.get("debug_asyncio", False))
- return create_arch(config, fs_provider, zmq_context, course_factory)
+ return create_arch(config, fs_provider, zmq_context, taskset_factory)
def compare_all_outputs(output1, output2, keys):
@@ -120,7 +120,7 @@ def compare_output(output1, output2, key):
return func.get(key, generic_compare)(output1, output2)
-def test_task(yaml_data, task, client, client_sync):
+def test_task(yaml_data, taskset, task, client, client_sync):
"""
Test the task by comparing the new outputs with the old ones
:param yaml_data: dict corresponding to the yaml output file for the task
@@ -133,13 +133,13 @@ def test_task(yaml_data, task, client, client_sync):
time.sleep(1)
if task.get_environment() not in client.get_available_containers():
raise Exception('Environment not available')
- new_output = client_sync.new_job(0, task, yaml_data['input']) # request the client with input from yaml and given task
+ new_output = client_sync.new_job(0, taskset, task, yaml_data['input']) # request the client with input from yaml and given task
keys = ["result", "grade", "problems", "tests", "custom", "state", "archive", "stdout", "stderr"]
old_output = [yaml_data.get(x, None) for x in keys]
return compare_all_outputs(old_output, new_output, keys)
-def test_web_task(yaml_data, task, config, yaml_path):
+def test_web_task(yaml_data, course, task, config, yaml_path):
"""
Test the correctness of the data and task input, i.e. the content does not raise any exception and the rst contents
are compiling
@@ -151,7 +151,7 @@ def test_web_task(yaml_data, task, config, yaml_path):
"""
try:
web_task = Task(
- task.get_course_id(),
+ course.get_id(),
task.get_id(),
yaml_data,
task.get_fs(),
@@ -172,12 +172,12 @@ def test_web_task(yaml_data, task, config, yaml_path):
return yaml_data
-def test_submission_yaml(client, course_factory, path, output, client_sync):
+def test_submission_yaml(client, taskset_factory, path, output, client_sync):
"""
Test the content of a submission.test yaml by comparing it to the output of the client for this task and the same
input.
:param client: Client object
- :param course_factory: CourseFactory object
+ :param taskset_factory: CourseFactory object
:param path: String, path to the submission.test
:param output: dict, output variable
:param client_sync: ClientSync object, client_sync = ClientSync(client)
@@ -186,17 +186,17 @@ def test_submission_yaml(client, course_factory, path, output, client_sync):
# print(os.path.join(test_path, yaml_file.name))
with open(path, 'r') as yaml:
yaml_data = load(yaml, Loader=SafeLoader)
- res = test_task(yaml_data, course_factory.get_task(yaml_data["courseid"], yaml_data["taskid"]), client, client_sync)
+ res = test_task(yaml_data, taskset_factory.get_taskset(yaml_data["courseid"]), taskset_factory.get_task(yaml_data["courseid"], yaml_data["taskid"]), client, client_sync)
if res != {}:
output[path] = res
-def test_task_yaml(path, output, course_factory, task_name, course_name, config):
+def test_task_yaml(path, output, taskset_factory, task_name, course_name, config):
"""
Test the format and content of a task.yaml file and, if incorrect, the data is stored in the output dict
:param path: path to the task.yaml
:param output: output dictionary
- :param course_factory: CourseFactory object
+ :param taskset_factory: CourseFactory object
:param task_name: String, name of the task
:param course_name: String, name of the course
:param config: dict, contains configuration variable
@@ -204,18 +204,18 @@ def test_task_yaml(path, output, course_factory, task_name, course_name, config)
"""
with open(path, 'r') as yaml_file:
yaml_data = load(yaml_file, Loader=SafeLoader)
- res = test_web_task(yaml_data, course_factory.get_task(course_name, task_name), config, path)
+ res = test_web_task(yaml_data, taskset_factory.get_taskset(course_name), taskset_factory.get_task(course_name, task_name), config, path)
if res != {}:
output[path] = res
-def test_all_files(config, client, course_factory):
+def test_all_files(config, client, taskset_factory):
"""
Test each yaml file contained in the dir_path directory, with dir_path specified in the config var, as specified in
the test_task function
:param config: dict for configuration
:param client: backend client of type Client
- :param course_factory: CourseFactory object
+ :param taskset_factory: CourseFactory object
:return: None
"""
test_output = {}
@@ -232,9 +232,9 @@ def test_all_files(config, client, course_factory):
test_files = os.scandir(test_path)
for yaml_file in test_files:
if not yaml_file.name.startswith('.') and yaml_file.is_file(): # Exclude possible failures
- test_submission_yaml(client, course_factory, yaml_file.path, test_output, client_sync)
+ test_submission_yaml(client, taskset_factory, yaml_file.path, test_output, client_sync)
task_yaml_path = os.path.join(task.path, "task.yaml")
- test_task_yaml(task_yaml_path, test_output, course_factory, task.name, os.path.split(dir_path)[1], config)
+ test_task_yaml(task_yaml_path, test_output, taskset_factory, task.name, os.path.split(dir_path)[1], config)
if test_output != {}: # errors in task.yaml ou submission.test
output = json.dumps(test_output)
if "file" in config:
@@ -299,13 +299,13 @@ if __name__ == "__main__":
fs_provider = LocalFSProvider(config["task_directory"])
try:
- course_factory, _ = create_factories(fs_provider, task_dispensers, problem_types) # used for getting tasks
+ taskset_factory, _ = create_factories(fs_provider, task_dispensers, problem_types) # used for getting tasks
- client = create_client(config, course_factory, fs_provider)
+ client = create_client(config, taskset_factory, fs_provider)
client.start()
- test_all_files(config, client, course_factory)
+ test_all_files(config, client, taskset_factory)
except BaseException as e:
print("\nAn error has occured: {}\n".format(e), file=sys.stderr)
diff --git a/inginious-webapp b/inginious-webapp
index ecae80739b..fc4be02cfa 100755
--- a/inginious-webapp
+++ b/inginious-webapp
@@ -58,12 +58,13 @@ if not configfile:
# Load configuration and application (!!! For mod_wsgi, application identifier must be present)
config = load_json_or_yaml(configfile)
-application, close_app_func = inginious.frontend.app.get_app(config)
# Init logging
init_logging(config.get('log_level', 'INFO'))
logging.getLogger("inginious.webapp").info("http://%s:%d/" % (host, int(port)))
+application, close_app_func = inginious.frontend.app.get_app(config)
+
if 'SERVER_SOFTWARE' in os.environ: # cgi
os.environ['FCGI_FORCE_CGI'] = 'Y'
diff --git a/inginious-webdav b/inginious-webdav
index 9a332f8f6a..5403f0f709 100755
--- a/inginious-webdav
+++ b/inginious-webdav
@@ -51,11 +51,11 @@ if not configfile:
# Load configuration and application (!!! For mod_wsgi, application identifier must be present)
config = load_json_or_yaml(configfile)
-application = inginious.frontend.webdav.get_app(config)
-
# Init logging
init_logging(config.get('log_level', 'INFO'))
logging.getLogger("inginious.webdav").info("http://%s:%d/" % (host, int(port)))
+application = inginious.frontend.webdav.get_app(config)
+
if 'SERVER_SOFTWARE' in os.environ: # cgi
os.environ['FCGI_FORCE_CGI'] = 'Y'
diff --git a/inginious/__init__.py b/inginious/__init__.py
index e5a435240c..00ba212ad3 100644
--- a/inginious/__init__.py
+++ b/inginious/__init__.py
@@ -16,7 +16,7 @@
try:
__version__ = version(__name__)
except PackageNotFoundError:
- __version__ = "0.7.dev0"
+ __version__ = "0.9.dev0"
MARKETPLACE_URL = "https://marketplace.inginious.org/marketplace.json"
DB_VERSION = 15
diff --git a/inginious/agent/__init__.py b/inginious/agent/__init__.py
index c2d20a5046..cad9950fba 100644
--- a/inginious/agent/__init__.py
+++ b/inginious/agent/__init__.py
@@ -167,15 +167,15 @@ async def __handle_new_job(self, message: BackendNewJob):
try:
if message.environment_type not in self.environments or message.environment not in self.environments[message.environment_type]:
- self._logger.warning("Task %s/%s ask for an unknown environment %s/%s", message.course_id, message.task_id,
+ self._logger.warning("Task %s/%s ask for an unknown environment %s/%s", message.taskset_id, message.task_id,
message.environment_type, message.environment)
- raise CannotCreateJobException('This environment is not available in this agent. Please contact your course administrator.')
+ raise CannotCreateJobException('This environment is not available in this agent. Please contact the taskset administrator.')
- task_fs = self._fs.from_subfolder(message.course_id).from_subfolder(message.task_id)
+ task_fs = self._fs.from_subfolder(message.taskset_id).from_subfolder(message.task_id)
if not task_fs.exists():
- self._logger.warning("Task %s/%s unavailable on this agent", message.course_id, message.task_id)
+ self._logger.warning("Task %s/%s unavailable on this agent", message.taskset_id, message.task_id)
raise CannotCreateJobException('Task unavailable on agent. Please retry later, the agents should synchronize soon. If the error '
- 'persists, please contact your course administrator.')
+ 'persists, please contact the taskset administrator.')
# Let the subclass run the job
await self.new_job(message)
@@ -184,14 +184,14 @@ async def __handle_new_job(self, message: BackendNewJob):
except TooManyCallsException:
self._logger.exception("TooManyCallsException in new_job")
await self.send_job_result(job_id=message.job_id, result="crash",
- text="An unknown error occurred in the agent. Please contact your course administrator.",
+ text="An unknown error occurred in the agent. Please contact the taskset administrator.",
state=previous_state)
except JobNotRunningException:
self._logger.exception("JobNotRunningException in new_job")
except:
self._logger.exception("Unknown exception in new_job")
await self.send_job_result(job_id=message.job_id, result="crash",
- text="An unknown error occurred in the agent. Please contact your course administrator.",
+ text="An unknown error occurred in the agent. Please contact the taskset administrator.",
state=previous_state)
async def send_ssh_job_info(self, job_id: BackendJobId, host: str, port: int, username: str, key: str):
diff --git a/inginious/agent/docker_agent/__init__.py b/inginious/agent/docker_agent/__init__.py
index 7696ded2ff..b242e05a60 100644
--- a/inginious/agent/docker_agent/__init__.py
+++ b/inginious/agent/docker_agent/__init__.py
@@ -45,7 +45,7 @@ class DockerRunningJob:
sockets_path: str
student_path: str
systemfiles_path: str
- course_common_student_path: str
+ taskset_common_student_path: str
run_cmd: str
assigned_external_ports: List[int]
student_containers: Set[str] # container ids of student containers
@@ -71,7 +71,7 @@ def __init__(self, context, backend_addr, friendly_name, concurrency, tasks_fs:
:param backend_addr: address of the backend (for example, "tcp://127.0.0.1:2222")
:param friendly_name: a string containing a friendly name to identify agent
:param concurrency: number of simultaneous jobs that can be run by this agent
- :param tasks_fs: FileSystemProvider for the course / tasks
+ :param tasks_fs: FileSystemProvider for the taskset / tasks
:param address_host: hostname/ip/... to which external client should connect to access to the docker
:param external_ports: iterable containing ports to which the docker instance can bind internal ports
:param tmp_dir: temp dir that is used by the agent to start new containers
@@ -273,7 +273,7 @@ def __get_fd_limit(self):
def __new_job_sync(self, message: BackendNewJob, future_results):
""" Synchronous part of _new_job. Creates needed directories, copy files, and starts the container. """
- course_id = message.course_id
+ taskset_id = message.taskset_id
task_id = message.task_id
debug = message.debug
@@ -290,28 +290,28 @@ def __new_job_sync(self, message: BackendNewJob, future_results):
except:
raise CannotCreateJobException('The agent is unable to parse the parameters')
- course_fs = self._fs.from_subfolder(course_id)
- task_fs = course_fs.from_subfolder(task_id)
+ taskset_fs = self._fs.from_subfolder(taskset_id)
+ task_fs = taskset_fs.from_subfolder(task_id)
- if not course_fs.exists() or not task_fs.exists():
- self._logger.warning("Task %s/%s unavailable on this agent", course_id, task_id)
+ if not taskset_fs.exists() or not task_fs.exists():
+ self._logger.warning("Task %s/%s unavailable on this agent", taskset_id, task_id)
raise CannotCreateJobException(
'Task unavailable on agent. Please retry later, the agents should synchronize soon. '
- 'If the error persists, please contact your course administrator.')
+ 'If the error persists, please contact the taskset administrator.')
# Check for realistic memory limit value
if mem_limit < 20:
mem_limit = 20
elif mem_limit > self._max_memory_per_slot:
- self._logger.warning("Task %s/%s ask for too much memory (%dMB)! Available: %dMB", course_id, task_id,
+ self._logger.warning("Task %s/%s ask for too much memory (%dMB)! Available: %dMB", taskset_id, task_id,
mem_limit, self._max_memory_per_slot)
raise CannotCreateJobException(
- 'Not enough memory on agent (available: %dMB). Please contact your course administrator.' % self._max_memory_per_slot)
+ 'Not enough memory on agent (available: %dMB). Please contact the taskset administrator.' % self._max_memory_per_slot)
if environment_type not in self._containers or environment_name not in self._containers[environment_type]:
- self._logger.warning("Task %s/%s ask for an unknown environment %s/%s", course_id, task_id,
+ self._logger.warning("Task %s/%s ask for an unknown environment %s/%s", taskset_id, task_id,
environment_type, environment_name)
- raise CannotCreateJobException('Unknown container. Please contact your course administrator.')
+ raise CannotCreateJobException('Unknown container. Please contact the taskset administrator.')
environment = self._containers[environment_type][environment_name]["id"]
runtime = self._containers[environment_type][environment_name]["runtime"]
@@ -342,20 +342,20 @@ def __new_job_sync(self, message: BackendNewJob, future_results):
raise CannotCreateJobException('Cannot make container temp directory.')
task_path = path_join(container_path, 'task') # tmp_dir/id/task/
- course_path = path_join(container_path, 'course')
+ taskset_path = path_join(container_path, 'course')
sockets_path = path_join(container_path, 'sockets') # tmp_dir/id/socket/
student_path = path_join(task_path, 'student') # tmp_dir/id/task/student/
systemfiles_path = path_join(task_path, 'systemfiles') # tmp_dir/id/task/systemfiles/
- course_common_path = path_join(course_path, 'common')
- course_common_student_path = path_join(course_path, 'common', 'student')
+ taskset_common_path = path_join(taskset_path, 'common')
+ taskset_common_student_path = path_join(taskset_path, 'common', 'student')
# Create the needed directories
os.mkdir(sockets_path)
os.chmod(container_path, 0o777)
os.chmod(sockets_path, 0o777)
- os.mkdir(course_path)
+ os.mkdir(taskset_path)
# TODO: avoid copy
task_fs.copy_from(None, task_path)
@@ -367,21 +367,21 @@ def __new_job_sync(self, message: BackendNewJob, future_results):
# Copy common and common/student if needed
# TODO: avoid copy
- if course_fs.from_subfolder("$common").exists():
- course_fs.from_subfolder("$common").copy_from(None, course_common_path)
+ if taskset_fs.from_subfolder("$common").exists():
+ taskset_fs.from_subfolder("$common").copy_from(None, taskset_common_path)
else:
- os.mkdir(course_common_path)
+ os.mkdir(taskset_common_path)
- if course_fs.from_subfolder("$common").from_subfolder("student").exists():
- course_fs.from_subfolder("$common").from_subfolder("student").copy_from(None, course_common_student_path)
+ if taskset_fs.from_subfolder("$common").from_subfolder("student").exists():
+ taskset_fs.from_subfolder("$common").from_subfolder("student").copy_from(None, taskset_common_student_path)
else:
- os.mkdir(course_common_student_path)
+ os.mkdir(taskset_common_student_path)
# Run the container
try:
container_id = self._docker.sync.create_container(environment, enable_network, mem_limit, task_path,
- sockets_path, course_common_path,
- course_common_student_path,
+ sockets_path, taskset_common_path,
+ taskset_common_student_path,
self.__get_fd_limit(), runtime,
ports)
except Exception as e:
@@ -409,7 +409,7 @@ def __new_job_sync(self, message: BackendNewJob, future_results):
sockets_path=sockets_path,
student_path=student_path,
systemfiles_path=systemfiles_path,
- course_common_student_path=course_common_student_path,
+ taskset_common_student_path=taskset_common_student_path,
run_cmd=run_cmd,
assigned_external_ports=list(ports.values()),
student_containers=set(),
@@ -489,7 +489,7 @@ async def create_student_container(self, parent_info, socket_id, environment_nam
memory_limit, parent_info.student_path,
socket_path,
parent_info.systemfiles_path,
- parent_info.course_common_student_path,
+ parent_info.taskset_common_student_path,
parent_info.environment_type,
self.__get_fd_limit(),
parent_info.container_id if share_network else None,
diff --git a/inginious/agent/docker_agent/_docker_interface.py b/inginious/agent/docker_agent/_docker_interface.py
index 67e6cfa218..32363397fb 100644
--- a/inginious/agent/docker_agent/_docker_interface.py
+++ b/inginious/agent/docker_agent/_docker_interface.py
@@ -110,7 +110,7 @@ def get_host_ip(self, env_with_dig='ingi/inginious-c-default'):
return None
def create_container(self, image, network_grading, mem_limit, task_path, sockets_path,
- course_common_path, course_common_student_path, fd_limit, runtime: str, ports=None):
+ taskset_common_path, taskset_common_student_path, fd_limit, runtime: str, ports=None):
"""
Creates a container.
:param image: env to start (name/id of a docker image)
@@ -118,8 +118,8 @@ def create_container(self, image, network_grading, mem_limit, task_path, sockets
:param mem_limit: in Mo
:param task_path: path to the task directory that will be mounted in the container
:param sockets_path: path to the socket directory that will be mounted in the container
- :param course_common_path:
- :param course_common_student_path:
+ :param taskset_common_path:
+ :param taskset_common_student_path:
:param fd_limit: Tuple with soft and hard limits per slot for FS
:param runtime: name of the docker runtime to use
:param ports: dictionary in the form {docker_port: external_port}
@@ -127,8 +127,8 @@ def create_container(self, image, network_grading, mem_limit, task_path, sockets
"""
task_path = os.path.abspath(task_path)
sockets_path = os.path.abspath(sockets_path)
- course_common_path = os.path.abspath(course_common_path)
- course_common_student_path = os.path.abspath(course_common_student_path)
+ taskset_common_path = os.path.abspath(taskset_common_path)
+ taskset_common_student_path = os.path.abspath(taskset_common_student_path)
if ports is None:
ports = {}
@@ -146,8 +146,8 @@ def create_container(self, image, network_grading, mem_limit, task_path, sockets
volumes={
task_path: {'bind': '/task'},
sockets_path: {'bind': '/sockets'},
- course_common_path: {'bind': '/course/common', 'mode': 'ro'},
- course_common_student_path: {'bind': '/course/common/student', 'mode': 'ro'}
+ taskset_common_path: {'bind': '/course/common', 'mode': 'ro'},
+ taskset_common_student_path: {'bind': '/course/common/student', 'mode': 'ro'}
},
runtime=runtime,
ulimits=[nofile_limit]
@@ -155,7 +155,7 @@ def create_container(self, image, network_grading, mem_limit, task_path, sockets
return response.id
def create_container_student(self, runtime: str, image: str, mem_limit, student_path,
- socket_path, systemfiles_path, course_common_student_path,
+ socket_path, systemfiles_path, taskset_common_student_path,
parent_runtime: str,fd_limit, share_network_of_container: str=None, ports=None):
"""
Creates a student container
@@ -166,7 +166,7 @@ def create_container_student(self, runtime: str, image: str, mem_limit, student_
:param student_path: path to the task directory that will be mounted in the container
:param socket_path: path to the socket that will be mounted in the container
:param systemfiles_path: path to the systemfiles folder containing files that can override partially some defined system files
- :param course_common_student_path:
+ :param taskset_common_student_path:
:param share_network_of_container: (deprecated) if a container id is given, the new container will share its
network stack.
:param ports: dictionary in the form {docker_port: external_port}
@@ -175,7 +175,7 @@ def create_container_student(self, runtime: str, image: str, mem_limit, student_
student_path = os.path.abspath(student_path)
socket_path = os.path.abspath(socket_path)
systemfiles_path = os.path.abspath(systemfiles_path)
- course_common_student_path = os.path.abspath(course_common_student_path)
+ taskset_common_student_path = os.path.abspath(taskset_common_student_path)
secured_scripts_path = student_path+"/scripts"
if ports is None:
@@ -205,7 +205,7 @@ def create_container_student(self, runtime: str, image: str, mem_limit, student_
secured_scripts_path: {'bind': '/task/student/scripts'},
socket_path: {'bind': '/__parent.sock'},
systemfiles_path: {'bind': '/task/systemfiles', 'mode': 'ro'},
- course_common_student_path: {'bind': '/course/common/student', 'mode': 'ro'}
+ taskset_common_student_path: {'bind': '/course/common/student', 'mode': 'ro'}
},
runtime=runtime,
ulimits=[nofile_limit]
diff --git a/inginious/agent/mcq_agent/__init__.py b/inginious/agent/mcq_agent/__init__.py
index f468b86d57..d406192e52 100644
--- a/inginious/agent/mcq_agent/__init__.py
+++ b/inginious/agent/mcq_agent/__init__.py
@@ -19,7 +19,7 @@ def __init__(self, context, backend_addr, friendly_name, concurrency, tasks_file
:param context: ZeroMQ context for this process
:param backend_addr: address of the backend (for example, "tcp://127.0.0.1:2222")
:param friendly_name: a string containing a friendly name to identify agent
- :param tasks_filesystem: FileSystemProvider to the course/tasks
+ :param tasks_filesystem: FileSystemProvider to the taskset/tasks
:param problem_types: Problem types dictionary
"""
super().__init__(context, backend_addr, friendly_name, concurrency, tasks_filesystem)
@@ -78,15 +78,15 @@ async def new_job(self, msg: BackendNewJob):
# This may pose problem with apps that start multiple MCQAgents in the same process...
builtins.__dict__['_'] = translation.gettext
- course_fs = self._fs.from_subfolder(msg.course_id)
- task_fs = course_fs.from_subfolder(msg.task_id)
+ taskset_fs = self._fs.from_subfolder(msg.taskset_id)
+ task_fs = taskset_fs.from_subfolder(msg.task_id)
translations_fs = task_fs.from_subfolder("$i18n")
if not translations_fs.exists():
translations_fs = task_fs.from_subfolder("student").from_subfolder("$i18n")
if not translations_fs.exists():
- translations_fs = course_fs.from_subfolder("$common").from_subfolder("$i18n")
+ translations_fs = taskset_fs.from_subfolder("$common").from_subfolder("$i18n")
if not translations_fs.exists():
- translations_fs = course_fs.from_subfolder("$common").from_subfolder("student")\
+ translations_fs = taskset_fs.from_subfolder("$common").from_subfolder("student")\
.from_subfolder("$i18n")
if translations_fs.exists() and translations_fs.exists(language + ".mo"):
@@ -113,7 +113,7 @@ async def new_job(self, msg: BackendNewJob):
problems[key] = (p_result, "\n\n".join(messages))
if need_emul:
- self._logger.warning("Task %s/%s is not a pure MCQ but has env=MCQ", msg.course_id, msg.task_id)
+ self._logger.warning("Task %s/%s is not a pure MCQ but has env=MCQ", msg.taskset_id, msg.task_id)
raise CannotCreateJobException("Task wrongly configured as a MCQ")
if error_count != 0:
diff --git a/inginious/backend/backend.py b/inginious/backend/backend.py
index 4fd46db935..1764858eac 100644
--- a/inginious/backend/backend.py
+++ b/inginious/backend/backend.py
@@ -171,12 +171,12 @@ async def handle_client_get_queue(self, client_addr, _: ClientGetQueue):
for job_id, content in self._job_running.items():
agent_friendly_name = self._registered_agents[content.agent_addr].name
jobs_running.append((content.msg.job_id, content.client_addr == client_addr, agent_friendly_name,
- content.msg.course_id+"/"+content.msg.task_id,
+ content.msg.taskset_id+"/"+content.msg.task_id,
content.msg.launcher, int(content.time_started), self._get_time_limit_estimate(content.msg)))
#jobs_waiting: a list of tuples in the form
#(job_id, is_current_client_job, info, launcher, max_time)
- jobs_waiting = [(job.job_id, job.client_addr == client_addr, job.msg.course_id+"/"+job.msg.task_id, job.msg.launcher,
+ jobs_waiting = [(job.job_id, job.client_addr == client_addr, job.msg.taskset_id+"/"+job.msg.task_id, job.msg.launcher,
self._get_time_limit_estimate(job.msg)) for job in self._waiting_jobs.values()]
await ZMQUtils.send_with_addr(self._client_socket, client_addr, BackendGetQueue(jobs_running, jobs_waiting))
@@ -216,7 +216,7 @@ async def update_queue(self):
# Send the job to agent
self._job_running[job_id] = RunningJob(agent_addr, client_addr, job_msg, time.time())
self._logger.info("Sending job %s %s to agent %s", client_addr, job_id, agent_addr)
- await ZMQUtils.send_with_addr(self._agent_socket, agent_addr, BackendNewJob(job_id, job_msg.course_id, job_msg.task_id,
+ await ZMQUtils.send_with_addr(self._agent_socket, agent_addr, BackendNewJob(job_id, job_msg.taskset_id, job_msg.task_id,
job_msg.task_problems, job_msg.inputdata,
job_msg.environment_type,
job_msg.environment,
diff --git a/inginious/client/client.py b/inginious/client/client.py
index 2d0823fcb5..4778253439 100644
--- a/inginious/client/client.py
+++ b/inginious/client/client.py
@@ -47,7 +47,7 @@ def get_available_environments(self) -> Dict[str, List[str]]:
pass
@abstractmethod
- def new_job(self, task, inputdata, callback, launcher_name="Unknown", debug=False, ssh_callback=None):
+ def new_job(self, priority, taskset, task, inputdata, callback, launcher_name="Unknown", debug=False, ssh_callback=None):
""" Add a new job. Every callback will be called once and only once.
:type task: Task
@@ -97,7 +97,7 @@ def get_job_queue_snapshot(self):
- job_id is a job id. It may be from another client.
- is_current_client_job is a boolean indicating if the client that asked the request has started the job
- agent_name is the agent name
- - info is "courseid/taskid"
+ - info is "tasksetid/taskid"
- launcher is the name of the launcher, which may be anything
- started_at the time (in seconds since UNIX epoch) at which the job started
- max_time the maximum time that can be used, or -1 if no timeout is set
@@ -108,7 +108,7 @@ def get_job_queue_snapshot(self):
- job_id is a job id. It may be from another client.
- is_current_client_job is a boolean indicating if the client that asked the request has started the job
- - info is "courseid/taskid"
+ - info is "tasksetid/taskid"
- launcher is the name of the launcher, which may be anything
- max_time the maximum time that can be used, or -1 if no timeout is set
@@ -269,10 +269,11 @@ def get_available_environments(self) -> Dict[str, List[str]]:
"""
return self._available_environments
- def new_job(self, priority, task, inputdata, callback, launcher_name="Unknown", debug=False, ssh_callback=None):
+ def new_job(self, priority, taskset, task, inputdata, callback, launcher_name="Unknown", debug=False, ssh_callback=None):
""" Add a new job. Every callback will be called once and only once.
:param priority: Priority of the job
- :type task: Task
+ :param taskset : Taskset
+ :param task: Task
:param inputdata: input from the student
:type inputdata: Storage or dict
:param callback: a function that will be called asynchronously in the client's process, with the results.
@@ -297,7 +298,7 @@ def new_job(self, priority, task, inputdata, callback, launcher_name="Unknown",
safe_callback = _callable_once(callback)
if debug == "ssh" and ssh_callback is None:
- self._logger.error("SSH callback not set in %s/%s", task.get_course_id(), task.get_id())
+ self._logger.error("SSH callback not set in %s/%s", taskset.get_id(), task.get_id())
safe_callback(("crash", "SSH callback not set."), 0.0, {}, {}, {}, None, "", "")
return None
# wrap ssh_callback to ensure it is called at most once, and that it can always be called to simplify code
@@ -307,7 +308,7 @@ def new_job(self, priority, task, inputdata, callback, launcher_name="Unknown",
environment = task.get_environment_id()
if environment_type not in self._available_environments or environment not in self._available_environments[environment_type]:
- self._logger.warning("Env %s/%s not available for task %s/%s", environment_type, environment, task.get_course_id(),
+ self._logger.warning("Env %s/%s not available for task %s/%s", environment_type, environment, taskset.get_id(),
task.get_id())
ssh_callback(None, None, None, None) # ssh_callback must be called once
safe_callback(("crash", "Environment not available."), 0.0, {}, {}, "", {}, None, "", "")
@@ -315,7 +316,7 @@ def new_job(self, priority, task, inputdata, callback, launcher_name="Unknown",
environment_parameters = task.get_environment_parameters()
- msg = ClientNewJob(job_id, priority, task.get_course_id(), task.get_id(), task.get_problems_dict(), inputdata,
+ msg = ClientNewJob(job_id, priority, taskset.get_id(), task.get_id(), task.get_problems_dict(), inputdata,
environment_type, environment, environment_parameters, debug, launcher_name)
self._loop.call_soon_threadsafe(asyncio.ensure_future,
self._create_transaction(msg, task=task, callback=safe_callback,
diff --git a/inginious/client/client_buffer.py b/inginious/client/client_buffer.py
index fd6b325040..905397f394 100644
--- a/inginious/client/client_buffer.py
+++ b/inginious/client/client_buffer.py
@@ -16,11 +16,11 @@ def __init__(self, client):
self._waiting_jobs = []
self._jobs_done = {}
- def new_job(self, priority, task, inputdata, launcher_name="Unknown", debug=False):
+ def new_job(self, priority, taskset, task, inputdata, launcher_name="Unknown", debug=False):
""" Runs a new job. It works exactly like the Client class, instead that there is no callback """
bjobid = uuid.uuid4()
self._waiting_jobs.append(str(bjobid))
- self._client.new_job(priority, task, inputdata,
+ self._client.new_job(priority, taskset, task, inputdata,
(lambda result, grade, problems, tests, custom, archive, stdout, stderr:
self._callback(bjobid, result, grade, problems, tests, custom, archive, stdout, stderr)),
launcher_name, debug)
diff --git a/inginious/client/client_sync.py b/inginious/client/client_sync.py
index 2d42d6d52f..e1fe79b31c 100644
--- a/inginious/client/client_sync.py
+++ b/inginious/client/client_sync.py
@@ -13,7 +13,7 @@ class ClientSync(object):
def __init__(self, client):
self._client = client
- def new_job(self, priority, task, inputdata, launcher_name="Unknown", debug=False):
+ def new_job(self, priority, taskset, task, inputdata, launcher_name="Unknown", debug=False):
"""
Runs a new job.
It works exactly like the Client class, instead that there is no callback and directly returns result, in the form of a tuple
@@ -28,7 +28,7 @@ def manage_output(result, grade, problems, tests, custom, state, archive, stdout
manage_output.job_return = None
- self._client.new_job(priority, task, inputdata, manage_output, launcher_name, debug)
+ self._client.new_job(priority, taskset, task, inputdata, manage_output, launcher_name, debug)
job_semaphore.acquire()
job_return = manage_output.job_return
return job_return
diff --git a/inginious/common/base.py b/inginious/common/base.py
index c75c763b70..9b116c8a28 100644
--- a/inginious/common/base.py
+++ b/inginious/common/base.py
@@ -112,7 +112,7 @@ def dict_from_prefix(prefix, dictionary):
>>> od["problem[q0][b][c]"]=2
>>> od["problem[q1][first]"]=1
>>> od["problem[q1][second]"]=2
- >>> AdminCourseEditTask.dict_from_prefix("problem",od)
+ >>> dict_from_prefix("problem",od)
OrderedDict([('q0', OrderedDict([('a', 1), ('b', OrderedDict([('c', 2)]))])), ('q1', OrderedDict([('first', 1), ('second', 2)]))])
"""
o_dictionary = OrderedDict()
diff --git a/inginious/common/exceptions.py b/inginious/common/exceptions.py
index 51c9355343..96040d4926 100644
--- a/inginious/common/exceptions.py
+++ b/inginious/common/exceptions.py
@@ -10,32 +10,17 @@ class InvalidNameException(Exception):
pass
-class CourseNotFoundException(Exception):
- pass
-
-
class TaskNotFoundException(Exception):
pass
-class CourseUnreadableException(Exception):
- pass
-
-
-class CourseAlreadyExistsException(Exception):
- pass
-
class TaskAlreadyExistsException(Exception):
pass
+
class TaskUnreadableException(Exception):
pass
class TaskReaderNotFoundException(Exception):
pass
-
-
-class ImportCourseException(Exception):
- pass
-
diff --git a/inginious/common/filesystems/local.py b/inginious/common/filesystems/local.py
index 88dfc0a02e..3b133a5565 100644
--- a/inginious/common/filesystems/local.py
+++ b/inginious/common/filesystems/local.py
@@ -25,7 +25,7 @@ def get_needed_args(cls):
Only int and str are supported as types.
"""
return {
- "location": (str, True, "On-disk path to the directory containing courses/tasks")
+ "location": (str, True, "On-disk path to the directory containing tasksets/tasks")
}
@classmethod
diff --git a/inginious/common/log.py b/inginious/common/log.py
index f919e9ffa1..3952914dfe 100644
--- a/inginious/common/log.py
+++ b/inginious/common/log.py
@@ -29,14 +29,6 @@ def init_logging(log_level=logging.DEBUG):
oauthlib_log.setLevel(log_level)
oauthlib_log.addHandler(ch)
-def get_course_logger(coursename):
- """
- :param coursename: the course id
- :return: a logger object associated to a specific course
- """
- return logging.getLogger("inginious.course."+coursename)
-
-
class CustomLogMiddleware:
""" WSGI middleware for logging the status in webpy"""
diff --git a/inginious/common/messages.py b/inginious/common/messages.py
index eecfab9f32..fbf5debfe0 100644
--- a/inginious/common/messages.py
+++ b/inginious/common/messages.py
@@ -31,7 +31,7 @@ class ClientNewJob:
""" Creates a new job """
job_id: ClientJobId # the client-side job id that is associated to this job
priority: int # the job priority
- course_id: str # course id of the task to run
+ taskset_id: str # taskset id of the task to run
task_id: str # task id of the task to run
task_problems: Dict[str, Any] # task dictionary
inputdata: Dict[str, Any] # student input data
@@ -117,7 +117,7 @@ class BackendGetQueue:
- job_id is a job id. It may be from another client.
- is_current_client_job is a boolean indicating if the client that asked the request has started the job
- agent_name is the agent name
- - info is "courseid/taskid"
+ - info is "tasksetid/taskid"
- launcher is the name of the launcher, which may be anything
- started_at the time (in seconds since UNIX epoch) at which the job started
- max_time the maximum time that can be used, or -1 if no timeout is set
@@ -128,7 +128,7 @@ class BackendGetQueue:
- job_id is a job id. It may be from another client.
- is_current_client_job is a boolean indicating if the client that asked the request has started the job
- - info is "courseid/taskid"
+ - info is "tasksetid/taskid"
- launcher is the name of the launcher, which may be anything
- max_time the maximum time that can be used, or -1 if no timeout is set
@@ -148,7 +148,7 @@ class BackendGetQueue:
class BackendNewJob:
""" Creates a new job """
job_id: BackendJobId # the backend-side job id that is associated to this job
- course_id: str # course id of the task to run
+ taskset_id: str # taskset id of the task to run
task_id: str # task id of the task to run
task_problems: Dict[str, Any] # task dictionary
inputdata: Dict[str, Any] # student input data
diff --git a/inginious/frontend/app.py b/inginious/frontend/app.py
index 5f4e8c522b..780833d5ad 100644
--- a/inginious/frontend/app.py
+++ b/inginious/frontend/app.py
@@ -18,6 +18,7 @@
from werkzeug.exceptions import InternalServerError
import inginious.frontend.pages.course_admin.utils as course_admin_utils
+import inginious.frontend.pages.taskset_admin.utils as taskset_admin_utils
import inginious.frontend.pages.preferences.utils as preferences_utils
from inginious.frontend.environment_types import register_base_env_types
from inginious.frontend.arch_helper import create_arch, start_asyncio_and_zmq
@@ -29,7 +30,7 @@
from inginious.frontend.user_manager import UserManager
from inginious.frontend.l10n_manager import L10nManager
from inginious import get_root_path, __version__, DB_VERSION
-from inginious.frontend.course_factory import create_factories
+from inginious.frontend.taskset_factory import create_factories
from inginious.common.entrypoints import filesystem_from_config_dict
from inginious.common.filesystems.local import LocalFSProvider
from inginious.frontend.lti_outcome_manager import LTIOutcomeManager
@@ -175,6 +176,15 @@ def get_app(config):
available_languages = {"en": "English"}
available_languages.update(available_translations)
+ available_themes = [
+ theme_dir for theme_dir in os.listdir(os.path.join(get_root_path(), 'frontend', 'static', 'css', 'themes'))
+ if theme_dir not in ('codemirror', ) and not theme_dir.startswith(('.', '_'))
+ ]
+ available_codemirror_themes= [
+ file.split('.')[0] for file in os.listdir(os.path.join(get_root_path(), 'frontend', 'static', 'css', 'themes', 'codemirror'))
+ if file.endswith('.css') and not file.startswith('main')
+ ]
+
l10n_manager = L10nManager()
l10n_manager.translations["en"] = gettext.NullTranslations() # English does not need translation ;-)
@@ -188,6 +198,8 @@ def get_app(config):
template_helper.add_to_template_globals("get_homepath", get_homepath)
template_helper.add_to_template_globals("pkg_version", __version__)
template_helper.add_to_template_globals("available_languages", available_languages)
+ template_helper.add_to_template_globals("available_themes", available_themes)
+ template_helper.add_to_template_globals("available_codemirror_themes", available_codemirror_themes)
template_helper.add_to_template_globals("_", _)
flask_app.template_helper = template_helper
init_flask_maintenance_mapping(flask_app)
@@ -217,13 +229,13 @@ def get_app(config):
default_problem_types = get_default_displayable_problem_types()
- course_factory, task_factory = create_factories(fs_provider, default_task_dispensers, default_problem_types, plugin_manager, database)
+ taskset_factory, course_factory, task_factory = create_factories(fs_provider, default_task_dispensers, default_problem_types, plugin_manager, database)
user_manager = UserManager(database, config.get('superadmins', []))
update_pending_jobs(database)
- client = create_arch(config, fs_provider, zmq_context, course_factory)
+ client = create_arch(config, fs_provider, zmq_context, taskset_factory)
lti_outcome_manager = LTIOutcomeManager(database, user_manager, course_factory)
@@ -241,6 +253,8 @@ def get_app(config):
template_helper.add_to_template_globals("_", _)
template_helper.add_to_template_globals("str", str)
template_helper.add_to_template_globals("available_languages", available_languages)
+ template_helper.add_to_template_globals("available_themes", available_themes)
+ template_helper.add_to_template_globals("available_codemirror_themes", available_codemirror_themes)
template_helper.add_to_template_globals("get_homepath", get_homepath)
template_helper.add_to_template_globals("pkg_version", __version__)
template_helper.add_to_template_globals("allow_registration", config.get("allow_registration", True))
@@ -253,6 +267,9 @@ def get_app(config):
template_helper.add_other("course_admin_menu",
lambda course, current: course_admin_utils.get_menu(course, current, template_helper.render,
plugin_manager, user_manager))
+ template_helper.add_other("taskset_admin_menu",
+ lambda taskset, current: taskset_admin_utils.get_menu(taskset, current, template_helper.render,
+ user_manager))
template_helper.add_other("preferences_menu",
lambda current: preferences_utils.get_menu(config.get("allow_deletion", True),
current, template_helper.render,
@@ -280,6 +297,7 @@ def flask_internalerror(e):
# Insert the needed singletons into the application, to allow pages to call them
flask_app.get_homepath = get_homepath
flask_app.plugin_manager = plugin_manager
+ flask_app.taskset_factory = taskset_factory
flask_app.course_factory = course_factory
flask_app.task_factory = task_factory
flask_app.submission_manager = submission_manager
@@ -297,6 +315,8 @@ def flask_internalerror(e):
flask_app.allow_registration = config.get("allow_registration", True)
flask_app.allow_deletion = config.get("allow_deletion", True)
flask_app.available_languages = available_languages
+ flask_app.available_themes = available_themes
+ flask_app.available_codemirror_themes = available_codemirror_themes
flask_app.welcome_page = config.get("welcome_page", None)
flask_app.terms_page = config.get("terms_page", None)
flask_app.privacy_page = config.get("privacy_page", None)
diff --git a/inginious/frontend/arch_helper.py b/inginious/frontend/arch_helper.py
index 6015fdf0ac..4790b5864b 100644
--- a/inginious/frontend/arch_helper.py
+++ b/inginious/frontend/arch_helper.py
@@ -55,13 +55,13 @@ async def _restart_on_cancel(logger, agent):
logger.exception("Restarting agent")
pass
-def create_arch(configuration, tasks_fs, context, course_factory):
+def create_arch(configuration, tasks_fs, context, taskset_factory):
""" Helper that can start a simple complete INGInious arch locally if needed, or a client to a remote backend.
Intended to be used on command line, makes uses of exit() and the logger inginious.frontend.
:param configuration: configuration dict
:param tasks_fs: FileSystemProvider to the courses/tasks folders
:param context: a ZMQ context
- :param course_factory: The course factory to be used by the frontend
+ :param taskset_factory: The course factory to be used by the frontend
:param is_testing: boolean
:return: a Client object
"""
@@ -91,7 +91,7 @@ def create_arch(configuration, tasks_fs, context, course_factory):
client = Client(context, "inproc://backend_client")
backend = Backend(context, "inproc://backend_agent", "inproc://backend_client")
agent_docker = DockerAgent(context, "inproc://backend_agent", "Docker - Local agent", concurrency, tasks_fs, debug_host, debug_ports, tmp_dir, ssh_allowed=True)
- agent_mcq = MCQAgent(context, "inproc://backend_agent", "MCQ - Local agent", 1, tasks_fs, course_factory.get_task_factory().get_problem_types())
+ agent_mcq = MCQAgent(context, "inproc://backend_agent", "MCQ - Local agent", 1, tasks_fs, taskset_factory.get_task_factory().get_problem_types())
asyncio.ensure_future(_restart_on_cancel(logger, agent_docker))
asyncio.ensure_future(_restart_on_cancel(logger, agent_mcq))
diff --git a/inginious/frontend/babel.cfg b/inginious/frontend/babel.cfg
index 3281286e2f..179cc8a783 100644
--- a/inginious/frontend/babel.cfg
+++ b/inginious/frontend/babel.cfg
@@ -1,3 +1,3 @@
[python: inginious/frontend/**.py]
[jinja2: inginious/frontend/**.html]
-extensions=jinja2.ext.i18n,jinja2.ext.autoescape,jinja2.ext.with_
+extensions=jinja2.ext.i18n
diff --git a/inginious/frontend/course_factory.py b/inginious/frontend/course_factory.py
index 637eebdc11..05e66d3297 100644
--- a/inginious/frontend/course_factory.py
+++ b/inginious/frontend/course_factory.py
@@ -4,64 +4,37 @@
# more information about the licensing of this file.
""" Factory for loading courses from disk """
-import logging
-from inginious.common.filesystems import FileSystemProvider
-from inginious.common.log import get_course_logger
-from inginious.common.base import id_checker, get_json_or_yaml, loads_json_or_yaml
-from inginious.frontend.plugin_manager import PluginManager
-from inginious.common.exceptions import InvalidNameException, CourseNotFoundException, CourseUnreadableException, CourseAlreadyExistsException
+from pymongo import ReturnDocument
+from inginious.frontend.log import get_course_logger
+
+from inginious.frontend.exceptions import CourseNotFoundException, CourseAlreadyExistsException, TasksetNotFoundException
from inginious.frontend.courses import Course
-from inginious.frontend.task_factory import TaskFactory
class CourseFactory(object):
""" Load courses from disk """
- _logger = logging.getLogger("inginious.course_factory")
- def __init__(self, filesystem: FileSystemProvider, task_factory, plugin_manager, task_dispensers, database):
- self._filesystem = filesystem
+ def __init__(self, taskset_factory, task_factory, plugin_manager, database):
+ self._taskset_factory = taskset_factory
self._task_factory = task_factory
self._plugin_manager = plugin_manager
- self._task_dispensers = task_dispensers
- self._cache = {}
self._database = database
+ self._migrate_legacy_courses()
+
def add_task_dispenser(self, task_dispenser):
"""
:param task_dispenser: TaskDispenser class
"""
- self._task_dispensers.update({task_dispenser.get_id(): task_dispenser})
+ self._taskset_factory.add_task_dispenser(task_dispenser)
def get_task_dispensers(self):
"""
- Returns the supported task dispensers by this course factory
+ Returns the supported task dispensers by this taskset factory
"""
- return self._task_dispensers
-
- def get_course(self, courseid):
- """
- :param courseid: the course id of the course
- :raise: InvalidNameException, CourseNotFoundException, CourseUnreadableException
- :return: an object representing the course, of the type given in the constructor
- """
- if not id_checker(courseid):
- raise InvalidNameException("Course with invalid name: " + courseid)
- if self._cache_update_needed(courseid):
- self._update_cache(courseid)
-
- return self._cache[courseid][0]
-
- def get_task(self, courseid, taskid):
- """
- Shorthand for CourseFactory.get_course(courseid).get_task(taskid)
- :param courseid: the course id of the course
- :param taskid: the task id of the task
- :raise InvalidNameException, CourseNotFoundException, CourseUnreadableException, TaskNotFoundException, TaskUnreadableException
- :return: an object representing the task, of the type given in the constructor
- """
- return self.get_course(courseid).get_task(taskid)
+ return self._taskset_factory.get_task_dispensers()
def get_task_factory(self):
"""
@@ -70,184 +43,78 @@ def get_task_factory(self):
return self._task_factory
def get_course_descriptor_content(self, courseid):
- """
- :param courseid: the course id of the course
- :raise: InvalidNameException, CourseNotFoundException, CourseUnreadableException
- :return: the content of the dict that describes the course
- """
- path = self._get_course_descriptor_path(courseid)
- return loads_json_or_yaml(path, self._filesystem.get(path).decode("utf-8"))
+ return self._database.courses.find_one({"_id": courseid})
- def update_course_descriptor_content(self, courseid, content):
- """
- Updates the content of the dict that describes the course
- :param courseid: the course id of the course
- :param content: the new dict that replaces the old content
- :raise InvalidNameException, CourseNotFoundException
- """
- path = self._get_course_descriptor_path(courseid)
- self._filesystem.put(path, get_json_or_yaml(path, content))
+ def update_course_descriptor_content(self, courseid, course_content):
+ self._database.courses.find_one_and_update({"_id": courseid}, {"$set": course_content})
def update_course_descriptor_element(self, courseid, key, value):
- """
- Updates the value for the key in the dict that describes the course
- :param courseid: the course id of the course
- :param key: the element to change in the dict
- :param value: the new value that replaces the old one
- :raise InvalidNameException, CourseNotFoundException
- """
- course_structure = self.get_course_descriptor_content(courseid)
- course_structure[key] = value
- self.update_course_descriptor_content(courseid, course_structure)
+ self._database.courses.find_one_and_update({"_id": courseid}, {"$set": {key: value}})
- def get_fs(self):
- """
- :return: a FileSystemProvider pointing to the task directory
- """
- return self._filesystem
+ def import_legacy_course(self, database, courseid):
+ course_desc = self.get_course_descriptor_content(courseid)
+ database.courses.find_one_and_update({"_id": courseid}, {"$set": course_desc}, upsert=True,
+ return_document=ReturnDocument.AFTER)
- def get_course_fs(self, courseid):
- """
- :param courseid: the course id of the course
- :return: a FileSystemProvider pointing to the directory of the course
- """
- if not id_checker(courseid):
- raise InvalidNameException("Course with invalid name: " + courseid)
- return self._filesystem.from_subfolder(courseid)
+ def create_course(self, courseid, descriptor):
+ existing_course = self._database.courses.find_one({"_id": courseid})
+ if existing_course:
+ raise CourseAlreadyExistsException()
+
+ descriptor["_id"] = courseid
+ self._database.courses.insert_one(descriptor)
+
+ def get_course(self, courseid):
+ course_desc = self.get_course_descriptor_content(courseid)
+ try:
+ return Course(courseid, course_desc, self._taskset_factory, self._task_factory, self._plugin_manager, self._database)
+ except Exception as e:
+ raise CourseNotFoundException()
def get_all_courses(self):
- """
- :return: a table containing courseid=>Course pairs
- """
- course_ids = [f[0:len(f)-1] for f in self._filesystem.list(folders=True, files=False, recursive=False)] # remove trailing "/"
- output = {}
- for courseid in course_ids:
+ course_descriptors = self._database.courses.find({})
+ result = {}
+ for course_desc in course_descriptors:
+ courseid = course_desc["_id"]
try:
- output[courseid] = self.get_course(courseid)
+ result[courseid] = Course(courseid, course_desc, self._taskset_factory, self._task_factory, self._plugin_manager, self._database)
except Exception:
get_course_logger(courseid).warning("Cannot open course", exc_info=True)
- return output
- def _get_course_descriptor_path(self, courseid):
- """
- :param courseid: the course id of the course
- :raise InvalidNameException, CourseNotFoundException
- :return: the path to the descriptor of the course
- """
- if not id_checker(courseid):
- raise InvalidNameException("Course with invalid name: " + courseid)
- course_fs = self.get_course_fs(courseid)
- if course_fs.exists("course.yaml"):
- return courseid+"/course.yaml"
- if course_fs.exists("course.json"):
- return courseid+"/course.json"
- raise CourseNotFoundException()
-
- def create_course(self, courseid, init_content):
- """
- Create a new course folder and set initial descriptor content, folder can already exist
- :param courseid: the course id of the course
- :param init_content: initial descriptor content
- :raise: InvalidNameException or CourseAlreadyExistsException
- """
- if not id_checker(courseid):
- raise InvalidNameException("Course with invalid name: " + courseid)
-
- course_fs = self.get_course_fs(courseid)
- course_fs.ensure_exists()
-
- if course_fs.exists("course.yaml") or course_fs.exists("course.json"):
- raise CourseAlreadyExistsException("Course with id " + courseid + " already exists.")
- else:
- course_fs.put("course.yaml", get_json_or_yaml("course.yaml", init_content))
-
- get_course_logger(courseid).info("Course %s created in the factory.", courseid)
+ return result
def delete_course(self, courseid):
- """
- Erase the content of the course folder
- :param courseid: the course id of the course
- :raise: InvalidNameException or CourseNotFoundException
- """
- if not id_checker(courseid):
- raise InvalidNameException("Course with invalid name: " + courseid)
+ self._database.courses.delete_one({"_id": courseid})
- course_fs = self.get_course_fs(courseid)
+ def _migrate_legacy_courses(self):
+ courseids = []
- if not course_fs.exists():
- raise CourseNotFoundException()
+ existing_courses = self._database.courses.find({})
+ for course_descriptor in existing_courses:
+ if "tasksetid" not in course_descriptor:
+ courseids.append(course_descriptor["_id"])
- course_fs.delete()
+ for tasksetid, taskset in self._taskset_factory.get_all_tasksets().items():
+ if taskset.is_legacy() and not self._database.courses.find_one({"_id": tasksetid}):
+ courseids.append(tasksetid)
- get_course_logger(courseid).info("Course %s erased from the factory.", courseid)
+ for courseid in courseids:
+ get_course_logger(courseid).warning("Trying to migrate legacy course {}.".format(courseid))
- def _cache_update_needed(self, courseid):
- """
- :param courseid: the (valid) course id of the course
- :raise InvalidNameException, CourseNotFoundException
- :return: True if an update of the cache is needed, False else
- """
- if courseid not in self._cache:
- return True
-
- try:
- descriptor_name = self._get_course_descriptor_path(courseid)
- last_update = {descriptor_name: self._filesystem.get_last_modification_time(descriptor_name)}
- translations_fs = self._filesystem.from_subfolder("$i18n")
- if translations_fs.exists():
- for f in translations_fs.list(folders=False, files=True, recursive=False):
- lang = f[0:len(f) - 3]
- if translations_fs.exists(lang + ".mo"):
- last_update["$i18n/" + lang + ".mo"] = translations_fs.get_last_modification_time(lang + ".mo")
- except:
- raise CourseNotFoundException()
-
- last_modif = self._cache[courseid][1]
- for filename, mftime in last_update.items():
- if filename not in last_modif or last_modif[filename] < mftime:
- return True
-
- return False
+ try:
+ taskset_descriptor = self._taskset_factory.get_taskset_descriptor_content(courseid)
+ cleaned_taskset_descriptor = {
+ "name": taskset_descriptor["name"],
+ "admins": taskset_descriptor.get("admins", []),
+ "description": taskset_descriptor.get( "description", ""),
+ }
+ if "task_dispenser" in taskset_descriptor:
+ cleaned_taskset_descriptor["task_dispenser"] = taskset_descriptor["task_dispenser"]
+ cleaned_taskset_descriptor["dispenser_data"] = taskset_descriptor.get("dispenser_data", {})
+ taskset_descriptor["tasksetid"] = courseid
+ taskset_descriptor["admins"] = taskset_descriptor.get("admins", []) + taskset_descriptor.get("tutors", [])
+ self._database.courses.update_one({"_id": courseid}, {"$set": taskset_descriptor}, upsert=True)
+ self._taskset_factory.update_taskset_descriptor_content(courseid, cleaned_taskset_descriptor)
+ except TasksetNotFoundException as e:
+ get_course_logger(courseid).warning("No migration from taskset possible for courseid {}.".format(courseid))
- def _update_cache(self, courseid):
- """
- Updates the cache
- :param courseid: the (valid) course id of the course
- :raise InvalidNameException, CourseNotFoundException, CourseUnreadableException
- """
- self._logger.info("Caching course {}".format(courseid))
- path_to_descriptor = self._get_course_descriptor_path(courseid)
- try:
- course_descriptor = loads_json_or_yaml(path_to_descriptor, self._filesystem.get(path_to_descriptor).decode("utf8"))
- except Exception as e:
- raise CourseUnreadableException(str(e))
-
- last_modif = {path_to_descriptor: self._filesystem.get_last_modification_time(path_to_descriptor)}
- translations_fs = self._filesystem.from_subfolder("$i18n")
- if translations_fs.exists():
- for f in translations_fs.list(folders=False, files=True, recursive=False):
- lang = f[0:len(f) - 3]
- if translations_fs.exists(lang + ".mo"):
- last_modif["$i18n/" + lang + ".mo"] = translations_fs.get_last_modification_time(lang + ".mo")
-
- self._cache[courseid] = (
- Course(courseid, course_descriptor, self.get_course_fs(courseid), self._task_factory, self._plugin_manager, self._task_dispensers, self._database),
- last_modif
- )
-
- self._task_factory.update_cache_for_course(courseid)
-
-
-def create_factories(fs_provider, task_dispensers, task_problem_types, plugin_manager=None, database=None):
- """
- Shorthand for creating Factories
- :param fs_provider: A FileSystemProvider leading to the courses
- :param plugin_manager: a Plugin Manager instance. If None, a new Hook Manager is created
- :param task_class:
- :return: a tuple with two objects: the first being of type CourseFactory, the second of type TaskFactory
- """
- if plugin_manager is None:
- plugin_manager = PluginManager()
-
- task_factory = TaskFactory(fs_provider, plugin_manager, task_problem_types)
- return CourseFactory(fs_provider, task_factory, plugin_manager, task_dispensers, database), task_factory
diff --git a/inginious/frontend/courses.py b/inginious/frontend/courses.py
index e614f3cffa..351ce5edfa 100644
--- a/inginious/frontend/courses.py
+++ b/inginious/frontend/courses.py
@@ -9,7 +9,6 @@
import gettext
import re
from typing import List
-from collections import OrderedDict
from inginious.frontend.user_settings.course_user_setting import CourseUserSetting
from inginious.common.tags import Tag
@@ -17,81 +16,61 @@
from inginious.frontend.parsable_text import ParsableText
from inginious.frontend.user_manager import UserInfo
from inginious.frontend.task_dispensers.toc import TableOfContents
-
-
-def _migrate_from_v_0_6(content, task_list):
- if 'task_dispenser' not in content:
- content["task_dispenser"] = "toc"
- if 'toc' in content:
- content['dispenser_data'] = {"toc": content["toc"]}
- else:
- ordered_tasks = OrderedDict(sorted(list(task_list.items()),
- key=lambda t: (int(t[1]._data.get('order', -1)), t[1].get_id())))
- content['dispenser_data'] = {"toc": [{"id": "tasks-list", "title": _("List of exercises"),
- "rank": 0, "tasks_list": list(ordered_tasks.keys())}], "config": {}}
+from inginious.frontend.tasksets import _migrate_from_v_0_6
class Course(object):
""" A course with some modification for users """
- def __init__(self, courseid, content, course_fs, task_factory, plugin_manager, task_dispensers, database):
+ def __init__(self, courseid, content, taskset_factory, task_factory, plugin_manager, database):
self._id = courseid
self._content = content
- self._fs = course_fs
+ self._taskset_factory = taskset_factory
self._task_factory = task_factory
self._plugin_manager = plugin_manager
-
self._translations = {}
- translations_fs = self._fs.from_subfolder("$i18n")
- if translations_fs.exists():
- for f in translations_fs.list(folders=False, files=True, recursive=False):
- lang = f[0:len(f) - 3]
- if translations_fs.exists(lang + ".mo"):
- self._translations[lang] = gettext.GNUTranslations(translations_fs.get_fd(lang + ".mo"))
- else:
- self._translations[lang] = gettext.NullTranslations()
try:
self._name = self._content['name']
+ self._tasksetid = self._content['tasksetid']
+ self._taskset = taskset_factory.get_taskset(self._tasksetid)
except:
- raise Exception("Course has an invalid name: " + self.get_id())
-
- if self._content.get('nofrontend', False):
- raise Exception("That course is not allowed to be displayed directly in the webapp")
-
- _migrate_from_v_0_6(content, self._task_factory.get_all_tasks(self))
-
+ raise Exception("Course has an invalid name or tasksetid: " + self.get_id())
+
+ _migrate_from_v_0_6(content, self._task_factory.get_all_tasks(self._taskset))
+
+ self._admins = self._content.get('admins', [])
+ self._description = self._content.get('description', '')
+ self._accessible = AccessibleTime(self._content.get("accessible", None))
+ self._registration = AccessibleTime(self._content.get("registration", None))
+ self._registration_password = self._content.get('registration_password', None)
+ self._registration_ac = self._content.get('registration_ac', None)
+ if self._registration_ac not in [None, "username", "binding", "email"]:
+ raise Exception("Course has an invalid value for registration_ac: " + self.get_id())
+ self._registration_ac_accept = self._content.get('registration_ac_accept', True)
+ self._registration_ac_list = self._content.get('registration_ac_list', [])
+ self._groups_student_choice = self._content.get("groups_student_choice", False)
+ self._allow_unregister = self._content.get('allow_unregister', True)
+ self._allow_preview = self._content.get('allow_preview', False)
+ self._is_lti = self._content.get('is_lti', False)
+ self._lti_url = self._content.get('lti_url', '')
+ self._lti_keys = self._content.get('lti_keys', {})
+ self._lti_send_back_grade = self._content.get('lti_send_back_grade', False)
+ self._tags = {key: Tag(key, tag_dict, self.gettext) for key, tag_dict in self._content.get("tags", {}).items()}
+ self._course_user_setting = {key: CourseUserSetting(key,
+ field_dict["description"],
+ field_dict["type"])
+ for key, field_dict in self._content.get("fields", {}).items()}
+ task_dispensers = self._taskset_factory.get_task_dispensers()
+ task_dispenser_class = task_dispensers.get(self._content.get('task_dispenser', 'toc'), TableOfContents)
+ # Here we use a lambda to encourage the task dispenser to pass by the task_factory to fetch course tasks
+ # to avoid them to be cached along with the course object. Passing the task factory as argument
+ # would require to pass the course too, and have a useless reference back.
try:
- self._admins = self._content.get('admins', [])
- self._tutors = self._content.get('tutors', [])
- self._description = self._content.get('description', '')
- self._accessible = AccessibleTime(self._content.get("accessible", None))
- self._registration = AccessibleTime(self._content.get("registration", None))
- self._registration_password = self._content.get('registration_password', None)
- self._registration_ac = self._content.get('registration_ac', None)
- if self._registration_ac not in [None, "username", "binding", "email"]:
- raise Exception("Course has an invalid value for registration_ac: " + self.get_id())
- self._registration_ac_accept = self._content.get('registration_ac_accept', True)
- self._registration_ac_list = self._content.get('registration_ac_list', [])
- self._groups_student_choice = self._content.get("groups_student_choice", False)
- self._allow_unregister = self._content.get('allow_unregister', True)
- self._allow_preview = self._content.get('allow_preview', False)
- self._is_lti = self._content.get('is_lti', False)
- self._lti_url = self._content.get('lti_url', '')
- self._lti_keys = self._content.get('lti_keys', {})
- self._lti_send_back_grade = self._content.get('lti_send_back_grade', False)
- self._tags = {key: Tag(key, tag_dict, self.gettext) for key, tag_dict in self._content.get("tags", {}).items()}
- self._course_user_setting = {key: CourseUserSetting(key,
- field_dict["description"],
- field_dict["type"])
- for key, field_dict in self._content.get("fields", {}).items()}
- task_dispenser_class = task_dispensers.get(self._content.get('task_dispenser', 'toc'), TableOfContents)
- # Here we use a lambda to encourage the task dispenser to pass by the task_factory to fetch course tasks
- # to avoid them to be cached along with the course object. Passing the task factory as argument
- # would require to pass the course too, and have a useless reference back.
- self._task_dispenser = task_dispenser_class(lambda: self._task_factory.get_all_tasks(self), self._content.get("dispenser_data", ''), database, self.get_id())
- except:
- raise Exception("Course has an invalid YAML spec: " + self.get_id())
+ self._task_dispenser = task_dispenser_class(lambda: self._task_factory.get_all_tasks(self._taskset),
+ self._content.get("dispenser_data", ''), database, self.get_id())
+ except Exception as e:
+ raise
# Force some parameters if LTI is active
if self.is_lti():
@@ -120,30 +99,21 @@ def get_id(self):
""" Return the _id of this course """
return self._id
- def get_fs(self):
- """ Returns a FileSystemProvider which points to the folder of this course """
- return self._fs
+ def get_taskset(self):
+ return self._taskset
def get_task(self, taskid):
""" Returns a Task object """
- return self._task_factory.get_task(self, taskid)
+ return self._task_factory.get_task(self._taskset, taskid)
def get_descriptor(self):
""" Get (a copy) the description of the course """
return copy.deepcopy(self._content)
- def get_staff(self):
- """ Returns a list containing the usernames of all the staff users """
- return list(set(self.get_tutors() + self.get_admins()))
-
def get_admins(self):
""" Returns a list containing the usernames of the administrators of this course """
return self._admins
- def get_tutors(self):
- """ Returns a list containing the usernames of the tutors assigned to this course """
- return self._tutors
-
def is_open_to_non_staff(self):
""" Returns true if the course is accessible by users that are not administrator of this course """
return self.get_accessibility().is_open()
@@ -170,7 +140,7 @@ def get_registration_accessibility(self):
return self._registration
def get_tasks(self, ordered=False):
- return self._task_dispenser.get_ordered_tasks() if ordered else self._task_factory.get_all_tasks(self)
+ return self._task_dispenser.get_ordered_tasks() if ordered else self._task_factory.get_all_tasks(self._taskset)
def get_access_control_method(self):
""" Returns either None, "username", "binding", or "email", depending on the method used to verify that users can register to the course """
diff --git a/inginious/frontend/environment_types/generic_docker_oci_runtime.py b/inginious/frontend/environment_types/generic_docker_oci_runtime.py
index 264b55cf2e..b293260182 100644
--- a/inginious/frontend/environment_types/generic_docker_oci_runtime.py
+++ b/inginious/frontend/environment_types/generic_docker_oci_runtime.py
@@ -52,7 +52,7 @@ def check_task_environment_parameters(self, data):
return out
def studio_env_template(self, templator, task, allow_html: bool):
- return templator.render("course_admin/edit_tabs/env_generic_docker_oci.html", env_params=task.get("environment_parameters", {}),
+ return templator.render("taskset_admin/edit_tabs/env_generic_docker_oci.html", env_params=task.get("environment_parameters", {}),
content_is_html=allow_html, env_id=self.id)
def __init__(self, ssh_allowed=False):
diff --git a/inginious/frontend/exceptions.py b/inginious/frontend/exceptions.py
new file mode 100644
index 0000000000..33aef63b46
--- /dev/null
+++ b/inginious/frontend/exceptions.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of INGInious. See the LICENSE and the COPYRIGHTS files for
+# more information about the licensing of this file.
+
+""" Some type of exceptions used by parts of INGInious """
+
+
+class CourseNotFoundException(Exception):
+ pass
+
+
+class CourseAlreadyExistsException(Exception):
+ pass
+
+
+class TasksetNotFoundException(Exception):
+ pass
+
+
+class TasksetUnreadableException(Exception):
+ pass
+
+
+class TasksetAlreadyExistsException(Exception):
+ pass
+
+
+class ImportTasksetException(Exception):
+ pass
diff --git a/inginious/frontend/flask/mapping.py b/inginious/frontend/flask/mapping.py
index 99b720cf05..4ae54129a3 100644
--- a/inginious/frontend/flask/mapping.py
+++ b/inginious/frontend/flask/mapping.py
@@ -14,6 +14,7 @@
from inginious.frontend.pages.queue import QueuePage
from inginious.frontend.pages.courselist import CourseListPage
from inginious.frontend.pages.mycourses import MyCoursesPage
+from inginious.frontend.pages.tasksets import TasksetsPage
from inginious.frontend.pages.preferences.bindings import BindingsPage
from inginious.frontend.pages.preferences.delete import DeletePage
from inginious.frontend.pages.preferences.profile import ProfilePage
@@ -27,7 +28,7 @@
from inginious.frontend.pages.lti import LTITaskPage, LTILaunchPage, LTIBindPage, LTIAssetPage, LTILoginPage
from inginious.frontend.pages.group import GroupPage
from inginious.frontend.pages.marketplace import MarketplacePage
-from inginious.frontend.pages.marketplace_course import MarketplaceCoursePage
+from inginious.frontend.pages.marketplace_taskset import MarketplaceTasksetPage
from inginious.frontend.pages.api.auth_methods import APIAuthMethods
from inginious.frontend.pages.api.authentication import APIAuthentication
from inginious.frontend.pages.api.courses import APICourses
@@ -42,12 +43,16 @@
from inginious.frontend.pages.course_admin.submissions import CourseSubmissionsPage
from inginious.frontend.pages.course_admin.task_list import CourseTaskListPage
from inginious.frontend.pages.course_admin.audience_edit import CourseEditAudience
-from inginious.frontend.pages.course_admin.task_edit import CourseEditTask
-from inginious.frontend.pages.course_admin.task_edit_file import CourseTaskFiles
-from inginious.frontend.pages.course_admin.task_edit_file import CourseTaskFileUpload
-from inginious.frontend.pages.course_admin.danger_zone import CourseDangerZonePage
from inginious.frontend.pages.course_admin.statistics import CourseStatisticsPage
-from inginious.frontend.pages.course_admin.search_user import CourseAdminSearchUserPage
+from inginious.frontend.pages.course_admin.danger_zone import CourseDangerZonePage
+from inginious.frontend.pages.taskset_admin.utils import TasksetRedirectPage
+from inginious.frontend.pages.taskset_admin.settings import TasksetSettingsPage
+from inginious.frontend.pages.taskset_admin.task_edit import EditTaskPage
+from inginious.frontend.pages.taskset_admin.task_edit_file import CourseTaskFiles
+from inginious.frontend.pages.taskset_admin.task_edit_file import CourseTaskFileUpload
+from inginious.frontend.pages.taskset_admin.template import TasksetTemplatePage
+from inginious.frontend.pages.taskset_admin.danger_zone import TasksetDangerZonePage
+from inginious.frontend.pages.search_user import SearchUserPage
class CookielessConverter(BaseConverter):
@@ -78,8 +83,8 @@ def init_flask_mapping(flask_app):
flask_app.add_url_rule('/register/',
view_func=CourseRegisterPage.as_view('courseregisterpage'))
flask_app.add_url_rule('/marketplace', view_func=MarketplacePage.as_view('marketplacepage'))
- flask_app.add_url_rule('/marketplace/',
- view_func=MarketplaceCoursePage.as_view('marketplacecoursepage'))
+ flask_app.add_url_rule('/marketplace/',
+ view_func=MarketplaceTasksetPage.as_view('marketplacetasksetpage'))
flask_app.add_url_rule('/course/', view_func=CoursePage.as_view('coursepage'))
flask_app.add_url_rule('/course//', view_func=TaskPage.as_view('taskpage'))
flask_app.add_url_rule('/course///',
@@ -93,6 +98,7 @@ def init_flask_mapping(flask_app):
flask_app.add_url_rule('/pages/', view_func=INGIniousStaticPage.as_view('staticpage'))
flask_app.add_url_rule('/courselist', view_func=CourseListPage.as_view('courselistpage'))
flask_app.add_url_rule('/mycourses', view_func=MyCoursesPage.as_view('mycoursespage'))
+ flask_app.add_url_rule('/tasksets', view_func=TasksetsPage.as_view('tasksetspage'))
flask_app.add_url_rule('/preferences', view_func=PrefRedirectPage.as_view('prefredirectpage'))
flask_app.add_url_rule('/preferences/bindings',
view_func=BindingsPage.as_view('bindingspage'))
@@ -121,18 +127,29 @@ def init_flask_mapping(flask_app):
view_func=CourseTaskListPage.as_view('coursetasklistpage'))
flask_app.add_url_rule('/admin//edit/audience/',
view_func=CourseEditAudience.as_view('courseditaudience'))
- flask_app.add_url_rule('/admin//edit/task/',
- view_func=CourseEditTask.as_view('coursedittask'))
- flask_app.add_url_rule('/admin//edit/task//files',
- view_func=CourseTaskFiles.as_view('coursetaskfiles'))
- flask_app.add_url_rule('/admin//edit/task//dd_upload',
- view_func=CourseTaskFileUpload.as_view('coursetaskfileupload'))
+
+ flask_app.add_url_rule('/taskset/',
+ view_func=TasksetRedirectPage.as_view('tasksetredirectpage'))
+ flask_app.add_url_rule('/taskset//settings',
+ view_func=TasksetSettingsPage.as_view('tasksetsettingspage'))
+ flask_app.add_url_rule('/taskset//edit/',
+ view_func=EditTaskPage.as_view('tasksetedittask'))
+ flask_app.add_url_rule('/taskset//edit//files',
+ view_func=CourseTaskFiles.as_view('tasksettaskfiles'))
+ flask_app.add_url_rule('/taskset//edit//dd_upload',
+ view_func=CourseTaskFileUpload.as_view('tasksettaskfileupload'))
+ flask_app.add_url_rule('/taskset//template',
+ view_func=TasksetTemplatePage.as_view('tasksettemplatepage'))
+ flask_app.add_url_rule('/taskset//danger',
+ view_func=TasksetDangerZonePage.as_view('tasksetdangerzonepage'))
+
+ flask_app.add_url_rule('/search_user/',
+ view_func=SearchUserPage.as_view('searchuserpage'))
+
flask_app.add_url_rule('/admin//danger',
view_func=CourseDangerZonePage.as_view('coursedangerzonepage'))
flask_app.add_url_rule('/admin//stats',
view_func=CourseStatisticsPage.as_view('coursestatisticspage'))
- flask_app.add_url_rule('/admin//search_user/',
- view_func=CourseAdminSearchUserPage.as_view('courseadminsearchuserpage'))
flask_app.add_url_rule('/api/v0/auth_methods',
view_func=APIAuthMethods.as_view('apiauthmethods'))
flask_app.add_url_rule('/api/v0/authentication',
diff --git a/inginious/frontend/i18n/fr/LC_MESSAGES/messages.mo b/inginious/frontend/i18n/fr/LC_MESSAGES/messages.mo
index ac38299236..c377ada81e 100644
Binary files a/inginious/frontend/i18n/fr/LC_MESSAGES/messages.mo and b/inginious/frontend/i18n/fr/LC_MESSAGES/messages.mo differ
diff --git a/inginious/frontend/i18n/fr/LC_MESSAGES/messages.po b/inginious/frontend/i18n/fr/LC_MESSAGES/messages.po
index 91d076c1b3..86b3a82113 100644
--- a/inginious/frontend/i18n/fr/LC_MESSAGES/messages.po
+++ b/inginious/frontend/i18n/fr/LC_MESSAGES/messages.po
@@ -6,22 +6,17 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2023-04-04 15:06+0200\n"
+"POT-Creation-Date: 2023-08-22 13:53+0200\n"
"PO-Revision-Date: 2023-04-07 13:26+0000\n"
"Last-Translator: Ludovic Taffin \n"
-"Language-Team: French \n"
"Language: fr\n"
+"Language-Team: French "
+"\n"
+"Plural-Forms: nplurals=2; plural=n > 1;\n"
"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 4.14\n"
-"Generated-By: Babel 2.11.0\n"
-
-#: inginious/frontend/courses.py:30
-msgid "List of exercises"
-msgstr "Liste des exercices"
+"Generated-By: Babel 2.12.1\n"
#: inginious/frontend/parsable_text.py:37
msgid "[no content]"
@@ -29,8 +24,7 @@ msgstr "[pas de contenu]"
#: inginious/frontend/parsable_text.py:81
msgid "The feedback below will be hidden to the students until {}."
-msgstr ""
-"L'évaluation ci-dessous ne sera pas affichée aux étudiants avant le {}."
+msgstr "L'évaluation ci-dessous ne sera pas affichée aux étudiants avant le {}."
#: inginious/frontend/parsable_text.py:96
msgid ""
@@ -44,7 +38,15 @@ msgstr ""
msgid "Parsing failed:
{}
"
msgstr "L'analyse a échoué:
{}
"
-#: inginious/frontend/submission_manager.py:420
+#: inginious/frontend/submission_manager.py:91
+msgid ""
+"Maximum submission size exceeded. Check feedback, stdout, stderr and "
+"state."
+msgstr ""
+"Taille de soumission maximum excédée. Vérifiez les champs feedback, "
+"stdout, stderr et state"
+
+#: inginious/frontend/submission_manager.py:432
msgid "Feedback is badly formatted."
msgstr "Le feedback est mal formaté."
@@ -68,33 +70,45 @@ msgstr "choix multiple"
msgid "match"
msgstr "correspondance"
-#: inginious/frontend/tasks.py:86
+#: inginious/frontend/tasks.py:85
msgid "Environment type {0} is unknown"
msgstr "Type d'environnement {} inconnu"
-#: inginious/frontend/user_manager.py:436
+#: inginious/frontend/tasksets.py:24
+msgid "List of exercises"
+msgstr "Liste des exercices"
+
+#: inginious/frontend/user_manager.py:446
msgid "Auth method not found."
msgstr "Méthode d'authenfication non trouvée."
#: inginious/frontend/pages/preferences/bindings.py:42
-#: inginious/frontend/user_manager.py:487
+#: inginious/frontend/user_manager.py:497
msgid "Incorrect authentication binding."
msgstr "Méthode d'authentification incorrecte."
-#: inginious/frontend/user_manager.py:497
+#: inginious/frontend/user_manager.py:507
msgid "You must set a password before removing all bindings."
msgstr ""
-"Vous devez spécifier un mot de passe avant de pouvoir supprimer toutes les "
-"méthodes d'authentifications."
+"Vous devez spécifier un mot de passe avant de pouvoir supprimer toutes "
+"les méthodes d'authentifications."
-#: inginious/frontend/user_manager.py:528
+#: inginious/frontend/user_manager.py:538
msgid "User could not be created."
msgstr "L'utilisateur n'a pu être créé."
#: inginious/frontend/environment_types/docker.py:12
-msgid "Standard container (Docker)"
+msgid "Standard container + SSH"
+msgstr "Conteneur standard (Docker) + SSH"
+
+#: inginious/frontend/environment_types/docker.py:12
+msgid "Standard container"
msgstr "Conteneur standard (Docker)"
+#: inginious/frontend/environment_types/kata.py:11
+msgid "Container running as root (Kata) + SSH"
+msgstr "Conteneur s'exécutant en tant que root (Kata) + SSH"
+
#: inginious/frontend/environment_types/kata.py:11
msgid "Container running as root (Kata)"
msgstr "Conteneur s'exécutant en tant que root (Kata)"
@@ -103,6 +117,10 @@ msgstr "Conteneur s'exécutant en tant que root (Kata)"
msgid "Multiple Choice Question solver"
msgstr "Solveur intégré Questions à Choix Multiples"
+#: inginious/frontend/environment_types/nvidia.py:11
+msgid "Container with GPUs (NVIDIA) + SSH"
+msgstr "Conteneur avec GPUs (NVIDIA) + SSH"
+
#: inginious/frontend/environment_types/nvidia.py:11
msgid "Container with GPUs (NVIDIA)"
msgstr "Conteneur avec GPUs (NVIDIA)"
@@ -111,7 +129,7 @@ msgstr "Conteneur avec GPUs (NVIDIA)"
msgid "Course not found."
msgstr "Cours introuvable."
-#: inginious/frontend/pages/course_register.py:23
+#: inginious/frontend/pages/course_register.py:24
msgid "This course doesn't exist."
msgstr "Ce cours n'existe pas."
@@ -161,8 +179,8 @@ msgstr "Une erreur s'est produite lors de la validation de la requête LTI"
#: inginious/frontend/pages/lti.py:213
msgid ""
"In order to send grade back to the TC, INGInious needs the parameters "
-"lis_outcome_service_url and lis_outcome_result_id in the LTI basic-launch-"
-"request. Please contact your administrator."
+"lis_outcome_service_url and lis_outcome_result_id in the LTI basic-"
+"launch-request. Please contact your administrator."
msgstr ""
"Afin d'envoyer la note à la plateforme hôte, les paramètres "
"lis_outcome_service_urlet lis_outcome_result_id doivent être fournis à "
@@ -173,12 +191,12 @@ msgid "Couldn't validate LTI request"
msgstr "Impossible de valider la requête LTI"
#: inginious/frontend/pages/marketplace.py:32
-msgid "You don't have superadmin rights on this course."
-msgstr "Vous n'avez pas les droits de super-administrateur pour ce cours."
+msgid "You don't have superadmin rights on this taskset."
+msgstr "Vous n'avez pas les droits de super-administrateur pour ce jeu d'exercices."
#: inginious/frontend/pages/marketplace.py:39
-#: inginious/frontend/pages/marketplace_course.py:33
-#: inginious/frontend/pages/marketplace_course.py:42
+#: inginious/frontend/pages/marketplace_taskset.py:33
+#: inginious/frontend/pages/marketplace_taskset.py:42
msgid "You're not allowed to do that"
msgstr "Vous n'avez pas les droits nécessaires."
@@ -188,16 +206,16 @@ msgid "User returned an invalid form."
msgstr "L'utilisateur a soumis un formulaire invalide."
#: inginious/frontend/pages/marketplace.py:75
-msgid "Couldn't clone course into your instance"
-msgstr "Impossible de clôner ce cours sur votre instance"
+msgid "Couldn't clone taskset into your instance"
+msgstr "Impossible de copier ce jeu d'exercices sur votre instance"
#: inginious/frontend/pages/marketplace.py:96
-msgid "An error occur while editing the course description"
-msgstr "Une erreur a eu lieu lors de l'édition de la description du cours"
+msgid "An error occur while editing the taskset description"
+msgstr "Une erreur a eu lieu lors de l'édition de la description du jeu d'exercices"
-#: inginious/frontend/pages/marketplace_course.py:25
-msgid "Course unavailable."
-msgstr "Cours indisponible."
+#: inginious/frontend/pages/marketplace_taskset.py:25
+msgid "Taskset unavailable."
+msgstr "Jeu d'exercices indisponible."
#: inginious/frontend/pages/register.py:30
#: inginious/frontend/pages/register.py:198
@@ -265,15 +283,16 @@ msgstr ""
#: inginious/frontend/pages/register.py:116
msgid ""
-"You are succesfully registered. An email has been sent to you for activation."
+"You are succesfully registered. An email has been sent to you for "
+"activation."
msgstr ""
-"Votre compte a été créé. Un email vous a été envoyé afin de l'activer avant "
-"toute connexion."
+"Votre compte a été créé. Un email vous a été envoyé afin de l'activer "
+"avant toute connexion."
#: inginious/frontend/pages/register.py:121
msgid ""
-"Something went wrong while sending you activation email. Please contact the "
-"administrator."
+"Something went wrong while sending you activation email. Please contact "
+"the administrator."
msgstr ""
"Une erreur s'est produite lors de l'envoi du mail d'activation. Veuillez "
"contacter l'administrateur du site."
@@ -290,13 +309,13 @@ msgstr "Récupération de votre mot de passe INGInious"
msgid ""
"Dear {realname},\n"
"\n"
-"Someone (probably you) asked to reset your INGInious password. If this was "
-"you, please click on the following link :\n"
+"Someone (probably you) asked to reset your INGInious password. If this "
+"was you, please click on the following link :\n"
msgstr ""
"{realname},\n"
"\n"
-"Une personne (probablement vous) a demandé la réinitialisation de votre mot "
-"de passe INGInious.S'il s'agit de vous, veuillez cliquer sur le lien "
+"Une personne (probablement vous) a demandé la réinitialisation de votre "
+"mot de passe INGInious.S'il s'agit de vous, veuillez cliquer sur le lien "
"suivant: \n"
#: inginious/frontend/pages/register.py:161
@@ -308,8 +327,8 @@ msgid ""
"Something went wrong while sending you reset email. Please contact the "
"administrator."
msgstr ""
-"Une erreur s'est produite lors de l'envoi du mail de réinitialisation du mot "
-"de passe.Veuillez contacter l'administrateur du site."
+"Une erreur s'est produite lors de l'envoi du mail de réinitialisation du "
+"mot de passe.Veuillez contacter l'administrateur du site."
#: inginious/frontend/pages/register.py:189
msgid "Invalid reset hash."
@@ -323,114 +342,139 @@ msgstr "Votre mot de passe a bien été modifié."
msgid "Auth method doesn't exist"
msgstr "La méthode d'authentification n'existe pas."
-#: inginious/frontend/pages/social.py:39
+#: inginious/frontend/pages/social.py:38
msgid "Auth method doesn't exist."
msgstr "La méthode d'authentification n'existe pas."
-#: inginious/frontend/pages/tasks.py:93 inginious/frontend/pages/tasks.py:264
+#: inginious/frontend/pages/tasks.py:93 inginious/frontend/pages/tasks.py:263
msgid "Submission doesn't exist."
msgstr "La soumission n'existe pas."
-#: inginious/frontend/pages/tasks.py:176 inginious/frontend/pages/tasks.py:184
-#: inginious/frontend/pages/tasks.py:202 inginious/frontend/pages/tasks.py:224
-#: inginious/frontend/pages/tasks.py:231 inginious/frontend/pages/tasks.py:248
+#: inginious/frontend/pages/tasks.py:175 inginious/frontend/pages/tasks.py:183
+#: inginious/frontend/pages/tasks.py:201 inginious/frontend/pages/tasks.py:223
+#: inginious/frontend/pages/tasks.py:230 inginious/frontend/pages/tasks.py:247
msgid "Error"
msgstr "Erreur"
-#: inginious/frontend/pages/tasks.py:176
+#: inginious/frontend/pages/tasks.py:175
msgid "You are not allowed to submit for this task."
msgstr "Vous n'êtes pas autorisé à soumettre pour cet exercice."
-#: inginious/frontend/pages/tasks.py:184
+#: inginious/frontend/pages/tasks.py:183
msgid "Your task has been regenerated. This current task is outdated."
msgstr "Votre exercice a été regénéré. La version consultée est périmée."
-#: inginious/frontend/pages/tasks.py:203
+#: inginious/frontend/pages/tasks.py:202
msgid ""
-"Please answer to all the questions and verify the extensions of the files "
-"you want to upload. Your responses were not tested."
+"Please answer to all the questions and verify the extensions of the files"
+" you want to upload. Your responses were not tested."
msgstr ""
"Veuillez répondre à toutes les questions et vérifier les extensions des "
-"fichiers que vous souhaitez télécharger. Votre réponse n'a pas été soumise "
-"pour correction."
+"fichiers que vous souhaitez télécharger. Votre réponse n'a pas été "
+"soumise pour correction."
-#: inginious/frontend/pages/tasks.py:220 inginious/frontend/pages/tasks.py:329
+#: inginious/frontend/pages/tasks.py:219 inginious/frontend/pages/tasks.py:328
msgid "Your submission has been sent..."
msgstr "Votre soumission a bien été envoyée..."
-#: inginious/frontend/pages/course_admin/task_edit_file.py:209
-#: inginious/frontend/pages/course_admin/task_edit_file.py:230
-#: inginious/frontend/pages/course_admin/task_edit_file.py:234
-#: inginious/frontend/pages/tasks.py:231 inginious/frontend/pages/tasks.py:248
+#: inginious/frontend/pages/tasks.py:230 inginious/frontend/pages/tasks.py:247
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:205
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:226
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:230
#: inginious/frontend/templates/internalerror.html:5
#: inginious/frontend/templates/task.html:325
msgid "Internal error"
msgstr "Erreur interne"
-#: inginious/frontend/pages/tasks.py:298
+#: inginious/frontend/pages/tasks.py:297
msgid "INGInious is currently grading your answers. (almost done)"
msgstr "INGinious est en train de corriger vos réponses. (presque fini)"
-#: inginious/frontend/pages/tasks.py:300
+#: inginious/frontend/pages/tasks.py:299
msgid ""
-"INGInious is currently grading your answers. (Approx. wait time: {} "
-"seconds)"
+"INGInious is currently grading your answers. (Approx. wait time: "
+"{} seconds)"
msgstr ""
"INGInious est en train de corriger vos réponses. (Temps d'attente "
"approx. : {} secondes)"
-#: inginious/frontend/pages/tasks.py:303
+#: inginious/frontend/pages/tasks.py:302
msgid "You are next in the waiting queue!"
msgstr "Votre soumission est la suivante dans la file d'attente!"
-#: inginious/frontend/pages/tasks.py:305
+#: inginious/frontend/pages/tasks.py:304
msgid "There is one task in front of you in the waiting queue."
msgstr "Il y a une tâche devant vous dans la file d'attente."
-#: inginious/frontend/pages/tasks.py:307
+#: inginious/frontend/pages/tasks.py:306
msgid "There are {} tasks in front of you in the waiting queue."
msgstr "Il y a {} tâches devant vous dans la file d'attente."
-#: inginious/frontend/pages/tasks.py:331
+#: inginious/frontend/pages/tasks.py:330
msgid "There are some errors in your answer. Your score is {score}%."
msgstr "Il y a des erreurs dans votre réponse. Votre note est de {score}%."
-#: inginious/frontend/pages/tasks.py:333
+#: inginious/frontend/pages/tasks.py:332
msgid "Your answer passed the tests! Your score is {score}%."
msgstr "Votre réponse a passé les tests ! Votre note est de {score}%."
-#: inginious/frontend/pages/tasks.py:335
+#: inginious/frontend/pages/tasks.py:334
msgid "Your submission timed out. Your score is {score}%."
msgstr ""
-"Votre soumission a pris trop de temps à exécuter. Votre note est de {score}%."
+"Votre soumission a pris trop de temps à exécuter. Votre note est de "
+"{score}%."
-#: inginious/frontend/pages/tasks.py:337
+#: inginious/frontend/pages/tasks.py:336
msgid "Your submission made an overflow. Your score is {score}%."
msgstr ""
-"Votre soumission a nécessité l'allocation de trop de mémoire. Votre note est "
-"de {score}%."
+"Votre soumission a nécessité l'allocation de trop de mémoire. Votre note "
+"est de {score}%."
-#: inginious/frontend/pages/tasks.py:339
+#: inginious/frontend/pages/tasks.py:338
msgid "Your submission was killed."
msgstr "Votre soumission a été annulée."
-#: inginious/frontend/pages/tasks.py:341
+#: inginious/frontend/pages/tasks.py:340
msgid ""
-"An internal error occurred. Please retry later. If the error persists, send "
-"an email to the course administrator."
+"An internal error occurred. Please retry later. If the error persists, "
+"send an email to the course administrator."
msgstr ""
-"Une erreur interne s'est produite. Veuillez réessayer plus tard. Si l'erreur "
-"persiste, contactez l'administrateur du cours."
+"Une erreur interne s'est produite. Veuillez réessayer plus tard. Si "
+"l'erreur persiste, contactez l'administrateur du cours."
-#: inginious/frontend/pages/tasks.py:344
+#: inginious/frontend/pages/tasks.py:343
msgid "[Submission #{submissionid} ({submissionDate})]"
msgstr "[Soumission #{submissionid} ({submissionDate})]"
-#: inginious/frontend/pages/tasks.py:379 inginious/frontend/pages/tasks.py:381
+#: inginious/frontend/pages/tasks.py:378 inginious/frontend/pages/tasks.py:380
msgid " "
msgstr " "
-#: inginious/frontend/pages/utils.py:192
+#: inginious/frontend/pages/tasksets.py:43
+msgid "Course with id {} successfully instantiated from taskset {}"
+msgstr "Le cours avec l'identifiant {} a été instantié à partir du jeu d'exercice {}"
+
+#: inginious/frontend/pages/tasksets.py:46
+msgid "You are not allowed to instantiate a course from this taskset."
+msgstr "Vous n'êtes pas autorisé à instancier un cours à partir de ce jeu d'exercices."
+
+#: inginious/frontend/pages/tasksets.py:49
+msgid "A course with id {} already exists."
+msgstr "Un cours avec l'identifiant {} existe déjà"
+
+#: inginious/frontend/pages/tasksets.py:52
+msgid "Couldn't instantiate course with id {} : "
+msgstr "Impossible d'instantier un cours avec l'identifiant {} : "
+
+#: inginious/frontend/pages/tasksets.py:59
+msgid "Taskset created."
+msgstr "Jeu d'exercices créé."
+
+#: inginious/frontend/pages/tasksets.py:62
+msgid "Failed to create the taskset."
+msgstr "Une erreur s'est produite lors de la création du jeu d'exercices."
+
+#: inginious/frontend/pages/utils.py:197
msgid ""
"An account using this email already exists and is not bound with this "
"service. For security reasons, please log in via another method and bind "
@@ -440,31 +484,31 @@ msgstr ""
"Pour des raisons de sécurités, veuillez vous identifier via une autre "
"méthode et lier votre compte depuis votre profil."
-#: inginious/frontend/pages/utils.py:195
+#: inginious/frontend/pages/utils.py:200
msgid ""
-"Couldn't fetch the required information from the service. Please check the "
-"provided permissions (name, email) and contact your INGInious administrator "
-"if the error persists."
+"Couldn't fetch the required information from the service. Please check "
+"the provided permissions (name, email) and contact your INGInious "
+"administrator if the error persists."
msgstr ""
-"Impossible d'obtenir les informations requises depuis le service. Veuillez "
-"vérifier les permissions (nom, email) octroyées et contacter votre "
-"administrateur INGInious si l'erreur persiste."
+"Impossible d'obtenir les informations requises depuis le service. "
+"Veuillez vérifier les permissions (nom, email) octroyées et contacter "
+"votre administrateur INGInious si l'erreur persiste."
-#: inginious/frontend/pages/utils.py:221
+#: inginious/frontend/pages/utils.py:226
msgid "Invalid login/password"
msgstr "Nom d'utilisateur/mot de passe invalide"
-#: inginious/frontend/pages/utils.py:249
+#: inginious/frontend/pages/utils.py:254
#: inginious/frontend/templates/forbidden.html:5
#: inginious/frontend/templates/forbidden.html:9
msgid "Forbidden"
msgstr "Accès interdit"
-#: inginious/frontend/pages/utils.py:263
+#: inginious/frontend/pages/utils.py:268
msgid "You have not sufficient right to see this part."
msgstr "Vous n'avez pas les droits suffisants pour voir cette partie."
-#: inginious/frontend/pages/utils.py:312
+#: inginious/frontend/pages/utils.py:317
msgid "File doesn't exist."
msgstr "Le fichier n'existe pas."
@@ -503,6 +547,7 @@ msgid "This file doesn't exist."
msgstr "Le fichier n'existe pas."
#: inginious/frontend/pages/course_admin/danger_zone.py:164
+#: inginious/frontend/pages/taskset_admin/danger_zone.py:50
msgid "Operation aborted due to invalid token."
msgstr "L'opération a échoué car le jeton est invalide."
@@ -518,8 +563,8 @@ msgstr "Toutes les données du cours ont été supprimées."
#: inginious/frontend/pages/course_admin/danger_zone.py:175
msgid "An error occurred while dumping course from database: {}"
msgstr ""
-"Une erreur a eu lieu lors de l'archivage du cours depuis la base de donnée : "
-"{}"
+"Une erreur a eu lieu lors de l'archivage du cours depuis la base de "
+"donnée : {}"
#: inginious/frontend/pages/course_admin/danger_zone.py:185
msgid "Course restored to date : {}."
@@ -534,50 +579,51 @@ msgid "An error occurred while deleting the course data: {}"
msgstr "Une erreur a eu lieu lors de la suppression des données du cours : {}"
#: inginious/frontend/pages/course_admin/settings.py:34
+#: inginious/frontend/pages/taskset_admin/settings.py:48
msgid "Invalid name"
msgstr "Nom invalide"
#: inginious/frontend/pages/course_admin/settings.py:38
msgid "You cannot remove yourself from the administrators of this course"
-msgstr ""
-"Vous ne pouvez pas vous supprimer de la liste des administrateurs du cours"
+msgstr "Vous ne pouvez pas vous supprimer de la liste des administrateurs du cours"
-#: inginious/frontend/pages/course_admin/settings.py:55
+#: inginious/frontend/pages/course_admin/settings.py:52
msgid "Invalid accessibility dates"
msgstr "Dates d'accessibilité invalides"
-#: inginious/frontend/pages/course_admin/settings.py:70
+#: inginious/frontend/pages/course_admin/settings.py:67
msgid "Invalid registration dates"
msgstr "Dates d'inscription invalides"
-#: inginious/frontend/pages/course_admin/settings.py:78
+#: inginious/frontend/pages/course_admin/settings.py:75
msgid "Invalid ACL value"
msgstr "Valeur de liste de contrôle d'accès invalide"
-#: inginious/frontend/pages/course_admin/settings.py:92
+#: inginious/frontend/pages/course_admin/settings.py:89
msgid "LTI keys must be alphanumerical."
msgstr "Les clés LTI doivent être alphanumériques."
-#: inginious/frontend/pages/course_admin/settings.py:129
+#: inginious/frontend/pages/course_admin/settings.py:126
msgid "Some tag fields were missing."
msgstr "Quelques champs d'étiquettes sont manquants."
-#: inginious/frontend/pages/course_admin/settings.py:132
+#: inginious/frontend/pages/course_admin/settings.py:129
msgid "Invalid tag id: {}"
msgstr "Identifiant d'étiquette invalide: {}"
-#: inginious/frontend/pages/course_admin/settings.py:151
+#: inginious/frontend/pages/course_admin/settings.py:148
msgid "Invalid type value: {}"
msgstr "Type de valeur invalide : {}"
-#: inginious/frontend/pages/course_admin/settings.py:153
+#: inginious/frontend/pages/course_admin/settings.py:150
msgid "Invalid id: {}"
msgstr "Identifiant invalide: {}"
-#: inginious/frontend/pages/course_admin/settings.py:167
+#: inginious/frontend/pages/course_admin/settings.py:164
msgid "Some datas have the same id! The id must be unique."
msgstr ""
-"Plusieurs données ont le même identifiant ! L'identifiant doit être unique."
+"Plusieurs données ont le même identifiant ! L'identifiant doit être "
+"unique."
#: inginious/frontend/pages/course_admin/statistics.py:192
#: inginious/frontend/pages/course_admin/submissions.py:138
@@ -627,8 +673,7 @@ msgstr "Une erreur s'est produite lors du traitement des données."
#: inginious/frontend/pages/course_admin/student_list.py:353
msgid "Changes couldn't be applied for following students :"
-msgstr ""
-"Les changements n'ont pas pu être appliqués pour les étudiants suivants :"
+msgstr "Les changements n'ont pas pu être appliqués pour les étudiants suivants :"
#: inginious/frontend/pages/course_admin/student_list.py:360
msgid "Groups updated."
@@ -645,11 +690,10 @@ msgstr "ObjectId inavlide."
#: inginious/frontend/pages/course_admin/submissions.py:62
msgid "The following submission could not be prepared for download: {}"
-msgstr ""
-"La soumission suivante n'a pas pu être préparée pour le téléchargement: {}"
+msgstr "La soumission suivante n'a pas pu être préparée pour le téléchargement: {}"
#: inginious/frontend/pages/course_admin/submissions.py:67
-#: inginious/frontend/pages/course_admin/utils.py:43
+#: inginious/frontend/pages/course_admin/utils.py:38
msgid "You don't have admin rights on this course."
msgstr "Vous n'avez pas les droits d'administrateur pour ce cours."
@@ -661,154 +705,82 @@ msgstr "{0} soumissions vont être rejouées."
msgid "The submission doesn't exist."
msgstr "La soumission n'existe pas."
-#: inginious/frontend/pages/course_admin/task_edit.py:34
-#: inginious/frontend/pages/course_admin/task_edit_file.py:24
-#: inginious/frontend/pages/course_admin/task_edit_file.py:45
-#: inginious/frontend/pages/course_admin/task_edit_file.py:264
-msgid "Invalid task id"
-msgstr "Identifiant d'exercice invalide"
-
-#: inginious/frontend/pages/course_admin/task_edit.py:87
-msgid "Invalid course/task id"
-msgstr "Identifiant de cours/exercice invalide"
-
-#: inginious/frontend/pages/course_admin/task_edit.py:117
-msgid "Invalid file type: {}"
-msgstr "Type de fichier invalide : {}"
-
-#: inginious/frontend/pages/course_admin/task_edit.py:135
-msgid "The number of random inputs must be an integer!"
-msgstr "Le nombre d'entrées aléatoires doit être un entier !"
-
-#: inginious/frontend/pages/course_admin/task_edit.py:137
-msgid "The number of random inputs must be positive!"
-msgstr "Le nombre d'entrées aléatoires doit être positif !"
-
-#: inginious/frontend/pages/course_admin/task_edit.py:146
-msgid "Your browser returned an invalid form ({})"
-msgstr "Votre navigateur a renvoyé un formaulaire invalide ({})"
-
-#: inginious/frontend/pages/course_admin/task_edit.py:152
-msgid "Error while reading course's informations"
-msgstr "Une erreur s'est produite lors de la lecture des informations du cours"
-
-#: inginious/frontend/pages/course_admin/task_edit.py:176
-msgid "Invalid data: {}"
-msgstr "Données invalides : {}"
-
-#: inginious/frontend/pages/course_admin/task_edit.py:182
-msgid "Cannot read zip file. Files were not modified"
-msgstr "Impossible de lire le fichier ZIP. Les fichiers n'ont pas été modifiés"
-
-#: inginious/frontend/pages/course_admin/task_edit.py:189
-msgid ""
-"There was a problem while extracting the zip archive. Some files may have "
-"been modified"
-msgstr ""
-"Une erreur s'est produite lors de l'extraction de l'archive ZIP. Il se peut "
-"que des fichiers aient été modifiés"
-
-#: inginious/frontend/pages/course_admin/task_edit_file.py:168
-#: inginious/frontend/pages/course_admin/task_edit_file.py:188
-#: inginious/frontend/pages/course_admin/task_edit_file.py:213
-msgid "Invalid new path"
-msgstr "Nouveau chemin invalide"
-
-#: inginious/frontend/pages/course_admin/task_edit_file.py:174
-msgid "An error occurred while writing the file"
-msgstr "Une erreur s'est produite lors de l'écriture du fichier"
-
-#: inginious/frontend/pages/course_admin/task_edit_file.py:219
-msgid "An error occurred while moving the files"
-msgstr "Une erreur s'est produite lors du déplacement des fichiers"
-
-#: inginious/frontend/pages/course_admin/task_edit_file.py:240
-msgid "An error occurred while deleting the files"
-msgstr "Une erreur s'est produite lors de la suppression des fichiers"
-
-#: inginious/frontend/pages/course_admin/task_edit_file.py:247
-msgid "This path doesn't exist."
-msgstr "Le chemin n'existe pas."
-
#: inginious/frontend/pages/course_admin/task_list.py:36
+#: inginious/frontend/pages/taskset_admin/template.py:36
msgid "Invalid task dispenser"
msgstr "Distribution d'exercice invalide"
-#: inginious/frontend/pages/course_admin/task_list.py:45
-msgid "Invalid course structure: "
-msgstr "Structure du cours invalide:"
-
-#: inginious/frontend/pages/course_admin/task_list.py:47
+#: inginious/frontend/pages/course_admin/task_list.py:43
+#: inginious/frontend/pages/course_admin/task_list.py:48
+#: inginious/frontend/pages/taskset_admin/template.py:43
+#: inginious/frontend/pages/taskset_admin/template.py:48
+#: inginious/frontend/pages/taskset_admin/template.py:53
msgid "Something wrong happened: "
msgstr "Quelque choe de mal est survenu: "
#: inginious/frontend/pages/course_admin/task_list.py:54
-msgid "Couldn't create task {} : "
-msgstr "Impossible de supprimer l'exercice : {} "
-
-#: inginious/frontend/pages/course_admin/task_list.py:59
-msgid "Couldn't delete task {} : "
-msgstr "Impossible de supprimer l'exercice : {} "
-
-#: inginious/frontend/pages/course_admin/task_list.py:64
msgid "Couldn't wipe task {} : "
msgstr "Impossible de supprimer les données de la tâche : {} "
-#: inginious/frontend/pages/course_admin/utils.py:40
-msgid "You don't have staff rights on this course."
-msgstr "Vous n'avez pas les droits d'enseignant pour ce cours."
+#: inginious/frontend/pages/course_admin/task_list.py:69
+msgid "Invalid course structure: "
+msgstr "Structure du cours invalide:"
-#: inginious/frontend/pages/course_admin/utils.py:50
+#: inginious/frontend/pages/course_admin/utils.py:45
msgid "This course is unreachable"
msgstr "Ce cours n'est pas disponible."
-#: inginious/frontend/pages/course_admin/utils.py:144
+#: inginious/frontend/pages/course_admin/utils.py:139
msgid "List not valid."
msgstr "Liste invalide."
-#: inginious/frontend/pages/course_admin/utils.py:343
-#: inginious/frontend/templates/course_admin/settings.html:18
-#: inginious/frontend/templates/course_admin/settings.html:25
+#: inginious/frontend/pages/course_admin/utils.py:338
+#: inginious/frontend/templates/course.html:53
+#: inginious/frontend/templates/course.html:71
+#: inginious/frontend/templates/course_user_settings.html:33
#: inginious/frontend/templates/course_user_settings.html:47
#: inginious/frontend/templates/course_user_settings.html:57
-msgid "Course settings"
-msgstr "Paramètres du cours"
+msgid "User settings"
+msgstr "Paramètres utilisateur"
-#: inginious/frontend/pages/course_admin/utils.py:345
-#: inginious/frontend/templates/course_admin/stats.html:27
-#: inginious/frontend/templates/course_admin/stats.html:35
+#: inginious/frontend/pages/course_admin/utils.py:340
+#: inginious/frontend/templates/course_admin/stats.html:28
+#: inginious/frontend/templates/course_admin/stats.html:36
#: inginious/frontend/templates/course_admin/submissions_query.html:211
msgid "Statistics"
msgstr "Statistiques"
-#: inginious/frontend/pages/course_admin/utils.py:346
-#: inginious/frontend/templates/course_admin/student_list.html:20
-#: inginious/frontend/templates/course_admin/student_list.html:28
+#: inginious/frontend/pages/course_admin/utils.py:341
+#: inginious/frontend/templates/course_admin/student_list.html:21
+#: inginious/frontend/templates/course_admin/student_list.html:29
msgid "User management"
msgstr "Gestion des utilisateurs"
-#: inginious/frontend/pages/course_admin/utils.py:349
-#: inginious/frontend/templates/course_admin/stats.html:49
-#: inginious/frontend/templates/course_admin/stats.html:248
-#: inginious/frontend/templates/course_admin/task_edit.html:19
-#: inginious/frontend/templates/course_admin/task_list.html:19
-#: inginious/frontend/templates/course_admin/task_list.html:25
+#: inginious/frontend/pages/course_admin/utils.py:344
+#: inginious/frontend/templates/course_admin/stats.html:50
+#: inginious/frontend/templates/course_admin/stats.html:249
+#: inginious/frontend/templates/course_admin/task_list.html:20
+#: inginious/frontend/templates/course_admin/task_list.html:26
msgid "Tasks"
msgstr "Exercices"
-#: inginious/frontend/pages/course_admin/utils.py:351
-#: inginious/frontend/templates/course_admin/stats.html:43
-#: inginious/frontend/templates/course_admin/stats.html:168
+#: inginious/frontend/pages/course_admin/utils.py:346
+#: inginious/frontend/templates/course_admin/stats.html:44
+#: inginious/frontend/templates/course_admin/stats.html:169
#: inginious/frontend/templates/course_admin/submissions.html:6
-#: inginious/frontend/templates/course_admin/submissions.html:18
-#: inginious/frontend/templates/course_admin/submissions.html:26
+#: inginious/frontend/templates/course_admin/submissions.html:19
+#: inginious/frontend/templates/course_admin/submissions.html:27
msgid "Submissions"
msgstr "Soumissions"
-#: inginious/frontend/pages/course_admin/utils.py:354
+#: inginious/frontend/pages/course_admin/utils.py:349
+#: inginious/frontend/pages/taskset_admin/utils.py:63
#: inginious/frontend/templates/course_admin/danger_zone.html:6
-#: inginious/frontend/templates/course_admin/danger_zone.html:18
-#: inginious/frontend/templates/course_admin/danger_zone.html:24
+#: inginious/frontend/templates/course_admin/danger_zone.html:19
+#: inginious/frontend/templates/course_admin/danger_zone.html:25
+#: inginious/frontend/templates/taskset_admin/danger_zone.html:6
+#: inginious/frontend/templates/taskset_admin/danger_zone.html:19
+#: inginious/frontend/templates/taskset_admin/danger_zone.html:25
msgid "Danger zone"
msgstr "Zone dangereuse"
@@ -882,13 +854,132 @@ msgstr "Méthodes d'authentification"
msgid "Delete my account"
msgstr "Supprimer mon compte"
+#: inginious/frontend/pages/taskset_admin/danger_zone.py:54
+msgid "Wrong taskset id."
+msgstr "Identifiant de jeu d'exercices incorrect."
+
+#: inginious/frontend/pages/taskset_admin/danger_zone.py:59
+msgid "One or more course(s) rely on the current taskset."
+msgstr "Un ou plusieurs cours dépendent du jeu d'exercices actuel."
+
+#: inginious/frontend/pages/taskset_admin/danger_zone.py:63
+msgid "An error occurred while deleting the taskset data: {}"
+msgstr "Une erreur a eu lieu lors de la suppression des données du jeu d'exercices : {}"
+
+#: inginious/frontend/pages/taskset_admin/settings.py:33
+msgid "Invalid taskid : {}"
+msgstr "Identifiant d'éxercice invalide: {}"
+
+#: inginious/frontend/pages/taskset_admin/settings.py:42
+msgid "Couldn't create task {} : "
+msgstr "Impossible de supprimer l'exercice : {} "
+
+#: inginious/frontend/pages/taskset_admin/settings.py:53
+msgid "You cannot remove yourself from the administrators of this taskset"
+msgstr "Vous ne pouvez pas vous supprimer de la liste des administrateurs du jeu d'exercices"
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:34
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:24
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:45
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:260
+msgid "Invalid task id"
+msgstr "Identifiant d'exercice invalide"
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:87
+msgid "Invalid taskset/task id"
+msgstr "Identifiant de (jeux d') exercice(s) invalide"
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:117
+msgid "Invalid file type: {}"
+msgstr "Type de fichier invalide : {}"
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:135
+msgid "The number of random inputs must be an integer!"
+msgstr "Le nombre d'entrées aléatoires doit être un entier !"
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:137
+msgid "The number of random inputs must be positive!"
+msgstr "Le nombre d'entrées aléatoires doit être positif !"
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:146
+msgid "Your browser returned an invalid form ({})"
+msgstr "Votre navigateur a renvoyé un formaulaire invalide ({})"
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:152
+msgid "Error while reading taskset data"
+msgstr "Une erreur s'est produite lors de la lecture des informations du jeu d'exercices"
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:176
+msgid "Invalid data: {}"
+msgstr "Données invalides : {}"
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:182
+msgid "Cannot read zip file. Files were not modified"
+msgstr "Impossible de lire le fichier ZIP. Les fichiers n'ont pas été modifiés"
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:189
+msgid ""
+"There was a problem while extracting the zip archive. Some files may have"
+" been modified"
+msgstr ""
+"Une erreur s'est produite lors de l'extraction de l'archive ZIP. Il se "
+"peut que des fichiers aient été modifiés"
+
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:164
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:184
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:209
+msgid "Invalid new path"
+msgstr "Nouveau chemin invalide"
+
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:170
+msgid "An error occurred while writing the file"
+msgstr "Une erreur s'est produite lors de l'écriture du fichier"
+
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:215
+msgid "An error occurred while moving the files"
+msgstr "Une erreur s'est produite lors du déplacement des fichiers"
+
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:236
+msgid "An error occurred while deleting the files"
+msgstr "Une erreur s'est produite lors de la suppression des fichiers"
+
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:243
+msgid "This path doesn't exist."
+msgstr "Le chemin n'existe pas."
+
+#: inginious/frontend/pages/taskset_admin/template.py:68
+msgid "Invalid taskset structure: "
+msgstr "Structure du jeu d'exercices invalide:"
+
+#: inginious/frontend/pages/taskset_admin/utils.py:34
+msgid "You don't have admin rights on this taskset."
+msgstr "Vous n'avez pas les droits d'administrateur pour ce jeu d'exercices."
+
+#: inginious/frontend/pages/taskset_admin/utils.py:43
+msgid "This taskset is unreachable"
+msgstr "Ce jeu d'exercices n'est pas disponible."
+
+#: inginious/frontend/pages/taskset_admin/utils.py:61
+#: inginious/frontend/templates/course_admin/settings.html:6
+#: inginious/frontend/templates/taskset_admin/settings.html:6
+#: inginious/frontend/templates/taskset_admin/settings.html:49
+msgid "Settings"
+msgstr "Paramètres"
+
+#: inginious/frontend/pages/taskset_admin/utils.py:62
+#: inginious/frontend/templates/taskset_admin/template.html:20
+#: inginious/frontend/templates/taskset_admin/template.html:26
+msgid "Course template"
+msgstr "Modèle de cours"
+
#: inginious/frontend/plugins/auth/custom_auth_form.html:6
msgid "Login"
msgstr "Connexion"
#: inginious/frontend/plugins/auth/custom_auth_form.html:25
-#: inginious/frontend/templates/course_admin/edit_tabs/file_modals.html:26
-#: inginious/frontend/templates/course_admin/task_dispensers/util_task_edit_modal.html:22
+#: inginious/frontend/templates/task_dispensers_admin/util.html:67
+#: inginious/frontend/templates/task_dispensers_admin/util_task_edit_modal.html:22
+#: inginious/frontend/templates/taskset_admin/edit_tabs/file_modals.html:26
msgid "Close"
msgstr "Fermer"
@@ -898,20 +989,20 @@ msgstr "L'authentification a échoué"
#: inginious/frontend/plugins/auth/custom_auth_form.html:31
#: inginious/frontend/templates/auth.html:21
-#: inginious/frontend/templates/course_admin/task_list.html:78
+#: inginious/frontend/templates/taskset_admin/settings.html:102
msgid "Username"
msgstr "Nom d'utilisateur"
#: inginious/frontend/plugins/auth/custom_auth_form.html:34
#: inginious/frontend/templates/auth.html:24
-#: inginious/frontend/templates/course_admin/task_list.html:79
#: inginious/frontend/templates/course_register.html:29
+#: inginious/frontend/templates/taskset_admin/settings.html:103
msgid "Password"
msgstr "Mot de passe"
#: inginious/frontend/plugins/auth/custom_auth_form.html:36
#: inginious/frontend/templates/auth.html:26
-#: inginious/frontend/templates/layout.html:179
+#: inginious/frontend/templates/layout.html:180
#: inginious/frontend/templates/signin_button.html:4
#: inginious/frontend/templates/signin_button.html:11
msgid "Sign in"
@@ -953,28 +1044,32 @@ msgstr "Exercices à réaliser"
#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:11
#: inginious/frontend/templates/admin/admin_users.html:13
#: inginious/frontend/templates/course.html:103
-#: inginious/frontend/templates/course_admin/audience_edit.html:20
-#: inginious/frontend/templates/course_admin/danger_zone.html:18
-#: inginious/frontend/templates/course_admin/settings.html:18
-#: inginious/frontend/templates/course_admin/stats.html:27
-#: inginious/frontend/templates/course_admin/student_info.html:23
-#: inginious/frontend/templates/course_admin/student_list.html:20
-#: inginious/frontend/templates/course_admin/submission.html:32
-#: inginious/frontend/templates/course_admin/submissions.html:18
-#: inginious/frontend/templates/course_admin/task_edit.html:21
-#: inginious/frontend/templates/course_admin/task_list.html:19
+#: inginious/frontend/templates/course_admin/audience_edit.html:21
+#: inginious/frontend/templates/course_admin/danger_zone.html:19
+#: inginious/frontend/templates/course_admin/settings.html:19
+#: inginious/frontend/templates/course_admin/stats.html:28
+#: inginious/frontend/templates/course_admin/student_info.html:24
+#: inginious/frontend/templates/course_admin/student_list.html:21
+#: inginious/frontend/templates/course_admin/submission.html:33
+#: inginious/frontend/templates/course_admin/submissions.html:19
+#: inginious/frontend/templates/course_admin/task_list.html:20
#: inginious/frontend/templates/course_register.html:10
#: inginious/frontend/templates/course_user_settings.html:46
-#: inginious/frontend/templates/courselist.html:27
+#: inginious/frontend/templates/courselist.html:21
#: inginious/frontend/templates/group.html:61
#: inginious/frontend/templates/marketplace.html:26
-#: inginious/frontend/templates/marketplace_course.html:47
+#: inginious/frontend/templates/marketplace_taskset.html:47
#: inginious/frontend/templates/mycourses.html:11
#: inginious/frontend/templates/preferences/bindings.html:17
#: inginious/frontend/templates/preferences/delete.html:18
#: inginious/frontend/templates/preferences/profile.html:17
#: inginious/frontend/templates/queue.html:11
#: inginious/frontend/templates/task.html:266
+#: inginious/frontend/templates/taskset_admin/danger_zone.html:19
+#: inginious/frontend/templates/taskset_admin/settings.html:19
+#: inginious/frontend/templates/taskset_admin/task_edit.html:20
+#: inginious/frontend/templates/taskset_admin/template.html:20
+#: inginious/frontend/templates/tasksets.html:11
msgid "(current)"
msgstr "(actuel)"
@@ -984,8 +1079,8 @@ msgstr "Exercices à réaliser"
#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:20
msgid ""
-"This page lists the coming tasks ordered on deadlines for courses you are "
-"registered in."
+"This page lists the coming tasks ordered on deadlines for courses you are"
+" registered in."
msgstr ""
"Cette page liste les exercices à réaliser triés par ordre de date limite "
"pour les cours auxquels vous êtes inscrits."
@@ -996,42 +1091,42 @@ msgstr ""
#: inginious/frontend/templates/course_unavailable.html:9
#: inginious/frontend/templates/course_user_settings.html:44
#: inginious/frontend/templates/courselist.html:5
-#: inginious/frontend/templates/courselist.html:26
-#: inginious/frontend/templates/courselist.html:33
+#: inginious/frontend/templates/courselist.html:20
+#: inginious/frontend/templates/courselist.html:27
#: inginious/frontend/templates/layout.html:149
-#: inginious/frontend/templates/layout.html:170
-#: inginious/frontend/templates/mycourses.html:25
+#: inginious/frontend/templates/layout.html:171
#: inginious/frontend/templates/task.html:263
+#: inginious/frontend/templates/tasksets.html:24
msgid "Course list"
msgstr "Liste des cours"
#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:29
#: inginious/frontend/templates/course.html:21
#: inginious/frontend/templates/group.html:19
-#: inginious/frontend/templates/mycourses.html:29
+#: inginious/frontend/templates/mycourses.html:23
msgid "Last tried exercises"
msgstr "Derniers exercices essayés"
-#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:48
+#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:51
#: inginious/frontend/templates/course.html:40
#: inginious/frontend/templates/group.html:39
-#: inginious/frontend/templates/mycourses.html:48
+#: inginious/frontend/templates/mycourses.html:45
msgid "No submissions"
msgstr "Pas de soumission"
-#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:56
+#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:59
msgid "My Upcoming Tasks"
msgstr "Mes exercices à réaliser"
-#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:60
+#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:63
msgid "Switch time planner"
msgstr "Changer de planificateur"
-#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:71
+#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:74
msgid "Modify time planner"
msgstr "Modifier le planificateur"
-#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:77
+#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:80
msgid ""
"This allows to display only the tasks whose deadline is in a certain "
"temporal proximity."
@@ -1039,64 +1134,79 @@ msgstr ""
"Ceci vous permet de n'afficher que les exercices dont la date limite est "
"dans une certaine proximité temporelle."
-#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:78
+#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:81
msgid "Number of days:"
msgstr "Nombre de jours :"
-#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:86
+#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:89
#: inginious/frontend/templates/admin/admin_users.html:111
#: inginious/frontend/templates/admin/admin_users.html:131
#: inginious/frontend/templates/admin/admin_users.html:167
#: inginious/frontend/templates/admin/admin_users.html:187
-#: inginious/frontend/templates/course_admin/audience_edit.html:62
-#: inginious/frontend/templates/course_admin/danger_zone.html:85
-#: inginious/frontend/templates/course_admin/danger_zone.html:166
-#: inginious/frontend/templates/course_admin/student_info.html:106
-#: inginious/frontend/templates/course_admin/student_list.html:275
+#: inginious/frontend/templates/course_admin/audience_edit.html:63
+#: inginious/frontend/templates/course_admin/danger_zone.html:86
+#: inginious/frontend/templates/course_admin/danger_zone.html:167
+#: inginious/frontend/templates/course_admin/student_info.html:107
+#: inginious/frontend/templates/course_admin/student_list.html:276
#: inginious/frontend/templates/course_admin/student_list_table.html:86
-#: inginious/frontend/templates/course_admin/submissions.html:249
-#: inginious/frontend/templates/course_admin/task_dispensers/util_delete_modal.html:27
-#: inginious/frontend/templates/course_admin/task_list.html:56
-#: inginious/frontend/templates/course_admin/task_list.html:108
+#: inginious/frontend/templates/course_admin/submissions.html:250
+#: inginious/frontend/templates/course_admin/task_list.html:57
+#: inginious/frontend/templates/course_admin/task_list.html:100
#: inginious/frontend/templates/lti_bind.html:32
#: inginious/frontend/templates/preferences/delete.html:59
#: inginious/frontend/templates/queue.html:112
+#: inginious/frontend/templates/task_dispensers_admin/util_delete_modal.html:29
+#: inginious/frontend/templates/task_dispensers_admin/util_move_modal.html:17
+#: inginious/frontend/templates/taskset_admin/danger_zone.html:71
+#: inginious/frontend/templates/taskset_admin/settings.html:151
+#: inginious/frontend/templates/taskset_admin/settings.html:177
+#: inginious/frontend/templates/taskset_admin/template.html:57
+#: inginious/frontend/templates/taskset_admin/template.html:101
+#: inginious/frontend/templates/tasksets.html:101
#: inginious/frontend/templates/unregister_modal.html:19
msgid "Cancel"
msgstr "Annuler"
-#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:87
-#: inginious/frontend/templates/course_admin/settings.html:486
-#: inginious/frontend/templates/course_admin/task_edit.html:10
-#: inginious/frontend/templates/course_admin/task_edit.html:31
-#: inginious/frontend/templates/course_admin/task_edit.html:97
-#: inginious/frontend/templates/course_admin/task_list.html:57
-#: inginious/frontend/templates/course_admin/task_list.html:113
+#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:90
+#: inginious/frontend/templates/course_admin/settings.html:481
+#: inginious/frontend/templates/course_admin/task_list.html:58
+#: inginious/frontend/templates/course_admin/task_list.html:105
+#: inginious/frontend/templates/taskset_admin/settings.html:51
+#: inginious/frontend/templates/taskset_admin/task_edit.html:10
+#: inginious/frontend/templates/taskset_admin/task_edit.html:30
+#: inginious/frontend/templates/taskset_admin/task_edit.html:95
+#: inginious/frontend/templates/taskset_admin/template.html:58
+#: inginious/frontend/templates/taskset_admin/template.html:106
msgid "Save changes"
msgstr "Appliquer les changements"
-#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:102
+#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:105
msgid "You have no upcoming tasks"
msgstr "Vous n'avez aucun exercice à réaliser"
-#: inginious/frontend/task_dispensers/combinatory_test.py:22
+#: inginious/frontend/task_dispensers/combinatory_test.py:24
msgid "Combinatory test"
msgstr "Test combinatoire"
-#: inginious/frontend/task_dispensers/combinatory_test.py:45
+#: inginious/frontend/task_dispensers/combinatory_test.py:47
msgid "Amount of tasks to be displayed"
msgstr "Nombre d'exercices à afficher"
-#: inginious/frontend/task_dispensers/toc.py:40
+#: inginious/frontend/task_dispensers/toc.py:52
msgid "Table of contents"
msgstr "Table des matières"
-#: inginious/frontend/task_dispensers/toc.py:103
+#: inginious/frontend/task_dispensers/toc.py:115
msgid "Closed by default"
msgstr "Fermé par défaut"
+#: inginious/frontend/task_dispensers/toc.py:116
+msgid "Hidden if empty"
+msgstr "Masqué si vide"
+
#: inginious/frontend/task_dispensers/util.py:49
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/groups.html:4
+#: inginious/frontend/templates/task_dispensers_admin/config_items/groups.html:6
+#: inginious/frontend/templates/task_dispensers_admin/config_items/groups.html:10
msgid "Submission mode"
msgstr "Mode de soumission"
@@ -1105,7 +1215,8 @@ msgid "Weight"
msgstr "Poids de la note"
#: inginious/frontend/task_dispensers/util.py:97
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/submission_storage.html:4
+#: inginious/frontend/templates/task_dispensers_admin/config_items/submission_storage.html:6
+#: inginious/frontend/templates/task_dispensers_admin/config_items/submission_storage.html:12
msgid "Submission storage"
msgstr "Stockage des soumissions"
@@ -1114,7 +1225,8 @@ msgid "Evaluation mode"
msgstr "Mode d'évaluation"
#: inginious/frontend/task_dispensers/util.py:145
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/categories.html:4
+#: inginious/frontend/templates/task_dispensers_admin/config_items/categories.html:6
+#: inginious/frontend/templates/task_dispensers_admin/config_items/categories.html:9
msgid "Categories"
msgstr "Catégories"
@@ -1124,16 +1236,16 @@ msgid "Submission limit"
msgstr "Limite de soumission"
#: inginious/frontend/task_dispensers/util.py:193
-#: inginious/frontend/templates/course_admin/settings.html:43
-#: inginious/frontend/templates/course_admin/settings.html:140
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/accessibility.html:4
+#: inginious/frontend/templates/course_admin/settings.html:44
+#: inginious/frontend/templates/course_admin/settings.html:135
+#: inginious/frontend/templates/task_dispensers_admin/config_items/accessibility.html:6
+#: inginious/frontend/templates/task_dispensers_admin/config_items/accessibility.html:15
msgid "Accessibility"
msgstr "Accessibilité"
#: inginious/frontend/task_dispensers/util.py:219
msgid "One section don't contain a sections list nor a tasks list"
-msgstr ""
-"Une section ne contient ni de liste de sections ni de liste d'exercices"
+msgstr "Une section ne contient ni de liste de sections ni de liste d'exercices"
#: inginious/frontend/task_dispensers/util.py:251
msgid "No title for one section"
@@ -1164,17 +1276,12 @@ msgstr "Note actuelle"
#: inginious/frontend/templates/course_user_settings.html:13
msgid ""
"This course is currently invisible for students. You can change this by "
-"modifying the \"accessibility\" option in the configuration of the course."
+"modifying the \"accessibility\" option in the configuration of the "
+"course."
msgstr ""
"Ce cours n'est pas affiché aux étudiants. Vous pouvez changer cela en "
"modifiant l'option \"accessibilité\" dans les paramètres du cours."
-#: inginious/frontend/templates/course.html:53
-#: inginious/frontend/templates/course.html:71
-#: inginious/frontend/templates/course_user_settings.html:33
-msgid "Course Settings"
-msgstr "Paramètres du cours"
-
#: inginious/frontend/templates/course.html:56
#: inginious/frontend/templates/course_user_settings.html:18
msgid "Course administration"
@@ -1213,14 +1320,21 @@ msgstr ""
"problèmes."
#: inginious/frontend/templates/course.html:99
+#: inginious/frontend/templates/course_admin/audience_edit.html:15
+#: inginious/frontend/templates/course_admin/danger_zone.html:15
+#: inginious/frontend/templates/course_admin/settings.html:15
+#: inginious/frontend/templates/course_admin/stats.html:22
+#: inginious/frontend/templates/course_admin/student_info.html:17
+#: inginious/frontend/templates/course_admin/student_list.html:15
+#: inginious/frontend/templates/course_admin/submission.html:19
+#: inginious/frontend/templates/course_admin/submissions.html:15
+#: inginious/frontend/templates/course_admin/task_list.html:16
#: inginious/frontend/templates/course_user_settings.html:42
-#: inginious/frontend/templates/courselist.html:17
#: inginious/frontend/templates/layout.html:154
-#: inginious/frontend/templates/marketplace.html:17
#: inginious/frontend/templates/mycourses.html:5
#: inginious/frontend/templates/mycourses.html:10
#: inginious/frontend/templates/mycourses.html:18
-#: inginious/frontend/templates/mycourses.html:55
+#: inginious/frontend/templates/mycourses.html:52
#: inginious/frontend/templates/task.html:261
msgid "My courses"
msgstr "Mes cours"
@@ -1238,7 +1352,7 @@ msgid "Register to course '{}'"
msgstr "S'inscrire au cours '{}'"
#: inginious/frontend/templates/course_register.html:11
-#: inginious/frontend/templates/layout.html:175
+#: inginious/frontend/templates/layout.html:176
#: inginious/frontend/templates/register.html:5
msgid "Register"
msgstr "S'enregistrer"
@@ -1274,8 +1388,8 @@ msgid ""
"This course must be accessed on an external platform. Read the course "
"description or contact the teaching team for more information."
msgstr ""
-"Ce cours est accessible via une plateforme externe. Veuillez vous référer à "
-"la description du cours ou contacter l'équipe enseignante pour plus "
+"Ce cours est accessible via une plateforme externe. Veuillez vous référer"
+" à la description du cours ou contacter l'équipe enseignante pour plus "
"d'information."
#: inginious/frontend/templates/course_unavailable.html:25
@@ -1296,22 +1410,22 @@ msgstr "Cours publics"
#: inginious/frontend/templates/courselist.html:12
msgid ""
-"This page lists all the courses that are available now. If you are a course "
-"administrator, go to your 'My courses' page to see all of them."
+"This page lists all the courses that are available now. If you are a "
+"course administrator, go to your 'My courses' page to see all of them."
msgstr ""
"Cette page liste tous les cours disponibles pour l'instant. Si vous êtes "
-"administrateur de cours, rendez vous sur la page 'Mes cours' pour les voir "
-"tous."
+"administrateur de cours, rendez vous sur la page 'Mes cours' pour les "
+"voir tous."
-#: inginious/frontend/templates/courselist.html:62
+#: inginious/frontend/templates/courselist.html:56
msgid "External platform"
msgstr "Plateforme externe"
-#: inginious/frontend/templates/courselist.html:66
+#: inginious/frontend/templates/courselist.html:60
msgid "Auto-registration"
msgstr "Auto-enregistrement"
-#: inginious/frontend/templates/courselist.html:70
+#: inginious/frontend/templates/courselist.html:64
msgid "Password needed"
msgstr "Mot de passe requis"
@@ -1319,8 +1433,8 @@ msgstr "Mot de passe requis"
msgid "Register for a group"
msgstr "S'inscrire dans un groupe"
-#: inginious/frontend/templates/course_admin/student_list.html:48
-#: inginious/frontend/templates/course_admin/student_list.html:208
+#: inginious/frontend/templates/course_admin/student_list.html:49
+#: inginious/frontend/templates/course_admin/student_list.html:209
#: inginious/frontend/templates/group.html:59
#: inginious/frontend/templates/group.html:68
msgid "Groups"
@@ -1341,19 +1455,20 @@ msgstr "Envoyer un email"
#: inginious/frontend/templates/group.html:114
msgid ""
-"No set group in this course. If you think this is a mistake, please contact "
-"the course administrator."
+"No set group in this course. If you think this is a mistake, please "
+"contact the course administrator."
msgstr ""
"Pas de groupe configuré pour ce cours. Si vous pensez qu'il s'agitd'une "
"erreur, veuillez contacter l'administrateur du cours."
#: inginious/frontend/templates/group.html:118
msgid ""
-"Groups have been set up but you're not allowed to register in anyone. Please "
-"contact the course administrator."
+"Groups have been set up but you're not allowed to register in anyone. "
+"Please contact the course administrator."
msgstr ""
-"Des groupes ont été configurés pour ce cours mais vous n'êtes pas autorisés "
-"à vous y inscrire. Veuillez contacter l'administrateur du cours."
+"Des groupes ont été configurés pour ce cours mais vous n'êtes pas "
+"autorisés à vous y inscrire. Veuillez contacter l'administrateur du "
+"cours."
#: inginious/frontend/templates/group.html:121
#: inginious/frontend/templates/group.html:126
@@ -1362,9 +1477,9 @@ msgstr "Mon groupe"
#: inginious/frontend/templates/group.html:123
msgid ""
-"You're not registered to a group. Please consider registration in one of the "
-"group below to take part in all course activities. If no more group is "
-"available, please contact the course administrator."
+"You're not registered to a group. Please consider registration in one of "
+"the group below to take part in all course activities. If no more group "
+"is available, please contact the course administrator."
msgstr ""
"Vous n'êtes pas inscrit dans un groupe. Veuillez vous enregistrer pour "
"prendre part à toutes les activités du cours. S'il n'y a plus de groupe "
@@ -1375,8 +1490,8 @@ msgid ""
"You're not yet registered to a group. Please contact the course "
"administrator for more information."
msgstr ""
-"Vous n'êtes pas inscrit dans un groupe. Veuillez contacter l'admnistrateur "
-"du cours pour plus d'informations."
+"Vous n'êtes pas inscrit dans un groupe. Veuillez contacter "
+"l'admnistrateur du cours pour plus d'informations."
#: inginious/frontend/templates/group.html:133
msgid "All groups"
@@ -1399,8 +1514,8 @@ msgid ""
"Internal error Something bad happened. The error has been logged, "
"please contact your administrator if the error persists."
msgstr ""
-"Erreur interne Quelque chose de mal est arrivé. L'erreur a été notée, "
-"veuillez contacter votre administrateur si l'erreur persiste."
+"Erreur interne Quelque chose de mal est arrivé. L'erreur a été "
+"notée, veuillez contacter votre administrateur si l'erreur persiste."
#: inginious/frontend/templates/layout.html:19
#: inginious/frontend/templates/layout.html:76
@@ -1412,22 +1527,25 @@ msgstr "false"
msgid "Open menu"
msgstr "Ouvrir le menu"
-#: inginious/frontend/templates/course_admin/audience_edit.html:16
-#: inginious/frontend/templates/course_admin/danger_zone.html:16
-#: inginious/frontend/templates/course_admin/settings.html:16
-#: inginious/frontend/templates/course_admin/stats.html:23
-#: inginious/frontend/templates/course_admin/student_info.html:18
-#: inginious/frontend/templates/course_admin/student_list.html:16
-#: inginious/frontend/templates/course_admin/submission.html:21
-#: inginious/frontend/templates/course_admin/submissions.html:16
-#: inginious/frontend/templates/course_admin/task_edit.html:17
-#: inginious/frontend/templates/course_admin/task_list.html:17
+#: inginious/frontend/templates/course_admin/audience_edit.html:17
+#: inginious/frontend/templates/course_admin/danger_zone.html:17
+#: inginious/frontend/templates/course_admin/settings.html:17
+#: inginious/frontend/templates/course_admin/stats.html:24
+#: inginious/frontend/templates/course_admin/student_info.html:19
+#: inginious/frontend/templates/course_admin/student_list.html:17
+#: inginious/frontend/templates/course_admin/submission.html:22
+#: inginious/frontend/templates/course_admin/submissions.html:17
+#: inginious/frontend/templates/course_admin/task_list.html:18
#: inginious/frontend/templates/layout.html:142
#: inginious/frontend/templates/task.html:128
+#: inginious/frontend/templates/taskset_admin/danger_zone.html:18
+#: inginious/frontend/templates/taskset_admin/settings.html:18
+#: inginious/frontend/templates/taskset_admin/task_edit.html:19
+#: inginious/frontend/templates/taskset_admin/template.html:19
msgid "Administration"
msgstr "Administration"
-#: inginious/frontend/templates/course_admin/stats.html:260
+#: inginious/frontend/templates/course_admin/stats.html:261
#: inginious/frontend/templates/layout.html:145
msgid "Users"
msgstr "Utilisateurs"
@@ -1436,46 +1554,55 @@ msgstr "Utilisateurs"
#: inginious/frontend/templates/marketplace.html:5
#: inginious/frontend/templates/marketplace.html:25
#: inginious/frontend/templates/marketplace.html:32
-#: inginious/frontend/templates/marketplace_course.html:45
+#: inginious/frontend/templates/marketplace_taskset.html:45
msgid "Marketplace"
msgstr "Magasin de cours"
#: inginious/frontend/templates/layout.html:155
+#: inginious/frontend/templates/marketplace.html:17
+#: inginious/frontend/templates/tasksets.html:5
+#: inginious/frontend/templates/tasksets.html:10
+#: inginious/frontend/templates/tasksets.html:32
+msgid "My tasksets"
+msgstr "Mes jeux d'exercices"
+
+#: inginious/frontend/templates/layout.html:156
#: inginious/frontend/templates/preferences/bindings.html:15
#: inginious/frontend/templates/preferences/delete.html:16
#: inginious/frontend/templates/preferences/profile.html:15
msgid "Preferences"
msgstr "Préférences"
-#: inginious/frontend/templates/layout.html:156
+#: inginious/frontend/templates/layout.html:157
msgid "Service status"
msgstr "Etat du service"
-#: inginious/frontend/templates/layout.html:157
+#: inginious/frontend/templates/layout.html:158
msgid "Log out"
msgstr "Déconnexion"
-#: inginious/frontend/templates/layout.html:161
+#: inginious/frontend/templates/layout.html:162
msgid "English"
msgstr "Français"
-#: inginious/frontend/templates/layout.html:195
+#: inginious/frontend/templates/layout.html:196
msgid ""
-"Powered by INGInious"
-"a> v.{} , a free and open-source grading tool."
+"Powered by INGInious v.{} , a free and open-source grading "
+"tool."
msgstr ""
-"Propulsé par INGInious v.{}, un outil de correction libre."
+"Propulsé par INGInious v.{}, un outil de correction libre."
-#: inginious/frontend/templates/layout.html:197
+#: inginious/frontend/templates/layout.html:198
msgid "Running INGInious v.{}"
msgstr "Fourni par INGInious v.{}"
-#: inginious/frontend/templates/layout.html:199
+#: inginious/frontend/templates/layout.html:200
msgid "INGInious is distributed under AGPL license"
msgstr "INGInious est distribué sous licence AGPL"
-#: inginious/frontend/templates/layout.html:203
+#: inginious/frontend/templates/layout.html:204
msgid "Privacy Policy"
msgstr "Politique de confidentialité"
@@ -1536,8 +1663,8 @@ msgid ""
"In order to use INGInious via this platform, please bind your existing "
"account or create a new one."
msgstr ""
-"Afin d'utiliser INGInious via cette platforme, veuillez lier votre compte "
-"existant ou en créer un nouveau."
+"Afin d'utiliser INGInious via cette platforme, veuillez lier votre compte"
+" existant ou en créer un nouveau."
#: inginious/frontend/templates/lti_login.html:32
msgid "Bind your INGInious account"
@@ -1549,11 +1676,11 @@ msgstr "Rafraîchir la page"
#: inginious/frontend/templates/maintenance.html:9
msgid ""
-"This INGInious instance is currently under maintenance. Please come "
-"back in a few minutes!"
+"This INGInious instance is currently under maintenance. Please "
+"come back in a few minutes!"
msgstr ""
-"Cette instance d'INGInious est sous maintenance. Veuillez réessayer "
-"dans quelques minutes !"
+"Cette instance d'INGInious est sous maintenance. Veuillez "
+"réessayer dans quelques minutes !"
#: inginious/frontend/templates/marketplace.html:10
msgid "How to use"
@@ -1561,124 +1688,104 @@ msgstr "Comment cela fonctionne"
#: inginious/frontend/templates/marketplace.html:12
msgid ""
-"This page list all the course that are publicly avalaible. When you click "
-"import, it will create a new course on your INGInious instance. You will be "
-"admin of this new course and it will contain all the tasks and the structure "
-"of the imported course"
+"This page list all the tasksets that are publicly avalaible. When you "
+"click import, it will create a new taskset on your INGInious instance. "
+"You will be admin of this new taskset and it will contain all the tasks "
+"of the imported taskset."
msgstr ""
-"Cette page liste tous les cours disponibles publiquement. Lorsque vous "
-"cliquer sur Importer, un nouveau cours sera créé sur votre instance "
-"INGInious. Vous serez administrateur de ce nouveau cours qui contiendra la "
-"structure et l'ensemble des exercices du cours importé."
+"Cette page liste tous les jeux d'exercices disponibles publiquement. Lorsque vous "
+"cliquez sur Importer, un nouveau jeu d'exercices sera créé sur votre instance "
+"INGInious. Vous serez administrateur de ce nouveau jeu d'exercices qui contiendra "
+"la structure et l'ensemble des exercices du jeu importé."
#: inginious/frontend/templates/marketplace.html:56
-#: inginious/frontend/templates/marketplace_course.html:68
-msgid "Import course"
-msgstr "Importer le cours"
+#: inginious/frontend/templates/marketplace_taskset.html:68
+msgid "Import taskset"
+msgstr "Importer le jeu d'exercices"
#: inginious/frontend/templates/marketplace.html:74
-msgid "No public courses available"
-msgstr "Pas de cours publiques disponible."
+msgid "No public tasksets available"
+msgstr "Pas de jeu d'exercice publique disponible."
#: inginious/frontend/templates/marketplace.html:84
-msgid "Import this course"
-msgstr "Importer ce cours"
+msgid "Import this taskset"
+msgstr "Importer ce jeu d'exercices"
#: inginious/frontend/templates/marketplace.html:89
msgid ""
-"This will create a new course with the ID bellow. This new course will "
-"contain the tasks and structure of the selected course"
+"This will create a new taskset with the ID bellow. This new taskset will "
+"contain the tasks and structure of the selected taskset"
msgstr ""
-"Un nouveau cours sera créé avec l'identifiant ci-dessous. Celui-ci "
-"contiendra la structure et les exercices du cours sélectionné."
+"Un nouveau jeu d'exercices sera créé avec l'identifiant ci-dessous. Celui-ci "
+"contiendra la structure et les exercices du jeu d'exercices sélectionné."
#: inginious/frontend/templates/marketplace.html:90
msgid "Course ID"
msgstr "Identifiant du cours"
#: inginious/frontend/templates/marketplace.html:94
-msgid "Create course"
-msgstr "Créer le cours"
+msgid "Create taskset"
+msgstr "Créer un nouveau jeu d'exercices"
-#: inginious/frontend/templates/marketplace_course.html:7
+#: inginious/frontend/templates/marketplace_taskset.html:7
#: inginious/frontend/templates/task.html:13
msgid "Information"
msgstr "Informations"
-#: inginious/frontend/templates/marketplace_course.html:11
+#: inginious/frontend/templates/marketplace_taskset.html:11
msgid "Language(s)"
msgstr "Langue(s)"
-#: inginious/frontend/templates/marketplace_course.html:17
+#: inginious/frontend/templates/marketplace_taskset.html:17
msgid "License"
msgstr "Licence"
-#: inginious/frontend/templates/marketplace_course.html:23
+#: inginious/frontend/templates/marketplace_taskset.html:23
msgid "Maintainer(s)"
msgstr "Gestionnaire(s)"
-#: inginious/frontend/templates/marketplace_course.html:29
+#: inginious/frontend/templates/marketplace_taskset.html:29
#: inginious/frontend/templates/task.html:17
msgid "Author(s)"
msgstr "Auteur(s)"
-#: inginious/frontend/templates/marketplace_course.html:35
+#: inginious/frontend/templates/marketplace_taskset.html:35
msgid "Link"
msgstr "Lien"
-#: inginious/frontend/templates/marketplace_course.html:65
-#: inginious/frontend/templates/mycourses.html:110
-msgid "New course id"
-msgstr "Nouvel identifiant de cours"
+#: inginious/frontend/templates/marketplace_taskset.html:65
+#: inginious/frontend/templates/tasksets.html:75
+msgid "New taskset id"
+msgstr "Nouvel identifiant de jeu d'exercices"
#: inginious/frontend/templates/mycourses.html:20
msgid ""
-"This page lists all the courses you are currently enrolled in. You can find "
-"new courses on the course list page."
+"This page lists all the courses you are currently enrolled in. You can "
+"find new courses on the course list page."
msgstr ""
-"Cette page liste tous les cours auxquels vous êtes actuellement enregistré. "
-"Vous pouvez trouver de nouveaux cours dans la Liste des cours"
+"Cette page liste tous les cours auxquels vous êtes actuellement "
+"enregistré. Vous pouvez trouver de nouveaux cours dans la Liste des cours"
-#: inginious/frontend/templates/mycourses.html:73
+#: inginious/frontend/templates/mycourses.html:70
msgid "LTI course"
msgstr "Cours LTI"
-#: inginious/frontend/templates/mycourses.html:75
+#: inginious/frontend/templates/mycourses.html:72
msgid "Hidden course"
msgstr "Cours masqué"
-#: inginious/frontend/templates/mycourses.html:79
+#: inginious/frontend/templates/mycourses.html:76
msgid "Administrator"
msgstr "Administrateur"
-#: inginious/frontend/templates/mycourses.html:81
-msgid "Tutor"
-msgstr "Tuteur"
-
-#: inginious/frontend/templates/mycourses.html:83
+#: inginious/frontend/templates/mycourses.html:78
msgid "Student"
msgstr "Etudiant"
-#: inginious/frontend/templates/mycourses.html:91
+#: inginious/frontend/templates/mycourses.html:86
msgid "You are not registered to any course"
msgstr "Vous n'êtes inscrit à aucun cours"
-#: inginious/frontend/templates/mycourses.html:99
-msgid "Course created."
-msgstr "Cours créé."
-
-#: inginious/frontend/templates/mycourses.html:104
-msgid "Failed to create the course."
-msgstr "Une erreur s'est produite lors de la création du cours."
-
-#: inginious/frontend/templates/mycourses.html:109
-msgid "Course"
-msgstr "Cours"
-
-#: inginious/frontend/templates/mycourses.html:113
-msgid "Create new course"
-msgstr "Créer un nouveau cours"
-
#: inginious/frontend/templates/notfound.html:5
#: inginious/frontend/templates/notfound.html:10
msgid "Error 404"
@@ -1692,17 +1799,16 @@ msgstr "File d'attente"
#: inginious/frontend/templates/queue.html:19
msgid "This page shows a snapshot of the job queue."
-msgstr ""
-"Cette page affiche un instantané de la file d'attente."
+msgstr "Cette page affiche un instantané de la file d'attente."
#: inginious/frontend/templates/queue.html:21
msgid "Running jobs"
msgstr "Tâches en cours d'exécution"
-#: inginious/frontend/templates/course_admin/subproblems/code.html:19
-#: inginious/frontend/templates/course_admin/subproblems/file.html:23
#: inginious/frontend/templates/queue.html:25
#: inginious/frontend/templates/queue.html:66
+#: inginious/frontend/templates/taskset_admin/subproblems/code.html:19
+#: inginious/frontend/templates/taskset_admin/subproblems/file.html:23
msgid "Type"
msgstr "Type"
@@ -1710,11 +1816,12 @@ msgstr "Type"
msgid "Agent name"
msgstr "Nom de l'agent"
-#: inginious/frontend/templates/course_admin/edit_tabs/basic.html:5
-#: inginious/frontend/templates/course_admin/settings.html:62
-#: inginious/frontend/templates/course_admin/task_edit.html:124
+#: inginious/frontend/templates/course_admin/settings.html:63
#: inginious/frontend/templates/queue.html:27
#: inginious/frontend/templates/queue.html:67
+#: inginious/frontend/templates/taskset_admin/edit_tabs/basic.html:5
+#: inginious/frontend/templates/taskset_admin/settings.html:56
+#: inginious/frontend/templates/taskset_admin/task_edit.html:122
msgid "Name"
msgstr "Nom"
@@ -1806,11 +1913,11 @@ msgstr "Confirmez le mot de passe :"
#: inginious/frontend/templates/preferences/profile.html:90
#: inginious/frontend/templates/register.html:48
msgid ""
-"I have read the {a1start}Terms of service{a1end} and the {a2start}Privacy "
-"policy{a2end}."
+"I have read the {a1start}Terms of service{a1end} and the {a2start}Privacy"
+" policy{a2end}."
msgstr ""
-"J'ai pris connaissance des {a1start}conditions d'utilisation{a1end} et de la "
-"{a2start}politique de confidentialité{a2end}."
+"J'ai pris connaissance des {a1start}conditions d'utilisation{a1end} et de"
+" la {a2start}politique de confidentialité{a2end}."
#: inginious/frontend/templates/register.html:52
msgid "Sign up"
@@ -1830,18 +1937,18 @@ msgstr "Configurez le nouveau mot de passe"
#: inginious/frontend/templates/signin_button.html:6
msgid ""
-"Please register or sign in to see the complete list of courses and be able "
-"to submit answers to problems."
+"Please register or sign in to see the complete list of courses and be "
+"able to submit answers to problems."
msgstr ""
-"Veuillez vous enregistrer ou vous connecter pour voir la liste complète des "
-"cours et pouvoir soumettrez des réponses aux problèmes."
+"Veuillez vous enregistrer ou vous connecter pour voir la liste complète "
+"des cours et pouvoir soumettrez des réponses aux problèmes."
#: inginious/frontend/templates/task.html:23
msgid "Contact"
msgstr "Contact"
-#: inginious/frontend/templates/course_admin/edit_tabs/basic.html:49
#: inginious/frontend/templates/task.html:24
+#: inginious/frontend/templates/taskset_admin/edit_tabs/basic.html:49
msgid "Contact link"
msgstr "Lien de contact"
@@ -1865,8 +1972,8 @@ msgstr "Etat"
msgid "Not yet attempted"
msgstr "Pas encore essayé"
-#: inginious/frontend/templates/course_admin/student_info.html:73
-#: inginious/frontend/templates/course_admin/submissions.html:133
+#: inginious/frontend/templates/course_admin/student_info.html:74
+#: inginious/frontend/templates/course_admin/submissions.html:134
#: inginious/frontend/templates/task.html:52
msgid "Succeeded"
msgstr "Réussi"
@@ -1875,8 +1982,8 @@ msgstr "Réussi"
msgid "Waiting for verification"
msgstr "En attente de correction"
-#: inginious/frontend/templates/course_admin/student_info.html:76
-#: inginious/frontend/templates/course_admin/submissions.html:137
+#: inginious/frontend/templates/course_admin/student_info.html:77
+#: inginious/frontend/templates/course_admin/submissions.html:138
#: inginious/frontend/templates/task.html:63
msgid "Failed"
msgstr "Echoué"
@@ -1890,8 +1997,9 @@ msgstr "Note"
msgid "Attempts"
msgstr "Nombre d'essais"
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/submission_limit.html:7
#: inginious/frontend/templates/task.html:86
+#: inginious/frontend/templates/task_dispensers_admin/config_items/submission_limit.html:8
+#: inginious/frontend/templates/task_dispensers_admin/config_items/submission_limit.html:20
msgid "No limitation"
msgstr "Pas de limite"
@@ -1907,8 +2015,8 @@ msgstr "{nb_submissions} soumissions"
msgid "Category tags"
msgstr "Étiquettes de catégories"
-#: inginious/frontend/templates/course_admin/settings.html:49
-#: inginious/frontend/templates/course_admin/submission.html:98
+#: inginious/frontend/templates/course_admin/settings.html:50
+#: inginious/frontend/templates/course_admin/submission.html:99
#: inginious/frontend/templates/task.html:114
msgid "Tags"
msgstr "Etiquettes"
@@ -1926,19 +2034,20 @@ msgid ""
"This task is currently invisible for students. You can change this by "
"modifying the \"accessible\" option in the configuration of the task."
msgstr ""
-"Cet exercice n'est pas affiché aux étudiants. Vous pouvez changer cela en "
-"modifiant l'option \"accessibilité\" dans les paramètres de l'exercice."
+"Cet exercice n'est pas affiché aux étudiants. Vous pouvez changer cela en"
+" modifiant l'option \"accessibilité\" dans les paramètres de l'exercice."
-#: inginious/frontend/templates/course_admin/stats.html:81
-#: inginious/frontend/templates/course_admin/student_list.html:185
+#: inginious/frontend/templates/course_admin/stats.html:82
+#: inginious/frontend/templates/course_admin/student_list.html:186
#: inginious/frontend/templates/course_admin/student_list_table.html:59
-#: inginious/frontend/templates/course_admin/task_dispensers/task_buttons.html:18
#: inginious/frontend/templates/task.html:141
+#: inginious/frontend/templates/task_dispensers_admin/task_buttons.html:26
msgid "View submissions"
msgstr "Voir les soumissions"
-#: inginious/frontend/templates/course_admin/task_dispensers/task_buttons.html:14
#: inginious/frontend/templates/task.html:145
+#: inginious/frontend/templates/task_dispensers_admin/task_buttons.html:20
+#: inginious/frontend/templates/taskset_admin/settings.html:127
msgid "Edit task"
msgstr "Editer l'exercice"
@@ -1959,13 +2068,15 @@ msgstr "Groupe"
msgid "For evaluation"
msgstr "Pour évaluation"
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/evaluation_mode.html:15
#: inginious/frontend/templates/task.html:180
+#: inginious/frontend/templates/task_dispensers_admin/config_items/evaluation_mode.html:8
+#: inginious/frontend/templates/task_dispensers_admin/config_items/evaluation_mode.html:21
msgid "Last submission"
msgstr "Dernière soumission"
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/evaluation_mode.html:9
#: inginious/frontend/templates/task.html:182
+#: inginious/frontend/templates/task_dispensers_admin/config_items/evaluation_mode.html:7
+#: inginious/frontend/templates/task_dispensers_admin/config_items/evaluation_mode.html:15
msgid "Best submission"
msgstr "Meilleure soumission"
@@ -2000,8 +2111,7 @@ msgstr "Exercice suivant"
#: inginious/frontend/templates/task.html:315
msgid "Alternatively, you can also paste this command into your terminal:"
-msgstr ""
-"Vous pouvez également copier/coller cette commande dans votre terminal :"
+msgstr "Vous pouvez également copier/coller cette commande dans votre terminal :"
#: inginious/frontend/templates/task.html:317
msgid "Paste this command into your terminal:"
@@ -2025,12 +2135,12 @@ msgstr "{} est trop lourd."
#: inginious/frontend/templates/task.html:340
msgid ""
-"The raw data from the container will be displayed here, helping you to debug "
-"the task. This box is only displayed because you are an administrator "
-"of this course. It is not displayed to students."
+"The raw data from the container will be displayed here, helping you to "
+"debug the task. This box is only displayed because you are an "
+"administrator of this course. It is not displayed to students."
msgstr ""
-"Les données brutes du conteneur seront affichées ici, afin de vous aider à "
-"déboguer l'exercice. Ce dialogue est affiché car vous êtes "
+"Les données brutes du conteneur seront affichées ici, afin de vous aider "
+"à déboguer l'exercice. Ce dialogue est affiché car vous êtes "
"administrateur du cours. Il n'est pas affiché aux étudiants."
#: inginious/frontend/templates/task.html:373
@@ -2079,13 +2189,50 @@ msgstr "Vous n'êtes pas autorisé à soumettre"
msgid "Task unavailable"
msgstr "Exercice non disponible"
+#: inginious/frontend/templates/tasksets.html:19
+msgid ""
+"This page lists all the tasksets you have access to. You can find "
+"instantiated courses on the course list page."
+msgstr ""
+"Cette page liste tous les jeux d'exercices auxquels vous avez accès. "
+"Vous pouvez trouver les cours instanciés dans la Liste des cours"
+
+#: inginious/frontend/templates/tasksets.html:56
+msgid "Edit taskset"
+msgstr "Editer le jeu d'exercices"
+
+#: inginious/frontend/templates/tasksets.html:60
+#: inginious/frontend/templates/tasksets.html:102
+msgid "Instantiate"
+msgstr "Instancier"
+
+#: inginious/frontend/templates/tasksets.html:67
+msgid "You don't own any taskset."
+msgstr "Vous ne possédez aucun jeu d'exercices."
+
+#: inginious/frontend/templates/tasksets.html:74
+msgid "Course"
+msgstr "Cours"
+
+#: inginious/frontend/templates/tasksets.html:78
+msgid "Create new taskset"
+msgstr "Créer un nouvel exercice"
+
+#: inginious/frontend/templates/tasksets.html:88
+msgid "Instantiate a course"
+msgstr "Instancier un cours"
+
+#: inginious/frontend/templates/tasksets.html:93
+#: inginious/frontend/templates/tasksets.html:96
+msgid "New courseid"
+msgstr "Nouvel identifiant de cours"
+
#: inginious/frontend/templates/unregister_modal.html:9
msgid "Unregister from {}"
msgstr "Se désinscrire de {}"
#: inginious/frontend/templates/unregister_modal.html:15
-msgid ""
-"This will keep your submissions saved but will remove you from your group."
+msgid "This will keep your submissions saved but will remove you from your group."
msgstr "Ceci conservera vos soumissions mais vous enlèvera de votre groupe."
#: inginious/frontend/templates/course_admin/student_list_table.html:77
@@ -2109,7 +2256,7 @@ msgstr "membre"
#: inginious/frontend/templates/admin/admin_users.html:33
#: inginious/frontend/templates/course_admin/student_list_table.html:16
-#: inginious/frontend/templates/course_admin/submissions.html:55
+#: inginious/frontend/templates/course_admin/submissions.html:56
msgid "username"
msgstr "nom d'utilisateur"
@@ -2119,11 +2266,11 @@ msgid "email address"
msgstr "adresse email"
#: inginious/frontend/templates/admin/admin_users.html:38
-#: inginious/frontend/templates/course_admin/stats.html:61
-#: inginious/frontend/templates/course_admin/student_info.html:40
-#: inginious/frontend/templates/course_admin/student_list.html:159
+#: inginious/frontend/templates/course_admin/stats.html:62
+#: inginious/frontend/templates/course_admin/student_info.html:41
+#: inginious/frontend/templates/course_admin/student_list.html:160
#: inginious/frontend/templates/course_admin/student_list_table.html:26
-#: inginious/frontend/templates/course_admin/submissions.html:78
+#: inginious/frontend/templates/course_admin/submissions.html:79
msgid "Download CSV"
msgstr "Télécharger sous format CSV"
@@ -2132,10 +2279,11 @@ msgid "Bindings"
msgstr "Liaisons"
#: inginious/frontend/templates/admin/admin_users.html:102
-#: inginious/frontend/templates/course_admin/edit_tabs/files.html:19
-#: inginious/frontend/templates/course_admin/edit_tabs/files.html:53
-#: inginious/frontend/templates/course_admin/subproblems/multiple_choice_templates.html:31
-#: inginious/frontend/templates/course_admin/task_dispensers/section_menu.html:21
+#: inginious/frontend/templates/task_dispensers_admin/section_menu.html:21
+#: inginious/frontend/templates/task_dispensers_admin/util.html:131
+#: inginious/frontend/templates/taskset_admin/edit_tabs/files.html:19
+#: inginious/frontend/templates/taskset_admin/edit_tabs/files.html:53
+#: inginious/frontend/templates/taskset_admin/subproblems/multiple_choice_templates.html:31
msgid "Delete"
msgstr "Supprimer"
@@ -2161,8 +2309,8 @@ msgid "Add user"
msgstr "Ajouter un utilisateur"
#: inginious/frontend/templates/admin/admin_users.html:168
-#: inginious/frontend/templates/course_admin/edit_tabs/subproblems.html:22
-#: inginious/frontend/templates/course_admin/task_dispensers/util.html:103
+#: inginious/frontend/templates/task_dispensers_admin/util.html:98
+#: inginious/frontend/templates/taskset_admin/edit_tabs/subproblems.html:22
msgid "Add"
msgstr "Ajouter"
@@ -2179,687 +2327,673 @@ msgstr "Supprimer l'accès"
msgid "Identifier :"
msgstr "Identifiant :"
-#: inginious/frontend/templates/course_admin/audience_edit.html:18
-#: inginious/frontend/templates/course_admin/student_list.html:43
-#: inginious/frontend/templates/course_admin/student_list.html:78
-#: inginious/frontend/templates/course_admin/student_list.html:110
+#: inginious/frontend/templates/course_admin/audience_edit.html:19
+#: inginious/frontend/templates/course_admin/student_list.html:44
+#: inginious/frontend/templates/course_admin/student_list.html:79
+#: inginious/frontend/templates/course_admin/student_list.html:111
msgid "Audiences"
msgstr "Publics"
-#: inginious/frontend/templates/course_admin/audience_edit.html:20
-#: inginious/frontend/templates/course_admin/audience_edit.html:26
+#: inginious/frontend/templates/course_admin/audience_edit.html:21
+#: inginious/frontend/templates/course_admin/audience_edit.html:27
msgid "Edit audience {}"
msgstr "Éditer le public {}"
-#: inginious/frontend/templates/course_admin/audience_edit.html:44
-#: inginious/frontend/templates/course_admin/audience_edit.html:63
-#: inginious/frontend/templates/course_admin/student_list.html:67
-#: inginious/frontend/templates/course_admin/student_list.html:257
-#: inginious/frontend/templates/course_admin/student_list.html:276
+#: inginious/frontend/templates/course_admin/audience_edit.html:45
+#: inginious/frontend/templates/course_admin/audience_edit.html:64
+#: inginious/frontend/templates/course_admin/student_list.html:68
+#: inginious/frontend/templates/course_admin/student_list.html:258
+#: inginious/frontend/templates/course_admin/student_list.html:277
msgid "Add student"
msgstr "Ajouter un étudiant"
-#: inginious/frontend/templates/course_admin/audience_edit.html:51
+#: inginious/frontend/templates/course_admin/audience_edit.html:52
msgid "Choose student :"
msgstr "Sélectionner l'étudiant :"
-#: inginious/frontend/templates/course_admin/audience_edit.html:55
-#: inginious/frontend/templates/course_admin/settings.html:70
-#: inginious/frontend/templates/course_admin/settings.html:76
-#: inginious/frontend/templates/course_admin/student_list.html:65
+#: inginious/frontend/templates/course_admin/audience_edit.html:56
+#: inginious/frontend/templates/course_admin/settings.html:71
+#: inginious/frontend/templates/course_admin/student_list.html:66
+#: inginious/frontend/templates/taskset_admin/settings.html:64
msgid "Enter something here to search for a user"
msgstr "Commencez à taper pour rechercher un utilisateur"
-#: inginious/frontend/templates/course_admin/audience_edit.html:74
-#: inginious/frontend/templates/course_admin/task_edit.html:46
+#: inginious/frontend/templates/course_admin/audience_edit.html:75
+#: inginious/frontend/templates/taskset_admin/task_edit.html:44
msgid "Basic settings"
msgstr "Paramètres de base"
-#: inginious/frontend/templates/course_admin/audience_edit.html:78
-#: inginious/frontend/templates/course_admin/audience_edit.html:82
+#: inginious/frontend/templates/course_admin/audience_edit.html:79
+#: inginious/frontend/templates/course_admin/audience_edit.html:83
msgid "Audience description"
msgstr "Description du public"
-#: inginious/frontend/templates/course_admin/audience_edit.html:85
+#: inginious/frontend/templates/course_admin/audience_edit.html:86
msgid "Delete audience"
msgstr "Supprimer le public"
-#: inginious/frontend/templates/course_admin/audience_edit.html:92
+#: inginious/frontend/templates/course_admin/audience_edit.html:93
msgid "Tutor list"
msgstr "Liste des tuteurs"
-#: inginious/frontend/templates/course_admin/audience_edit.html:124
+#: inginious/frontend/templates/course_admin/audience_edit.html:125
msgid "Add tutor"
msgstr "Ajouter un tuteur"
-#: inginious/frontend/templates/course_admin/audience_edit.html:137
+#: inginious/frontend/templates/course_admin/audience_edit.html:138
msgid "Student list"
msgstr "Liste des étudiants"
-#: inginious/frontend/templates/course_admin/audience_edit.html:153
+#: inginious/frontend/templates/course_admin/audience_edit.html:154
msgid "Remove student"
msgstr "Supprimer l'étudiant"
-#: inginious/frontend/templates/course_admin/audience_edit.html:164
-#: inginious/frontend/templates/course_admin/student_list.html:435
+#: inginious/frontend/templates/course_admin/audience_edit.html:165
+#: inginious/frontend/templates/course_admin/student_list.html:436
msgid "Update"
msgstr "Mettre à jour"
-#: inginious/frontend/templates/course_admin/danger_zone.html:41
+#: inginious/frontend/templates/course_admin/danger_zone.html:42
msgid "Archive data"
msgstr "Archiver les données"
-#: inginious/frontend/templates/course_admin/danger_zone.html:44
-#: inginious/frontend/templates/course_admin/danger_zone.html:138
-#: inginious/frontend/templates/course_admin/danger_zone.html:151
-#: inginious/frontend/templates/course_admin/danger_zone.html:159
-#: inginious/frontend/templates/course_admin/danger_zone.html:167
+#: inginious/frontend/templates/course_admin/danger_zone.html:45
+#: inginious/frontend/templates/course_admin/danger_zone.html:139
+#: inginious/frontend/templates/course_admin/danger_zone.html:152
+#: inginious/frontend/templates/course_admin/danger_zone.html:160
+#: inginious/frontend/templates/course_admin/danger_zone.html:168
msgid "Delete course"
msgstr "Supprimer le cours"
-#: inginious/frontend/templates/course_admin/danger_zone.html:52
-#: inginious/frontend/templates/course_admin/danger_zone.html:63
+#: inginious/frontend/templates/course_admin/danger_zone.html:53
+#: inginious/frontend/templates/course_admin/danger_zone.html:64
msgid "Archive course data"
msgstr "Archiver les données du cours"
-#: inginious/frontend/templates/course_admin/danger_zone.html:55
+#: inginious/frontend/templates/course_admin/danger_zone.html:56
msgid ""
"
This will reset and backup all course data (submissions, audiences, "
"groups, user statistics) from the database.
To confirm your will, "
"please type the course id below :
"
msgstr ""
"
Ceci remettra à zéro et sauvegardera toutes les données du cours "
-"(soumissions, publics, groupes, statistiques) se trouvant dans la base de "
-"données.
Pour confirmer, veuillez entrer l'identifiant du cours ci-"
-"dessus :
"
+"(soumissions, publics, groupes, statistiques) se trouvant dans la base de"
+" données.
Pour confirmer, veuillez entrer l'identifiant du cours "
+"ci-dessus :
"
-#: inginious/frontend/templates/course_admin/danger_zone.html:75
+#: inginious/frontend/templates/course_admin/danger_zone.html:76
msgid "Restore backup from {}"
msgstr "Restaurer la sauvegarde du {}"
-#: inginious/frontend/templates/course_admin/danger_zone.html:79
+#: inginious/frontend/templates/course_admin/danger_zone.html:80
msgid "
This will restore your course data to {}. Are you sure ?
"
msgstr ""
-"
Ceci va restaurer les données du cours à la date du {}. Etes vous sûr ?"
-"p>"
+"
Ceci va restaurer les données du cours à la date du {}. Etes vous sûr "
+"?
This will permanently remove the taskset and all the tasks "
+"files from INGInious. Are you really sure ?
"
+msgstr ""
+"
Ceci va supprimer définitivement le jeu d'exercices et tous les fichiers "
+"d'exercices d'INGInious. Êtes-vous vraiment certain ?
"
+
+#: inginious/frontend/templates/taskset_admin/menu.html:15
+msgid "How to create a task?"
+msgstr "Comment créer un exercice ?"
+
+#: inginious/frontend/templates/taskset_admin/settings.html:19
+msgid "Taskset settings"
+msgstr "Paramètres du jeu d'exercices"
+
+#: inginious/frontend/templates/taskset_admin/settings.html:26
+msgid "Edit taskset {}"
+msgstr "Éditer le jeu d'exercices {}"
+
+#: inginious/frontend/templates/taskset_admin/settings.html:68
+msgid "Short description"
+msgstr "Description courte"
+
+#: inginious/frontend/templates/taskset_admin/settings.html:74
+msgid "Instantiable by"
+msgstr "Instanciable par"
+
+#: inginious/frontend/templates/taskset_admin/settings.html:77
+msgid "Taskset owners only"
+msgstr "Propriétaires du jeu d'exercices uniquement"
+
+#: inginious/frontend/templates/taskset_admin/settings.html:80
+msgid "Everyone"
+msgstr "Tout le monde"
+
+#: inginious/frontend/templates/taskset_admin/settings.html:93
+msgid "WebDAV access"
+msgstr "Accès WebDAV"
+
+#: inginious/frontend/templates/taskset_admin/settings.html:98
+msgid "Use this URL to access your taskset folder using WebDAV:"
+msgstr "Utilisez cette URL pour accéder au dossier du jeu d'exercices via WebDAV:"
+
+#: inginious/frontend/templates/taskset_admin/settings.html:101
+msgid "URL"
+msgstr "URL"
+
+#: inginious/frontend/templates/taskset_admin/settings.html:113
+msgid "Task list"
+msgstr "Liste des exercices"
+
+#: inginious/frontend/templates/taskset_admin/settings.html:114
+msgid "Add new task"
+msgstr "Ajouter des exercices"
+
+#: inginious/frontend/templates/taskset_admin/settings.html:144
+msgid "Delete task {}"
+msgstr "Supprimer l'exercice {}"
+
+#: inginious/frontend/templates/taskset_admin/settings.html:148
+msgid ""
+"This will permanently remove the task {} and its files from "
+"INGInious, making it unavailable from courses."
+msgstr ""
+"Ceci va définitivement supprimer l'exercice {} et ses fichiers"
+" d'INGInious, le rendant indisponible depuis les cours."
+
+#: inginious/frontend/templates/taskset_admin/settings.html:165
+msgid "Create new task"
+msgstr "Créer un nouvel exercice"
+
+#: inginious/frontend/templates/taskset_admin/settings.html:170
+#: inginious/frontend/templates/taskset_admin/settings.html:172
+msgid "New taskid"
+msgstr "Nouvel identifiant d'exercice"
+
+#: inginious/frontend/templates/taskset_admin/settings.html:178
+msgid "Add task"
+msgstr "Ajouter un exercice"
+
+#: inginious/frontend/templates/taskset_admin/task_edit.html:6
msgid "Edit {}"
msgstr "Éditer {}"
-#: inginious/frontend/templates/course_admin/task_edit.html:21
-#: inginious/frontend/templates/course_admin/task_edit.html:28
+#: inginious/frontend/templates/taskset_admin/task_edit.html:20
+#: inginious/frontend/templates/taskset_admin/task_edit.html:27
msgid "Edit task \"{}\""
msgstr "Éditer l'exercice \"{}\""
-#: inginious/frontend/templates/course_admin/task_dispensers/task_buttons.html:10
-#: inginious/frontend/templates/course_admin/task_edit.html:32
-msgid "View task"
-msgstr "Voir l'exercice"
-
-#: inginious/frontend/templates/course_admin/task_edit.html:49
+#: inginious/frontend/templates/taskset_admin/task_edit.html:47
msgid "Environment"
msgstr "Environnement"
-#: inginious/frontend/templates/course_admin/task_edit.html:52
+#: inginious/frontend/templates/taskset_admin/task_edit.html:50
msgid "Subproblems"
msgstr "Sous-problèmes"
-#: inginious/frontend/templates/course_admin/task_edit.html:55
+#: inginious/frontend/templates/taskset_admin/task_edit.html:53
msgid "Task files"
msgstr "Fichiers de l'exercice"
-#: inginious/frontend/templates/course_admin/task_edit.html:79
+#: inginious/frontend/templates/taskset_admin/task_edit.html:77
msgid "File list"
msgstr "Liste des fichiers"
-#: inginious/frontend/templates/course_admin/task_edit.html:109
+#: inginious/frontend/templates/taskset_admin/task_edit.html:107
msgid "Problem id:"
msgstr "Identifiant du problème :"
-#: inginious/frontend/templates/course_admin/task_edit.html:126
-#, fuzzy
+#: inginious/frontend/templates/taskset_admin/task_edit.html:124
msgid "A title for this question"
-msgstr "Une section n'a pas de titre"
+msgstr "Un titre pour la question"
-#: inginious/frontend/templates/course_admin/task_edit.html:144
+#: inginious/frontend/templates/taskset_admin/task_edit.html:142
msgid "Are you sure that you want to delete this subproblem?"
msgstr "Êtes-vous sûr de vouloir supprimer ce sous-problème ?"
-#: inginious/frontend/templates/course_admin/task_list.html:29
-#: inginious/frontend/templates/course_admin/task_list.html:41
-msgid "Switch task dispenser"
-msgstr "Changer de distributeur"
-
-#: inginious/frontend/templates/course_admin/task_list.html:47
+#: inginious/frontend/templates/taskset_admin/template.html:48
msgid ""
-"This will wipe your current course structure. Tasks file won't be deleted "
-"but you'll have to import them again."
+"This will wipe your current taskset structure. Tasks file won't be "
+"deleted but you'll have to import them again."
msgstr ""
-"Ceci va effacer l'actuelle structure de cours. Les fichiers des exercices ne "
-"seront pas supprimés mais vous devrez les importer à nouveau."
-
-#: inginious/frontend/templates/course_admin/task_list.html:48
-msgid "New task dispenser:"
-msgstr "Nouveau distributeur d'exercice:"
-
-#: inginious/frontend/templates/course_admin/task_list.html:69
-msgid "WebDAV access"
-msgstr "Accès WebDAV"
+"Ceci va effacer l'actuelle structure du jeu d'exercices. Les fichiers des exercices"
+" ne seront pas supprimés mais vous devrez les importer à nouveau."
-#: inginious/frontend/templates/course_admin/task_list.html:74
-msgid "Use this URL to access your course folder using WebDAV:"
-msgstr "Utilisez cette URL pour accéder au dossier du cours via WebDAV:"
-
-#: inginious/frontend/templates/course_admin/task_list.html:77
-msgid "URL"
-msgstr "URL"
+#: inginious/frontend/templates/taskset_admin/template.html:85
+msgid ""
+"This taskset currently includes legacy tasks with importable data to the "
+"task dispenser settings. The taskset owner can clean the task files."
+msgstr ""
+"Ce jeu d'exercices contient des anciens exercices avec des données importables "
+"vers les paramètres du distributeur d'exercices. Le propriétaire du jeu d'exercices"
+" peut nettoyer les fichiers."
-#: inginious/frontend/templates/course_admin/task_list.html:97
-#, fuzzy
-msgid "Changes saved."
-msgstr "Modifications sauvegardées."
+#: inginious/frontend/templates/taskset_admin/template.html:88
+msgid "Clean task files"
+msgstr "Nettoyer les fichiers d'exercices"
-#: inginious/frontend/templates/course_admin/edit_tabs/basic.html:13
+#: inginious/frontend/templates/taskset_admin/edit_tabs/basic.html:13
msgid "Filetype"
msgstr "Type de fichier"
-#: inginious/frontend/templates/course_admin/edit_tabs/basic.html:30
-#: inginious/frontend/templates/course_admin/subproblems/code.html:5
-#: inginious/frontend/templates/course_admin/subproblems/file.html:5
-#: inginious/frontend/templates/course_admin/subproblems/match.html:5
-#: inginious/frontend/templates/course_admin/subproblems/multiple_choice.html:5
+#: inginious/frontend/templates/taskset_admin/edit_tabs/basic.html:30
+#: inginious/frontend/templates/taskset_admin/subproblems/code.html:5
+#: inginious/frontend/templates/taskset_admin/subproblems/file.html:5
+#: inginious/frontend/templates/taskset_admin/subproblems/match.html:5
+#: inginious/frontend/templates/taskset_admin/subproblems/multiple_choice.html:5
msgid "Context"
msgstr "Énoncé"
-#: inginious/frontend/templates/course_admin/edit_tabs/basic.html:36
+#: inginious/frontend/templates/taskset_admin/edit_tabs/basic.html:36
msgid "Author"
msgstr "Auteur"
-#: inginious/frontend/templates/course_admin/edit_tabs/basic.html:43
-#, fuzzy
+#: inginious/frontend/templates/taskset_admin/edit_tabs/basic.html:43
msgid "Your name"
-msgstr "nom d'utilisateur"
+msgstr "Votre nom"
-#: inginious/frontend/templates/course_admin/edit_tabs/basic.html:47
+#: inginious/frontend/templates/taskset_admin/edit_tabs/basic.html:47
msgid "Contact URL"
msgstr "URL de contact"
-#: inginious/frontend/templates/course_admin/edit_tabs/basic.html:53
+#: inginious/frontend/templates/taskset_admin/edit_tabs/basic.html:53
msgid "Random inputs"
msgstr "Paramètres aléatoires"
-#: inginious/frontend/templates/course_admin/edit_tabs/basic.html:60
+#: inginious/frontend/templates/taskset_admin/edit_tabs/basic.html:60
msgid "Regenerate random inputs for each reloading of the task page"
msgstr "Regénère les entrées aléatoires à chaque chargement de la page"
-#: inginious/frontend/templates/course_admin/edit_tabs/basic.html:60
+#: inginious/frontend/templates/taskset_admin/edit_tabs/basic.html:60
msgid "Regenerate input random"
msgstr "Regénérer l'entrée aléatoire"
-#: inginious/frontend/templates/course_admin/edit_tabs/env_generic_docker_oci.html:5
+#: inginious/frontend/templates/taskset_admin/edit_tabs/env_generic_docker_oci.html:5
msgid "Timeout limit (in seconds)"
msgstr "Temps d'expiration (en secondes, temps CPU)"
-#: inginious/frontend/templates/course_admin/edit_tabs/env_generic_docker_oci.html:11
+#: inginious/frontend/templates/taskset_admin/edit_tabs/env_generic_docker_oci.html:11
msgid "Hard timeout limit (in seconds) Default to 3*timeout"
msgstr ""
-"Temps d'éxpiration (en seconde, temps total) 3 fois le temps CPU par "
-"défaut"
+"Temps d'éxpiration (en seconde, temps total) 3 fois le temps CPU "
+"par défaut"
-#: inginious/frontend/templates/course_admin/edit_tabs/env_generic_docker_oci.html:18
+#: inginious/frontend/templates/taskset_admin/edit_tabs/env_generic_docker_oci.html:18
msgid "Memory limit (in megabytes)"
msgstr "Limite de mémoire (en mégaoctets)"
-#: inginious/frontend/templates/course_admin/edit_tabs/env_generic_docker_oci.html:24
+#: inginious/frontend/templates/taskset_admin/edit_tabs/env_generic_docker_oci.html:24
msgid "Allow internet access inside the grading container?"
msgstr "Autoriser l'accès à Internet lors de l'exécution de la correction ?"
-#: inginious/frontend/templates/course_admin/edit_tabs/env_generic_docker_oci.html:24
+#: inginious/frontend/templates/taskset_admin/edit_tabs/env_generic_docker_oci.html:24
msgid "It also adds and configures local interfaces with IPv4 and IPv6."
msgstr ""
-#: inginious/frontend/templates/course_admin/edit_tabs/env_generic_docker_oci.html:34
-msgid "Allow ssh?"
-msgstr ""
-
-#: inginious/frontend/templates/course_admin/edit_tabs/env_generic_docker_oci.html:45
+#: inginious/frontend/templates/taskset_admin/edit_tabs/env_generic_docker_oci.html:35
msgid ""
"Custom command to be run in container (instead of running the run "
"script)"
msgstr ""
-"Commande devant être lancée dans le conteneur (à la place du script "
-"run)"
+"Commande devant être lancée dans le conteneur (à la place du "
+"script run)"
-#: inginious/frontend/templates/course_admin/edit_tabs/env_generic_docker_oci.html:48
-#, fuzzy
+#: inginious/frontend/templates/taskset_admin/edit_tabs/env_generic_docker_oci.html:38
msgid "Optional"
msgstr "Optionnel ?"
-#: inginious/frontend/templates/course_admin/edit_tabs/env_generic_docker_oci.html:53
+#: inginious/frontend/templates/taskset_admin/edit_tabs/env_generic_docker_oci.html:43
msgid "Are the task's responses written in HTML instead of restructuredText?"
msgstr "Évaluation écrite en HTML ou restructuredText ?"
-#: inginious/frontend/templates/course_admin/edit_tabs/environment.html:5
+#: inginious/frontend/templates/taskset_admin/edit_tabs/environment.html:5
msgid "Grading environment type"
msgstr "Type d'environnement de correction"
-#: inginious/frontend/templates/course_admin/edit_tabs/environment.html:20
+#: inginious/frontend/templates/taskset_admin/edit_tabs/environment.html:20
msgid "Grading environment"
msgstr "Environnement de correction"
-#: inginious/frontend/templates/course_admin/edit_tabs/file_modals.html:9
-#: inginious/frontend/templates/course_admin/edit_tabs/files.html:60
+#: inginious/frontend/templates/taskset_admin/edit_tabs/file_modals.html:9
+#: inginious/frontend/templates/taskset_admin/edit_tabs/files.html:60
msgid "Upload a file"
msgstr "Télécharger un fichier"
-#: inginious/frontend/templates/course_admin/edit_tabs/file_modals.html:16
+#: inginious/frontend/templates/taskset_admin/edit_tabs/file_modals.html:16
msgid "File:"
msgstr "Fichier :"
-#: inginious/frontend/templates/course_admin/edit_tabs/file_modals.html:20
+#: inginious/frontend/templates/taskset_admin/edit_tabs/file_modals.html:20
msgid "File path:"
msgstr "Chemin du fichier :"
-#: inginious/frontend/templates/course_admin/edit_tabs/file_modals.html:27
+#: inginious/frontend/templates/taskset_admin/edit_tabs/file_modals.html:27
msgid "Upload"
msgstr "Télécharger"
-#: inginious/frontend/templates/course_admin/edit_tabs/files.html:15
+#: inginious/frontend/templates/taskset_admin/edit_tabs/files.html:15
msgid "Path"
msgstr "Chemin"
-#: inginious/frontend/templates/course_admin/edit_tabs/files.html:17
-#: inginious/frontend/templates/course_admin/edit_tabs/files.html:44
+#: inginious/frontend/templates/taskset_admin/edit_tabs/files.html:17
+#: inginious/frontend/templates/taskset_admin/edit_tabs/files.html:44
msgid "Edit"
msgstr "Éditer"
-#: inginious/frontend/templates/course_admin/edit_tabs/files.html:18
+#: inginious/frontend/templates/taskset_admin/edit_tabs/files.html:18
msgid "Move"
msgstr "Déplacer"
-#: inginious/frontend/templates/course_admin/edit_tabs/files.html:49
-#, fuzzy
+#: inginious/frontend/templates/taskset_admin/edit_tabs/files.html:49
msgid "Move/Rename"
-msgstr "\"Déplacer/Renommer\""
+msgstr "Déplacer/Renommer"
-#: inginious/frontend/templates/course_admin/edit_tabs/files.html:59
+#: inginious/frontend/templates/taskset_admin/edit_tabs/files.html:59
msgid "Create a new file"
msgstr "Créer un nouveau fichier"
-#: inginious/frontend/templates/course_admin/edit_tabs/subproblems.html:8
+#: inginious/frontend/templates/taskset_admin/edit_tabs/subproblems.html:8
msgid "New problem id"
msgstr "Nouvel identifiant de problème"
-#: inginious/frontend/templates/course_admin/edit_tabs/subproblems.html:13
+#: inginious/frontend/templates/taskset_admin/edit_tabs/subproblems.html:13
msgid "Problem type"
msgstr "Type de problème"
-#: inginious/frontend/templates/course_admin/subproblems/code.html:12
+#: inginious/frontend/templates/taskset_admin/subproblems/code.html:12
msgid "Language"
msgstr "Langage"
-#: inginious/frontend/templates/course_admin/subproblems/code.html:14
+#: inginious/frontend/templates/taskset_admin/subproblems/code.html:14
msgid "Language used in this question (c/cpp/java/python/oz/...)"
msgstr ""
-"Langage de programmation utilisé pour cette question (c/cpp/java/python/"
-"oz/...)"
+"Langage de programmation utilisé pour cette question "
+"(c/cpp/java/python/oz/...)"
-#: inginious/frontend/templates/course_admin/subproblems/code.html:23
+#: inginious/frontend/templates/taskset_admin/subproblems/code.html:23
msgid "Multiline code"
msgstr "Code libre"
-#: inginious/frontend/templates/course_admin/subproblems/code.html:25
+#: inginious/frontend/templates/taskset_admin/subproblems/code.html:25
msgid "Single-line code"
msgstr "Ligne de code"
-#: inginious/frontend/templates/course_admin/subproblems/code.html:31
+#: inginious/frontend/templates/taskset_admin/subproblems/code.html:31
msgid "Optional?"
msgstr "Optionnel ?"
-#: inginious/frontend/templates/course_admin/subproblems/code.html:41
+#: inginious/frontend/templates/taskset_admin/subproblems/code.html:41
msgid "Default value"
msgstr "Valeur par défaut"
-#: inginious/frontend/templates/course_admin/subproblems/file.html:11
-#, fuzzy
+#: inginious/frontend/templates/taskset_admin/subproblems/file.html:11
msgid "Allowed file extensions (including the dot, separated by commas)"
msgstr ""
"Extensions de fichier autorisées (incluant le point, séparées par des "
"espaces)"
-#: inginious/frontend/templates/course_admin/subproblems/file.html:17
+#: inginious/frontend/templates/taskset_admin/subproblems/file.html:17
msgid "Maximum file size (in bytes)"
msgstr "Taille de fichier max. (en octets)"
-#: inginious/frontend/templates/course_admin/subproblems/file.html:26
+#: inginious/frontend/templates/taskset_admin/subproblems/file.html:26
msgid "File upload"
msgstr "Téléchargement d'un fichier"
-#: inginious/frontend/templates/course_admin/subproblems/match.html:11
+#: inginious/frontend/templates/taskset_admin/subproblems/match.html:11
msgid "Answer"
msgstr "Réponse"
-#: inginious/frontend/templates/course_admin/subproblems/match.html:13
-#, fuzzy
+#: inginious/frontend/templates/taskset_admin/subproblems/match.html:13
msgid "Answer of this question"
-msgstr "Veuillez répondre à toutes les questions."
+msgstr "Réponse à cette question"
-#: inginious/frontend/templates/course_admin/subproblems/match.html:17
-#: inginious/frontend/templates/course_admin/subproblems/multiple_choice.html:27
+#: inginious/frontend/templates/taskset_admin/subproblems/match.html:17
+#: inginious/frontend/templates/taskset_admin/subproblems/multiple_choice.html:27
msgid "Count error only at top of the page?"
msgstr "Uniquement compter les erreurs de tout l'exercice ?"
-#: inginious/frontend/templates/course_admin/subproblems/multiple_choice.html:11
+#: inginious/frontend/templates/taskset_admin/subproblems/multiple_choice.html:11
msgid "Can select multiple answers"
msgstr "Réponses multiples"
-#: inginious/frontend/templates/course_admin/subproblems/multiple_choice.html:19
+#: inginious/frontend/templates/taskset_admin/subproblems/multiple_choice.html:19
msgid "Unshuffle answers"
-msgstr ""
+msgstr "Ne pas mélanger les réponses"
-#: inginious/frontend/templates/course_admin/subproblems/multiple_choice.html:35
+#: inginious/frontend/templates/taskset_admin/subproblems/multiple_choice.html:35
msgid "Number of choices to display (0 = all choices)"
msgstr "Nombre de choix à afficher (0 = tous)"
-#: inginious/frontend/templates/course_admin/subproblems/multiple_choice.html:41
+#: inginious/frontend/templates/taskset_admin/subproblems/multiple_choice.html:41
msgid "Success message"
msgstr "Message lors de réussite"
-#: inginious/frontend/templates/course_admin/subproblems/multiple_choice.html:45
-#, fuzzy
+#: inginious/frontend/templates/taskset_admin/subproblems/multiple_choice.html:45
msgid "An optional success message"
-msgstr "Message lors de réussite"
+msgstr "Message optionnel en cas de réussite"
-#: inginious/frontend/templates/course_admin/subproblems/multiple_choice.html:49
+#: inginious/frontend/templates/taskset_admin/subproblems/multiple_choice.html:49
msgid "Error message"
msgstr "Message lors d'erreur"
-#: inginious/frontend/templates/course_admin/subproblems/multiple_choice.html:53
-#, fuzzy
+#: inginious/frontend/templates/taskset_admin/subproblems/multiple_choice.html:53
msgid "An optional error message"
-msgstr "Message lors d'erreur"
+msgstr "Message optionnel en cas d'erreur"
-#: inginious/frontend/templates/course_admin/subproblems/multiple_choice.html:58
+#: inginious/frontend/templates/taskset_admin/subproblems/multiple_choice.html:58
msgid "Choices"
msgstr "Choix"
-#: inginious/frontend/templates/course_admin/subproblems/multiple_choice.html:63
+#: inginious/frontend/templates/taskset_admin/subproblems/multiple_choice.html:63
msgid "Add new choice"
msgstr "Ajouter un nouveau choix"
-#: inginious/frontend/templates/course_admin/subproblems/multiple_choice_templates.html:8
+#: inginious/frontend/templates/taskset_admin/subproblems/multiple_choice_templates.html:8
msgid "Content"
msgstr "Réponse possible"
-#: inginious/frontend/templates/course_admin/subproblems/multiple_choice_templates.html:15
+#: inginious/frontend/templates/taskset_admin/subproblems/multiple_choice_templates.html:15
msgid "Feedback message (displayed when selected)"
msgstr "Message d'évaluation affiché si sélectionné"
-#: inginious/frontend/templates/course_admin/subproblems/multiple_choice_templates.html:19
+#: inginious/frontend/templates/taskset_admin/subproblems/multiple_choice_templates.html:19
msgid "An optional feedback message"
msgstr "Un message de retour optionnel"
-#: inginious/frontend/templates/course_admin/subproblems/multiple_choice_templates.html:26
+#: inginious/frontend/templates/taskset_admin/subproblems/multiple_choice_templates.html:26
msgid "Valid ?"
msgstr "Correct ?"
-#: inginious/frontend/templates/course_admin/task_dispensers/combinatory_test.html:7
-msgid "Combinatory tests"
-msgstr "Tests combinatoires"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/combinatory_test.html:11
-msgid ""
-"Only the specified amount of tasks to be displayed will be shown to the "
-"user. There is no state: changing the amount of tasks per section, section "
-"position or section title will affect the effective task set a user will be "
-"displayed."
-msgstr ""
-"Seul le nombre spécifié d'exercices sera affiché à l'utilisateur. Ceci est "
-"calculé sans état: modifier la quantité d'exercice par section, la position "
-"de la section, ou le titre de la section affectera le jeu d'exercices "
-"effectivement affiché à l'utilisateur."
-
-#: inginious/frontend/templates/course_admin/task_dispensers/combinatory_test.html:13
-msgid ""
-"As an administrator, the only way to access all the tasks is through this "
-"administration page."
-msgstr ""
-"En tant qu'administrateur, la seule manière d'accéder à tous les exercices "
-"est via cette page d'administration."
-
-#: inginious/frontend/templates/course_admin/task_dispensers/combinatory_test.html:27
-#: inginious/frontend/templates/course_admin/task_dispensers/toc.html:17
-msgid "Add section"
-msgstr "Ajouter une section"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/combinatory_test.html:33
-#: inginious/frontend/templates/course_admin/task_dispensers/toc.html:23
-#, fuzzy
-msgid "New section"
-msgstr "Ajouter une section"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/empty_section.html:25
-msgid "Drag tasks or section here."
-msgstr "Glisser les exercices ou la section ici."
-
-#: inginious/frontend/templates/course_admin/task_dispensers/section_menu.html:10
-#: inginious/frontend/templates/course_admin/task_dispensers/util.html:78
-msgid "Add tasks"
-msgstr "Ajouter des exercices"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/section_menu.html:14
-msgid "Add subsection"
-msgstr "Ajouter une sous-section"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/section_menu.html:18
-msgid "Rename"
-msgstr "Renommer"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/task_buttons.html:6
-#, fuzzy
-msgid "Task parameters"
-msgstr "nom de l'exercice"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/task_buttons.html:23
-#: inginious/frontend/templates/course_admin/task_dispensers/util_delete_modal.html:11
-#: inginious/frontend/templates/course_admin/task_dispensers/util_delete_modal.html:33
-msgid "Delete task"
-msgstr "Supprimer l'exercice"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/task_list.html:35
-#, fuzzy
-msgid "No valid task with id:"
-msgstr "Identifiant d'étiquette invalide: {}"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/task_list.html:39
-#: inginious/frontend/templates/course_admin/task_dispensers/util.html:123
-#, fuzzy
-msgid "Delete invalid task"
-msgstr "\"Supprimer l'exercice invalide \""
-
-#: inginious/frontend/templates/course_admin/task_dispensers/util.html:82
-#: inginious/frontend/templates/course_admin/task_dispensers/util.html:85
-msgid "Create new task"
-msgstr "Créer un nouvel exercice"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/util.html:84
-#, fuzzy
-msgid "New task id"
-msgstr "\"Nouvel identifiant d'exercice\""
-
-#: inginious/frontend/templates/course_admin/task_dispensers/util.html:87
-msgid "Import from course filesystem"
-msgstr "Importer des fichiers du cours"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/util.html:88
-msgid "Search..."
-msgstr "Chercher..."
-
-#: inginious/frontend/templates/course_admin/task_dispensers/util.html:90
-msgid "No unassigned tasks in the filesystem of this course"
-msgstr "Pas d'exercice non-assigné dans les fichiers du cours"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/util.html:120
-#, fuzzy
-msgid "New task with id: "
-msgstr "\"Nouvel identifiant d'exercice\""
-
-#: inginious/frontend/templates/course_admin/task_dispensers/util_delete_modal.html:9
-#: inginious/frontend/templates/course_admin/task_dispensers/util_delete_modal.html:30
-msgid "Delete section"
-msgstr "Supprimer la section"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/util_delete_modal.html:17
-msgid ""
-"Once saved, this will permanently delete this section, all its "
-"subsections and remove all the tasks and their files from INGInious."
-msgstr ""
-"Une fois les modifications sauvegardées, cette section, ainsi que toutes ses "
-"sous-sections et exercices, seront définitivement supprimée "
-"d'INGInious."
-
-#: inginious/frontend/templates/course_admin/task_dispensers/util_delete_modal.html:19
-msgid ""
-"Once saved, this will permanently remove the task and its files from "
-"INGInious."
-msgstr ""
-"Une fois les modifications sauvegardées, cet exercice, ainsi que les "
-"fichiers associés, seront définitivement supprimé d'INGInious"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/util_delete_modal.html:23
-msgid "Wipe all submissions"
-msgstr "Effacer toutes les soumissions"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/util_delete_modal.html:29
-#: inginious/frontend/templates/course_admin/task_dispensers/util_delete_modal.html:32
-msgid "Keep files"
-msgstr "Conserver les fichiers"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/util_task_edit_modal.html:8
-#, fuzzy
-msgid "Edit task {}"
-msgstr "Éditer l'exercice \"{}\""
-
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/accessibility.html:11
-msgid "Never"
-msgstr "Jamais"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/accessibility.html:18
-msgid "Always"
-msgstr "Toujours"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/accessibility.html:26
-msgid "Custom, from: {} to: {}"
-msgstr "Personnalisé, de: {} jusqu'à: {}"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/accessibility.html:52
-msgid "Students can still submit after this date"
-msgstr "Les étudiants peuvent toujours soumettre après cette date"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/accessibility.html:52
-msgid "Soft Deadline"
-msgstr "Date limite affichée"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/categories.html:7
-#, fuzzy
-msgid "Tag name, separated by commas"
-msgstr "\"Identifiants d'étiquettes, séparés par des virgules\""
-
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/evaluation_mode.html:4
-msgid "Evaluation submission"
-msgstr "Soumission pour évaluation"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/groups.html:12
-msgid "Individually"
-msgstr "Indivuellement"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/groups.html:21
-msgid "Per group"
-msgstr "Par groupe"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/submission_limit.html:3
-msgid "Submission limits"
-msgstr "Limite de soumission"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/submission_limit.html:14
-msgid "Hard limit: {nbr_submissions} submission(s)"
-msgstr "Limite dure: {nbr_submissions} soumission(s)"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/submission_limit.html:23
-msgid "Soft limit: {nbr_submissions} submission(s) every {nbr_hours} hour(s)"
-msgstr ""
-"Limite douce: {nbr_submissions} soumission(s) toutes les {nbr_hours} heure(s)"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/submission_storage.html:15
-msgid "Only the last {nbr_submissions} submissions"
-msgstr "Seulement les {nbr_submissions} dernières soumissions"
-
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/weight.html:4
-msgid "Grade weight (in comparison to other tasks)"
-msgstr "Poids de la note (par rapport aux autres exercices)"
-
-#: inginious/frontend/templates/preferences/bindings.html:46
-#, fuzzy
-msgid "Refresh fields"
-msgstr "\"Mettre à jour les champs additionnels\""
-
-#: inginious/frontend/templates/preferences/bindings.html:58
-msgid "Identifier"
-msgstr "Identifiant"
-
-#: inginious/frontend/templates/preferences/bindings.html:60
-msgid "Additional fields"
-msgstr "Champs additionnels"
-
-#: inginious/frontend/templates/preferences/bindings.html:74
-msgid "Add a new binding"
-msgstr "Ajouter une nouvelle méthode"
-
-#: inginious/frontend/templates/preferences/bindings.html:78
-msgid "Select a authentication binding"
-msgstr "Sélectionner une méthode d'authentification"
-
-#: inginious/frontend/templates/preferences/bindings.html:87
-msgid "Add new binding"
-msgstr "Ajouter la méthode"
-
-#: inginious/frontend/templates/preferences/delete.html:41
-msgid "To confirm, type your email address :"
-msgstr "Pour confirmer, tapez votre addresse mail:"
-
-#: inginious/frontend/templates/preferences/delete.html:56
-#, fuzzy
-msgid "This will also delete all your data ! Are you really sure ?"
-msgstr ""
-"
Ceci va restaurer les données du cours à la date du {}. Etes vous sûr ?"
-"p>"
-
-#: inginious/frontend/templates/preferences/profile.html:40
-msgid "Please set a new username"
-msgstr "Veuillez choisir un nom d'utilisateur"
-
-#: inginious/frontend/templates/preferences/profile.html:54
-msgid "Username :"
-msgstr "Nom d'utilisateur :"
-
-#: inginious/frontend/templates/preferences/profile.html:66
-msgid "Language :"
-msgstr "Langue :"
-
-#: inginious/frontend/templates/preferences/profile.html:75
-msgid "Old password :"
-msgstr "Mot de passe actuel :"
-
-#: inginious/frontend/templates/preferences/profile.html:79
-msgid "New password (min. 6 characters) :"
-msgstr "Nouveau mot de passe (min. 6 caractères) :"
-
-#: inginious/frontend/templates/preferences/profile.html:83
-msgid "Confirm new password :"
-msgstr "Confirmez le nouveau mot de passe :"
-
-#: inginious/frontend/templates/preferences/profile.html:94
-msgid "Save my profile"
-msgstr "Sauvegarder mon profil"
-
-#: inginious/frontend/templates/task_dispensers/task_list.html:34
-msgid "deadline reached"
-msgstr "date limite atteinte"
-
-#: inginious/frontend/templates/task_dispensers/task_list.html:39
-msgid "Weight : "
-msgstr ""
-
-#: inginious/frontend/templates/tasks/file.html:7
-msgid "Click here to download the file you submitted previously"
-msgstr "Cliquez ici pour télécharger le fichier que vous aviez soumis"
-
-#: inginious/frontend/templates/tasks/file.html:22
-msgid "Max file size:"
-msgstr "Taille fichier max. :"
-
-#: inginious/frontend/templates/tasks/file.html:23
-msgid "Allowed extensions:"
-msgstr "Extensions autorisées :"
-
-#~ msgid "Docker container"
-#~ msgstr "Conteneur Docker"
-
-#~ msgid "Cannot have a soft deadline after the hard one"
-#~ msgstr "La date limite affichée doit précéder la date limite effective."
-
-#~ msgid "You are now activated. You can proceed to login."
-#~ msgstr "Votre compte est maintenant activé. Vous pouvez vous connecter."
-
-#~ msgid "Grade weight must be a floating-point number"
-#~ msgstr "Le poids de la note doit être un nombre réel"
-
-#~ msgid "Invalid submission limit!"
-#~ msgstr "Limite de soumission invalide !"
-
-#~ msgid "Invalid task accessibility ({})"
-#~ msgstr "Accessibilité invalide ({})"
-
-#~ msgid "INGInious | {course} - {task}"
-#~ msgstr "INGInious | {course} - {task}"
-
-#~ msgid ""
-#~ "Check out INGInious course {course} and beat my score of {score}%% on "
-#~ "task {task} !"
-#~ msgstr ""
-#~ "Découvrez le cours {course} sur INGInious et battez mon score de {score}%"
-#~ "% sur l'exercice {task} !"
-
-#~ msgid "No rank for one section"
-#~ msgstr "Une section n'a pas de rang"
-
-#~ msgid "No id for one section"
-#~ msgstr "Une section n'a pas d'identifiant"
-
-#~ msgid "Share my result"
-#~ msgstr "Partager mon résultat"
-
-#~ msgid "Share my result on: "
-#~ msgstr "Partager mon résultat sur: "
-
-#~ msgid "Registered student"
-#~ msgstr "Etudiant enregistré"
-
-#~ msgid "New student"
-#~ msgstr "Nouvel étudiant"
-
-#~ msgid "Category"
-#~ msgstr "Catégorie"
-
-#~ msgid ""
-#~ "This will permanently remove the task and its files from INGInious."
-#~ msgstr ""
-#~ "Ceci supprimera définitivement l'exercice et ses fichier "
-#~ "d'INGInious."
-#~ msgid "Accessible"
-#~ msgstr "Accessible"
diff --git a/inginious/frontend/i18n/messages.pot b/inginious/frontend/i18n/messages.pot
index 55b61c40e5..e030e948e8 100644
--- a/inginious/frontend/i18n/messages.pot
+++ b/inginious/frontend/i18n/messages.pot
@@ -8,18 +8,14 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2023-04-04 15:06+0200\n"
+"POT-Creation-Date: 2023-08-22 13:53+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Generated-By: Babel 2.11.0\n"
-
-#: inginious/frontend/courses.py:30
-msgid "List of exercises"
-msgstr ""
+"Generated-By: Babel 2.12.1\n"
#: inginious/frontend/parsable_text.py:37
msgid "[no content]"
@@ -39,7 +35,13 @@ msgstr ""
msgid "Parsing failed:
{}
"
msgstr ""
-#: inginious/frontend/submission_manager.py:420
+#: inginious/frontend/submission_manager.py:91
+msgid ""
+"Maximum submission size exceeded. Check feedback, stdout, stderr and "
+"state."
+msgstr ""
+
+#: inginious/frontend/submission_manager.py:432
msgid "Feedback is badly formatted."
msgstr ""
@@ -63,29 +65,41 @@ msgstr ""
msgid "match"
msgstr ""
-#: inginious/frontend/tasks.py:86
+#: inginious/frontend/tasks.py:85
msgid "Environment type {0} is unknown"
msgstr ""
-#: inginious/frontend/user_manager.py:436
+#: inginious/frontend/tasksets.py:24
+msgid "List of exercises"
+msgstr ""
+
+#: inginious/frontend/user_manager.py:446
msgid "Auth method not found."
msgstr ""
#: inginious/frontend/pages/preferences/bindings.py:42
-#: inginious/frontend/user_manager.py:487
+#: inginious/frontend/user_manager.py:497
msgid "Incorrect authentication binding."
msgstr ""
-#: inginious/frontend/user_manager.py:497
+#: inginious/frontend/user_manager.py:507
msgid "You must set a password before removing all bindings."
msgstr ""
-#: inginious/frontend/user_manager.py:528
+#: inginious/frontend/user_manager.py:538
msgid "User could not be created."
msgstr ""
#: inginious/frontend/environment_types/docker.py:12
-msgid "Standard container (Docker)"
+msgid "Standard container + SSH"
+msgstr ""
+
+#: inginious/frontend/environment_types/docker.py:12
+msgid "Standard container"
+msgstr ""
+
+#: inginious/frontend/environment_types/kata.py:11
+msgid "Container running as root (Kata) + SSH"
msgstr ""
#: inginious/frontend/environment_types/kata.py:11
@@ -96,6 +110,10 @@ msgstr ""
msgid "Multiple Choice Question solver"
msgstr ""
+#: inginious/frontend/environment_types/nvidia.py:11
+msgid "Container with GPUs (NVIDIA) + SSH"
+msgstr ""
+
#: inginious/frontend/environment_types/nvidia.py:11
msgid "Container with GPUs (NVIDIA)"
msgstr ""
@@ -104,7 +122,7 @@ msgstr ""
msgid "Course not found."
msgstr ""
-#: inginious/frontend/pages/course_register.py:23
+#: inginious/frontend/pages/course_register.py:24
msgid "This course doesn't exist."
msgstr ""
@@ -161,12 +179,12 @@ msgid "Couldn't validate LTI request"
msgstr ""
#: inginious/frontend/pages/marketplace.py:32
-msgid "You don't have superadmin rights on this course."
+msgid "You don't have superadmin rights on this taskset."
msgstr ""
#: inginious/frontend/pages/marketplace.py:39
-#: inginious/frontend/pages/marketplace_course.py:33
-#: inginious/frontend/pages/marketplace_course.py:42
+#: inginious/frontend/pages/marketplace_taskset.py:33
+#: inginious/frontend/pages/marketplace_taskset.py:42
msgid "You're not allowed to do that"
msgstr ""
@@ -176,15 +194,15 @@ msgid "User returned an invalid form."
msgstr ""
#: inginious/frontend/pages/marketplace.py:75
-msgid "Couldn't clone course into your instance"
+msgid "Couldn't clone taskset into your instance"
msgstr ""
#: inginious/frontend/pages/marketplace.py:96
-msgid "An error occur while editing the course description"
+msgid "An error occur while editing the taskset description"
msgstr ""
-#: inginious/frontend/pages/marketplace_course.py:25
-msgid "Course unavailable."
+#: inginious/frontend/pages/marketplace_taskset.py:25
+msgid "Taskset unavailable."
msgstr ""
#: inginious/frontend/pages/register.py:30
@@ -296,132 +314,156 @@ msgstr ""
msgid "Auth method doesn't exist"
msgstr ""
-#: inginious/frontend/pages/social.py:39
+#: inginious/frontend/pages/social.py:38
msgid "Auth method doesn't exist."
msgstr ""
-#: inginious/frontend/pages/tasks.py:93 inginious/frontend/pages/tasks.py:264
+#: inginious/frontend/pages/tasks.py:93 inginious/frontend/pages/tasks.py:263
msgid "Submission doesn't exist."
msgstr ""
-#: inginious/frontend/pages/tasks.py:176 inginious/frontend/pages/tasks.py:184
-#: inginious/frontend/pages/tasks.py:202 inginious/frontend/pages/tasks.py:224
-#: inginious/frontend/pages/tasks.py:231 inginious/frontend/pages/tasks.py:248
+#: inginious/frontend/pages/tasks.py:175 inginious/frontend/pages/tasks.py:183
+#: inginious/frontend/pages/tasks.py:201 inginious/frontend/pages/tasks.py:223
+#: inginious/frontend/pages/tasks.py:230 inginious/frontend/pages/tasks.py:247
msgid "Error"
msgstr ""
-#: inginious/frontend/pages/tasks.py:176
+#: inginious/frontend/pages/tasks.py:175
msgid "You are not allowed to submit for this task."
msgstr ""
-#: inginious/frontend/pages/tasks.py:184
+#: inginious/frontend/pages/tasks.py:183
msgid "Your task has been regenerated. This current task is outdated."
msgstr ""
-#: inginious/frontend/pages/tasks.py:203
+#: inginious/frontend/pages/tasks.py:202
msgid ""
"Please answer to all the questions and verify the extensions of the files"
" you want to upload. Your responses were not tested."
msgstr ""
-#: inginious/frontend/pages/tasks.py:220 inginious/frontend/pages/tasks.py:329
+#: inginious/frontend/pages/tasks.py:219 inginious/frontend/pages/tasks.py:328
msgid "Your submission has been sent..."
msgstr ""
-#: inginious/frontend/pages/course_admin/task_edit_file.py:209
-#: inginious/frontend/pages/course_admin/task_edit_file.py:230
-#: inginious/frontend/pages/course_admin/task_edit_file.py:234
-#: inginious/frontend/pages/tasks.py:231 inginious/frontend/pages/tasks.py:248
+#: inginious/frontend/pages/tasks.py:230 inginious/frontend/pages/tasks.py:247
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:205
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:226
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:230
#: inginious/frontend/templates/internalerror.html:5
#: inginious/frontend/templates/task.html:325
msgid "Internal error"
msgstr ""
-#: inginious/frontend/pages/tasks.py:298
+#: inginious/frontend/pages/tasks.py:297
msgid "INGInious is currently grading your answers. (almost done)"
msgstr ""
-#: inginious/frontend/pages/tasks.py:300
+#: inginious/frontend/pages/tasks.py:299
msgid ""
"INGInious is currently grading your answers. (Approx. wait time: "
"{} seconds)"
msgstr ""
-#: inginious/frontend/pages/tasks.py:303
+#: inginious/frontend/pages/tasks.py:302
msgid "You are next in the waiting queue!"
msgstr ""
-#: inginious/frontend/pages/tasks.py:305
+#: inginious/frontend/pages/tasks.py:304
msgid "There is one task in front of you in the waiting queue."
msgstr ""
-#: inginious/frontend/pages/tasks.py:307
+#: inginious/frontend/pages/tasks.py:306
msgid "There are {} tasks in front of you in the waiting queue."
msgstr ""
-#: inginious/frontend/pages/tasks.py:331
+#: inginious/frontend/pages/tasks.py:330
msgid "There are some errors in your answer. Your score is {score}%."
msgstr ""
-#: inginious/frontend/pages/tasks.py:333
+#: inginious/frontend/pages/tasks.py:332
msgid "Your answer passed the tests! Your score is {score}%."
msgstr ""
-#: inginious/frontend/pages/tasks.py:335
+#: inginious/frontend/pages/tasks.py:334
msgid "Your submission timed out. Your score is {score}%."
msgstr ""
-#: inginious/frontend/pages/tasks.py:337
+#: inginious/frontend/pages/tasks.py:336
msgid "Your submission made an overflow. Your score is {score}%."
msgstr ""
-#: inginious/frontend/pages/tasks.py:339
+#: inginious/frontend/pages/tasks.py:338
msgid "Your submission was killed."
msgstr ""
-#: inginious/frontend/pages/tasks.py:341
+#: inginious/frontend/pages/tasks.py:340
msgid ""
"An internal error occurred. Please retry later. If the error persists, "
"send an email to the course administrator."
msgstr ""
-#: inginious/frontend/pages/tasks.py:344
+#: inginious/frontend/pages/tasks.py:343
msgid "[Submission #{submissionid} ({submissionDate})]"
msgstr ""
-#: inginious/frontend/pages/tasks.py:379 inginious/frontend/pages/tasks.py:381
+#: inginious/frontend/pages/tasks.py:378 inginious/frontend/pages/tasks.py:380
msgid " "
msgstr ""
-#: inginious/frontend/pages/utils.py:192
+#: inginious/frontend/pages/tasksets.py:43
+msgid "Course with id {} successfully instantiated from taskset {}"
+msgstr ""
+
+#: inginious/frontend/pages/tasksets.py:46
+msgid "You are not allowed to instantiate a course from this taskset."
+msgstr ""
+
+#: inginious/frontend/pages/tasksets.py:49
+msgid "A course with id {} already exists."
+msgstr ""
+
+#: inginious/frontend/pages/tasksets.py:52
+msgid "Couldn't instantiate course with id {} : "
+msgstr ""
+
+#: inginious/frontend/pages/tasksets.py:59
+msgid "Taskset created."
+msgstr ""
+
+#: inginious/frontend/pages/tasksets.py:62
+msgid "Failed to create the taskset."
+msgstr ""
+
+#: inginious/frontend/pages/utils.py:197
msgid ""
"An account using this email already exists and is not bound with this "
"service. For security reasons, please log in via another method and bind "
"your account in your profile."
msgstr ""
-#: inginious/frontend/pages/utils.py:195
+#: inginious/frontend/pages/utils.py:200
msgid ""
"Couldn't fetch the required information from the service. Please check "
"the provided permissions (name, email) and contact your INGInious "
"administrator if the error persists."
msgstr ""
-#: inginious/frontend/pages/utils.py:221
+#: inginious/frontend/pages/utils.py:226
msgid "Invalid login/password"
msgstr ""
-#: inginious/frontend/pages/utils.py:249
+#: inginious/frontend/pages/utils.py:254
#: inginious/frontend/templates/forbidden.html:5
#: inginious/frontend/templates/forbidden.html:9
msgid "Forbidden"
msgstr ""
-#: inginious/frontend/pages/utils.py:263
+#: inginious/frontend/pages/utils.py:268
msgid "You have not sufficient right to see this part."
msgstr ""
-#: inginious/frontend/pages/utils.py:312
+#: inginious/frontend/pages/utils.py:317
msgid "File doesn't exist."
msgstr ""
@@ -460,6 +502,7 @@ msgid "This file doesn't exist."
msgstr ""
#: inginious/frontend/pages/course_admin/danger_zone.py:164
+#: inginious/frontend/pages/taskset_admin/danger_zone.py:50
msgid "Operation aborted due to invalid token."
msgstr ""
@@ -489,6 +532,7 @@ msgid "An error occurred while deleting the course data: {}"
msgstr ""
#: inginious/frontend/pages/course_admin/settings.py:34
+#: inginious/frontend/pages/taskset_admin/settings.py:48
msgid "Invalid name"
msgstr ""
@@ -496,39 +540,39 @@ msgstr ""
msgid "You cannot remove yourself from the administrators of this course"
msgstr ""
-#: inginious/frontend/pages/course_admin/settings.py:55
+#: inginious/frontend/pages/course_admin/settings.py:52
msgid "Invalid accessibility dates"
msgstr ""
-#: inginious/frontend/pages/course_admin/settings.py:70
+#: inginious/frontend/pages/course_admin/settings.py:67
msgid "Invalid registration dates"
msgstr ""
-#: inginious/frontend/pages/course_admin/settings.py:78
+#: inginious/frontend/pages/course_admin/settings.py:75
msgid "Invalid ACL value"
msgstr ""
-#: inginious/frontend/pages/course_admin/settings.py:92
+#: inginious/frontend/pages/course_admin/settings.py:89
msgid "LTI keys must be alphanumerical."
msgstr ""
-#: inginious/frontend/pages/course_admin/settings.py:129
+#: inginious/frontend/pages/course_admin/settings.py:126
msgid "Some tag fields were missing."
msgstr ""
-#: inginious/frontend/pages/course_admin/settings.py:132
+#: inginious/frontend/pages/course_admin/settings.py:129
msgid "Invalid tag id: {}"
msgstr ""
-#: inginious/frontend/pages/course_admin/settings.py:151
+#: inginious/frontend/pages/course_admin/settings.py:148
msgid "Invalid type value: {}"
msgstr ""
-#: inginious/frontend/pages/course_admin/settings.py:153
+#: inginious/frontend/pages/course_admin/settings.py:150
msgid "Invalid id: {}"
msgstr ""
-#: inginious/frontend/pages/course_admin/settings.py:167
+#: inginious/frontend/pages/course_admin/settings.py:164
msgid "Some datas have the same id! The id must be unique."
msgstr ""
@@ -600,7 +644,7 @@ msgid "The following submission could not be prepared for download: {}"
msgstr ""
#: inginious/frontend/pages/course_admin/submissions.py:67
-#: inginious/frontend/pages/course_admin/utils.py:43
+#: inginious/frontend/pages/course_admin/utils.py:38
msgid "You don't have admin rights on this course."
msgstr ""
@@ -612,152 +656,82 @@ msgstr ""
msgid "The submission doesn't exist."
msgstr ""
-#: inginious/frontend/pages/course_admin/task_edit.py:34
-#: inginious/frontend/pages/course_admin/task_edit_file.py:24
-#: inginious/frontend/pages/course_admin/task_edit_file.py:45
-#: inginious/frontend/pages/course_admin/task_edit_file.py:264
-msgid "Invalid task id"
-msgstr ""
-
-#: inginious/frontend/pages/course_admin/task_edit.py:87
-msgid "Invalid course/task id"
-msgstr ""
-
-#: inginious/frontend/pages/course_admin/task_edit.py:117
-msgid "Invalid file type: {}"
-msgstr ""
-
-#: inginious/frontend/pages/course_admin/task_edit.py:135
-msgid "The number of random inputs must be an integer!"
-msgstr ""
-
-#: inginious/frontend/pages/course_admin/task_edit.py:137
-msgid "The number of random inputs must be positive!"
-msgstr ""
-
-#: inginious/frontend/pages/course_admin/task_edit.py:146
-msgid "Your browser returned an invalid form ({})"
-msgstr ""
-
-#: inginious/frontend/pages/course_admin/task_edit.py:152
-msgid "Error while reading course's informations"
-msgstr ""
-
-#: inginious/frontend/pages/course_admin/task_edit.py:176
-msgid "Invalid data: {}"
-msgstr ""
-
-#: inginious/frontend/pages/course_admin/task_edit.py:182
-msgid "Cannot read zip file. Files were not modified"
-msgstr ""
-
-#: inginious/frontend/pages/course_admin/task_edit.py:189
-msgid ""
-"There was a problem while extracting the zip archive. Some files may have"
-" been modified"
-msgstr ""
-
-#: inginious/frontend/pages/course_admin/task_edit_file.py:168
-#: inginious/frontend/pages/course_admin/task_edit_file.py:188
-#: inginious/frontend/pages/course_admin/task_edit_file.py:213
-msgid "Invalid new path"
-msgstr ""
-
-#: inginious/frontend/pages/course_admin/task_edit_file.py:174
-msgid "An error occurred while writing the file"
-msgstr ""
-
-#: inginious/frontend/pages/course_admin/task_edit_file.py:219
-msgid "An error occurred while moving the files"
-msgstr ""
-
-#: inginious/frontend/pages/course_admin/task_edit_file.py:240
-msgid "An error occurred while deleting the files"
-msgstr ""
-
-#: inginious/frontend/pages/course_admin/task_edit_file.py:247
-msgid "This path doesn't exist."
-msgstr ""
-
#: inginious/frontend/pages/course_admin/task_list.py:36
+#: inginious/frontend/pages/taskset_admin/template.py:36
msgid "Invalid task dispenser"
msgstr ""
-#: inginious/frontend/pages/course_admin/task_list.py:45
-msgid "Invalid course structure: "
-msgstr ""
-
-#: inginious/frontend/pages/course_admin/task_list.py:47
+#: inginious/frontend/pages/course_admin/task_list.py:43
+#: inginious/frontend/pages/course_admin/task_list.py:48
+#: inginious/frontend/pages/taskset_admin/template.py:43
+#: inginious/frontend/pages/taskset_admin/template.py:48
+#: inginious/frontend/pages/taskset_admin/template.py:53
msgid "Something wrong happened: "
msgstr ""
#: inginious/frontend/pages/course_admin/task_list.py:54
-msgid "Couldn't create task {} : "
-msgstr ""
-
-#: inginious/frontend/pages/course_admin/task_list.py:59
-msgid "Couldn't delete task {} : "
-msgstr ""
-
-#: inginious/frontend/pages/course_admin/task_list.py:64
msgid "Couldn't wipe task {} : "
msgstr ""
-#: inginious/frontend/pages/course_admin/utils.py:40
-msgid "You don't have staff rights on this course."
+#: inginious/frontend/pages/course_admin/task_list.py:69
+msgid "Invalid course structure: "
msgstr ""
-#: inginious/frontend/pages/course_admin/utils.py:50
+#: inginious/frontend/pages/course_admin/utils.py:45
msgid "This course is unreachable"
msgstr ""
-#: inginious/frontend/pages/course_admin/utils.py:144
+#: inginious/frontend/pages/course_admin/utils.py:139
msgid "List not valid."
msgstr ""
-#: inginious/frontend/pages/course_admin/utils.py:343
-#: inginious/frontend/templates/course_admin/settings.html:18
-#: inginious/frontend/templates/course_admin/settings.html:25
+#: inginious/frontend/pages/course_admin/utils.py:338
+#: inginious/frontend/templates/course.html:53
+#: inginious/frontend/templates/course.html:71
+#: inginious/frontend/templates/course_user_settings.html:33
#: inginious/frontend/templates/course_user_settings.html:47
#: inginious/frontend/templates/course_user_settings.html:57
-msgid "Course settings"
+msgid "User settings"
msgstr ""
-#: inginious/frontend/pages/course_admin/utils.py:345
-#: inginious/frontend/templates/course_admin/stats.html:27
-#: inginious/frontend/templates/course_admin/stats.html:35
+#: inginious/frontend/pages/course_admin/utils.py:340
+#: inginious/frontend/templates/course_admin/stats.html:28
+#: inginious/frontend/templates/course_admin/stats.html:36
#: inginious/frontend/templates/course_admin/submissions_query.html:211
msgid "Statistics"
msgstr ""
-#: inginious/frontend/pages/course_admin/utils.py:346
-#: inginious/frontend/templates/course_admin/student_list.html:20
-#: inginious/frontend/templates/course_admin/student_list.html:28
+#: inginious/frontend/pages/course_admin/utils.py:341
+#: inginious/frontend/templates/course_admin/student_list.html:21
+#: inginious/frontend/templates/course_admin/student_list.html:29
msgid "User management"
msgstr ""
-#: inginious/frontend/pages/course_admin/utils.py:349
-#: inginious/frontend/templates/course_admin/stats.html:49
-#: inginious/frontend/templates/course_admin/stats.html:248
-#: inginious/frontend/templates/course_admin/task_edit.html:19
-#: inginious/frontend/templates/course_admin/task_list.html:19
-#: inginious/frontend/templates/course_admin/task_list.html:25
+#: inginious/frontend/pages/course_admin/utils.py:344
+#: inginious/frontend/templates/course_admin/stats.html:50
+#: inginious/frontend/templates/course_admin/stats.html:249
+#: inginious/frontend/templates/course_admin/task_list.html:20
+#: inginious/frontend/templates/course_admin/task_list.html:26
msgid "Tasks"
msgstr ""
-#: inginious/frontend/pages/course_admin/utils.py:351
-#: inginious/frontend/templates/course_admin/stats.html:43
-#: inginious/frontend/templates/course_admin/stats.html:168
+#: inginious/frontend/pages/course_admin/utils.py:346
+#: inginious/frontend/templates/course_admin/stats.html:44
+#: inginious/frontend/templates/course_admin/stats.html:169
#: inginious/frontend/templates/course_admin/submissions.html:6
-#: inginious/frontend/templates/course_admin/submissions.html:18
-#: inginious/frontend/templates/course_admin/submissions.html:26
+#: inginious/frontend/templates/course_admin/submissions.html:19
+#: inginious/frontend/templates/course_admin/submissions.html:27
msgid "Submissions"
msgstr ""
-#: inginious/frontend/pages/course_admin/utils.py:354
+#: inginious/frontend/pages/course_admin/utils.py:349
+#: inginious/frontend/pages/taskset_admin/utils.py:63
#: inginious/frontend/templates/course_admin/danger_zone.html:6
-#: inginious/frontend/templates/course_admin/danger_zone.html:18
-#: inginious/frontend/templates/course_admin/danger_zone.html:24
+#: inginious/frontend/templates/course_admin/danger_zone.html:19
+#: inginious/frontend/templates/course_admin/danger_zone.html:25
+#: inginious/frontend/templates/taskset_admin/danger_zone.html:6
+#: inginious/frontend/templates/taskset_admin/danger_zone.html:19
+#: inginious/frontend/templates/taskset_admin/danger_zone.html:25
msgid "Danger zone"
msgstr ""
@@ -831,13 +805,130 @@ msgstr ""
msgid "Delete my account"
msgstr ""
+#: inginious/frontend/pages/taskset_admin/danger_zone.py:54
+msgid "Wrong taskset id."
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/danger_zone.py:59
+msgid "One or more course(s) rely on the current taskset."
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/danger_zone.py:63
+msgid "An error occurred while deleting the taskset data: {}"
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/settings.py:33
+msgid "Invalid taskid : {}"
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/settings.py:42
+msgid "Couldn't create task {} : "
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/settings.py:53
+msgid "You cannot remove yourself from the administrators of this taskset"
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:34
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:24
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:45
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:260
+msgid "Invalid task id"
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:87
+msgid "Invalid taskset/task id"
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:117
+msgid "Invalid file type: {}"
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:135
+msgid "The number of random inputs must be an integer!"
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:137
+msgid "The number of random inputs must be positive!"
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:146
+msgid "Your browser returned an invalid form ({})"
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:152
+msgid "Error while reading taskset data"
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:176
+msgid "Invalid data: {}"
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:182
+msgid "Cannot read zip file. Files were not modified"
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/task_edit.py:189
+msgid ""
+"There was a problem while extracting the zip archive. Some files may have"
+" been modified"
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:164
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:184
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:209
+msgid "Invalid new path"
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:170
+msgid "An error occurred while writing the file"
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:215
+msgid "An error occurred while moving the files"
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:236
+msgid "An error occurred while deleting the files"
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/task_edit_file.py:243
+msgid "This path doesn't exist."
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/template.py:68
+msgid "Invalid taskset structure: "
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/utils.py:34
+msgid "You don't have admin rights on this taskset."
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/utils.py:43
+msgid "This taskset is unreachable"
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/utils.py:61
+#: inginious/frontend/templates/course_admin/settings.html:6
+#: inginious/frontend/templates/taskset_admin/settings.html:6
+#: inginious/frontend/templates/taskset_admin/settings.html:49
+msgid "Settings"
+msgstr ""
+
+#: inginious/frontend/pages/taskset_admin/utils.py:62
+#: inginious/frontend/templates/taskset_admin/template.html:20
+#: inginious/frontend/templates/taskset_admin/template.html:26
+msgid "Course template"
+msgstr ""
+
#: inginious/frontend/plugins/auth/custom_auth_form.html:6
msgid "Login"
msgstr ""
#: inginious/frontend/plugins/auth/custom_auth_form.html:25
-#: inginious/frontend/templates/course_admin/edit_tabs/file_modals.html:26
-#: inginious/frontend/templates/course_admin/task_dispensers/util_task_edit_modal.html:22
+#: inginious/frontend/templates/task_dispensers_admin/util.html:67
+#: inginious/frontend/templates/task_dispensers_admin/util_task_edit_modal.html:22
+#: inginious/frontend/templates/taskset_admin/edit_tabs/file_modals.html:26
msgid "Close"
msgstr ""
@@ -847,20 +938,20 @@ msgstr ""
#: inginious/frontend/plugins/auth/custom_auth_form.html:31
#: inginious/frontend/templates/auth.html:21
-#: inginious/frontend/templates/course_admin/task_list.html:78
+#: inginious/frontend/templates/taskset_admin/settings.html:102
msgid "Username"
msgstr ""
#: inginious/frontend/plugins/auth/custom_auth_form.html:34
#: inginious/frontend/templates/auth.html:24
-#: inginious/frontend/templates/course_admin/task_list.html:79
#: inginious/frontend/templates/course_register.html:29
+#: inginious/frontend/templates/taskset_admin/settings.html:103
msgid "Password"
msgstr ""
#: inginious/frontend/plugins/auth/custom_auth_form.html:36
#: inginious/frontend/templates/auth.html:26
-#: inginious/frontend/templates/layout.html:179
+#: inginious/frontend/templates/layout.html:180
#: inginious/frontend/templates/signin_button.html:4
#: inginious/frontend/templates/signin_button.html:11
msgid "Sign in"
@@ -902,28 +993,32 @@ msgstr ""
#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:11
#: inginious/frontend/templates/admin/admin_users.html:13
#: inginious/frontend/templates/course.html:103
-#: inginious/frontend/templates/course_admin/audience_edit.html:20
-#: inginious/frontend/templates/course_admin/danger_zone.html:18
-#: inginious/frontend/templates/course_admin/settings.html:18
-#: inginious/frontend/templates/course_admin/stats.html:27
-#: inginious/frontend/templates/course_admin/student_info.html:23
-#: inginious/frontend/templates/course_admin/student_list.html:20
-#: inginious/frontend/templates/course_admin/submission.html:32
-#: inginious/frontend/templates/course_admin/submissions.html:18
-#: inginious/frontend/templates/course_admin/task_edit.html:21
-#: inginious/frontend/templates/course_admin/task_list.html:19
+#: inginious/frontend/templates/course_admin/audience_edit.html:21
+#: inginious/frontend/templates/course_admin/danger_zone.html:19
+#: inginious/frontend/templates/course_admin/settings.html:19
+#: inginious/frontend/templates/course_admin/stats.html:28
+#: inginious/frontend/templates/course_admin/student_info.html:24
+#: inginious/frontend/templates/course_admin/student_list.html:21
+#: inginious/frontend/templates/course_admin/submission.html:33
+#: inginious/frontend/templates/course_admin/submissions.html:19
+#: inginious/frontend/templates/course_admin/task_list.html:20
#: inginious/frontend/templates/course_register.html:10
#: inginious/frontend/templates/course_user_settings.html:46
-#: inginious/frontend/templates/courselist.html:27
+#: inginious/frontend/templates/courselist.html:21
#: inginious/frontend/templates/group.html:61
#: inginious/frontend/templates/marketplace.html:26
-#: inginious/frontend/templates/marketplace_course.html:47
+#: inginious/frontend/templates/marketplace_taskset.html:47
#: inginious/frontend/templates/mycourses.html:11
#: inginious/frontend/templates/preferences/bindings.html:17
#: inginious/frontend/templates/preferences/delete.html:18
#: inginious/frontend/templates/preferences/profile.html:17
#: inginious/frontend/templates/queue.html:11
#: inginious/frontend/templates/task.html:266
+#: inginious/frontend/templates/taskset_admin/danger_zone.html:19
+#: inginious/frontend/templates/taskset_admin/settings.html:19
+#: inginious/frontend/templates/taskset_admin/task_edit.html:20
+#: inginious/frontend/templates/taskset_admin/template.html:20
+#: inginious/frontend/templates/tasksets.html:11
msgid "(current)"
msgstr ""
@@ -943,105 +1038,120 @@ msgstr ""
#: inginious/frontend/templates/course_unavailable.html:9
#: inginious/frontend/templates/course_user_settings.html:44
#: inginious/frontend/templates/courselist.html:5
-#: inginious/frontend/templates/courselist.html:26
-#: inginious/frontend/templates/courselist.html:33
+#: inginious/frontend/templates/courselist.html:20
+#: inginious/frontend/templates/courselist.html:27
#: inginious/frontend/templates/layout.html:149
-#: inginious/frontend/templates/layout.html:170
-#: inginious/frontend/templates/mycourses.html:25
+#: inginious/frontend/templates/layout.html:171
#: inginious/frontend/templates/task.html:263
+#: inginious/frontend/templates/tasksets.html:24
msgid "Course list"
msgstr ""
#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:29
#: inginious/frontend/templates/course.html:21
#: inginious/frontend/templates/group.html:19
-#: inginious/frontend/templates/mycourses.html:29
+#: inginious/frontend/templates/mycourses.html:23
msgid "Last tried exercises"
msgstr ""
-#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:48
+#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:51
#: inginious/frontend/templates/course.html:40
#: inginious/frontend/templates/group.html:39
-#: inginious/frontend/templates/mycourses.html:48
+#: inginious/frontend/templates/mycourses.html:45
msgid "No submissions"
msgstr ""
-#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:56
+#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:59
msgid "My Upcoming Tasks"
msgstr ""
-#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:60
+#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:63
msgid "Switch time planner"
msgstr ""
-#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:71
+#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:74
msgid "Modify time planner"
msgstr ""
-#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:77
+#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:80
msgid ""
"This allows to display only the tasks whose deadline is in a certain "
"temporal proximity."
msgstr ""
-#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:78
+#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:81
msgid "Number of days:"
msgstr ""
-#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:86
+#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:89
#: inginious/frontend/templates/admin/admin_users.html:111
#: inginious/frontend/templates/admin/admin_users.html:131
#: inginious/frontend/templates/admin/admin_users.html:167
#: inginious/frontend/templates/admin/admin_users.html:187
-#: inginious/frontend/templates/course_admin/audience_edit.html:62
-#: inginious/frontend/templates/course_admin/danger_zone.html:85
-#: inginious/frontend/templates/course_admin/danger_zone.html:166
-#: inginious/frontend/templates/course_admin/student_info.html:106
-#: inginious/frontend/templates/course_admin/student_list.html:275
+#: inginious/frontend/templates/course_admin/audience_edit.html:63
+#: inginious/frontend/templates/course_admin/danger_zone.html:86
+#: inginious/frontend/templates/course_admin/danger_zone.html:167
+#: inginious/frontend/templates/course_admin/student_info.html:107
+#: inginious/frontend/templates/course_admin/student_list.html:276
#: inginious/frontend/templates/course_admin/student_list_table.html:86
-#: inginious/frontend/templates/course_admin/submissions.html:249
-#: inginious/frontend/templates/course_admin/task_dispensers/util_delete_modal.html:27
-#: inginious/frontend/templates/course_admin/task_list.html:56
-#: inginious/frontend/templates/course_admin/task_list.html:108
+#: inginious/frontend/templates/course_admin/submissions.html:250
+#: inginious/frontend/templates/course_admin/task_list.html:57
+#: inginious/frontend/templates/course_admin/task_list.html:100
#: inginious/frontend/templates/lti_bind.html:32
#: inginious/frontend/templates/preferences/delete.html:59
#: inginious/frontend/templates/queue.html:112
+#: inginious/frontend/templates/task_dispensers_admin/util_delete_modal.html:29
+#: inginious/frontend/templates/task_dispensers_admin/util_move_modal.html:17
+#: inginious/frontend/templates/taskset_admin/danger_zone.html:71
+#: inginious/frontend/templates/taskset_admin/settings.html:151
+#: inginious/frontend/templates/taskset_admin/settings.html:177
+#: inginious/frontend/templates/taskset_admin/template.html:57
+#: inginious/frontend/templates/taskset_admin/template.html:101
+#: inginious/frontend/templates/tasksets.html:101
#: inginious/frontend/templates/unregister_modal.html:19
msgid "Cancel"
msgstr ""
-#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:87
-#: inginious/frontend/templates/course_admin/settings.html:486
-#: inginious/frontend/templates/course_admin/task_edit.html:10
-#: inginious/frontend/templates/course_admin/task_edit.html:31
-#: inginious/frontend/templates/course_admin/task_edit.html:97
-#: inginious/frontend/templates/course_admin/task_list.html:57
-#: inginious/frontend/templates/course_admin/task_list.html:113
+#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:90
+#: inginious/frontend/templates/course_admin/settings.html:481
+#: inginious/frontend/templates/course_admin/task_list.html:58
+#: inginious/frontend/templates/course_admin/task_list.html:105
+#: inginious/frontend/templates/taskset_admin/settings.html:51
+#: inginious/frontend/templates/taskset_admin/task_edit.html:10
+#: inginious/frontend/templates/taskset_admin/task_edit.html:30
+#: inginious/frontend/templates/taskset_admin/task_edit.html:95
+#: inginious/frontend/templates/taskset_admin/template.html:58
+#: inginious/frontend/templates/taskset_admin/template.html:106
msgid "Save changes"
msgstr ""
-#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:102
+#: inginious/frontend/plugins/upcoming_tasks/templates/coming_tasks.html:105
msgid "You have no upcoming tasks"
msgstr ""
-#: inginious/frontend/task_dispensers/combinatory_test.py:22
+#: inginious/frontend/task_dispensers/combinatory_test.py:24
msgid "Combinatory test"
msgstr ""
-#: inginious/frontend/task_dispensers/combinatory_test.py:45
+#: inginious/frontend/task_dispensers/combinatory_test.py:47
msgid "Amount of tasks to be displayed"
msgstr ""
-#: inginious/frontend/task_dispensers/toc.py:40
+#: inginious/frontend/task_dispensers/toc.py:52
msgid "Table of contents"
msgstr ""
-#: inginious/frontend/task_dispensers/toc.py:103
+#: inginious/frontend/task_dispensers/toc.py:115
msgid "Closed by default"
msgstr ""
+#: inginious/frontend/task_dispensers/toc.py:116
+msgid "Hidden if empty"
+msgstr ""
+
#: inginious/frontend/task_dispensers/util.py:49
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/groups.html:4
+#: inginious/frontend/templates/task_dispensers_admin/config_items/groups.html:6
+#: inginious/frontend/templates/task_dispensers_admin/config_items/groups.html:10
msgid "Submission mode"
msgstr ""
@@ -1050,7 +1160,8 @@ msgid "Weight"
msgstr ""
#: inginious/frontend/task_dispensers/util.py:97
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/submission_storage.html:4
+#: inginious/frontend/templates/task_dispensers_admin/config_items/submission_storage.html:6
+#: inginious/frontend/templates/task_dispensers_admin/config_items/submission_storage.html:12
msgid "Submission storage"
msgstr ""
@@ -1059,7 +1170,8 @@ msgid "Evaluation mode"
msgstr ""
#: inginious/frontend/task_dispensers/util.py:145
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/categories.html:4
+#: inginious/frontend/templates/task_dispensers_admin/config_items/categories.html:6
+#: inginious/frontend/templates/task_dispensers_admin/config_items/categories.html:9
msgid "Categories"
msgstr ""
@@ -1069,9 +1181,10 @@ msgid "Submission limit"
msgstr ""
#: inginious/frontend/task_dispensers/util.py:193
-#: inginious/frontend/templates/course_admin/settings.html:43
-#: inginious/frontend/templates/course_admin/settings.html:140
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/accessibility.html:4
+#: inginious/frontend/templates/course_admin/settings.html:44
+#: inginious/frontend/templates/course_admin/settings.html:135
+#: inginious/frontend/templates/task_dispensers_admin/config_items/accessibility.html:6
+#: inginious/frontend/templates/task_dispensers_admin/config_items/accessibility.html:15
msgid "Accessibility"
msgstr ""
@@ -1112,12 +1225,6 @@ msgid ""
"course."
msgstr ""
-#: inginious/frontend/templates/course.html:53
-#: inginious/frontend/templates/course.html:71
-#: inginious/frontend/templates/course_user_settings.html:33
-msgid "Course Settings"
-msgstr ""
-
#: inginious/frontend/templates/course.html:56
#: inginious/frontend/templates/course_user_settings.html:18
msgid "Course administration"
@@ -1154,14 +1261,21 @@ msgid "Please enroll in the course to be able to submit answers to problems."
msgstr ""
#: inginious/frontend/templates/course.html:99
+#: inginious/frontend/templates/course_admin/audience_edit.html:15
+#: inginious/frontend/templates/course_admin/danger_zone.html:15
+#: inginious/frontend/templates/course_admin/settings.html:15
+#: inginious/frontend/templates/course_admin/stats.html:22
+#: inginious/frontend/templates/course_admin/student_info.html:17
+#: inginious/frontend/templates/course_admin/student_list.html:15
+#: inginious/frontend/templates/course_admin/submission.html:19
+#: inginious/frontend/templates/course_admin/submissions.html:15
+#: inginious/frontend/templates/course_admin/task_list.html:16
#: inginious/frontend/templates/course_user_settings.html:42
-#: inginious/frontend/templates/courselist.html:17
#: inginious/frontend/templates/layout.html:154
-#: inginious/frontend/templates/marketplace.html:17
#: inginious/frontend/templates/mycourses.html:5
#: inginious/frontend/templates/mycourses.html:10
#: inginious/frontend/templates/mycourses.html:18
-#: inginious/frontend/templates/mycourses.html:55
+#: inginious/frontend/templates/mycourses.html:52
#: inginious/frontend/templates/task.html:261
msgid "My courses"
msgstr ""
@@ -1179,7 +1293,7 @@ msgid "Register to course '{}'"
msgstr ""
#: inginious/frontend/templates/course_register.html:11
-#: inginious/frontend/templates/layout.html:175
+#: inginious/frontend/templates/layout.html:176
#: inginious/frontend/templates/register.html:5
msgid "Register"
msgstr ""
@@ -1238,15 +1352,15 @@ msgid ""
"course administrator, go to your 'My courses' page to see all of them."
msgstr ""
-#: inginious/frontend/templates/courselist.html:62
+#: inginious/frontend/templates/courselist.html:56
msgid "External platform"
msgstr ""
-#: inginious/frontend/templates/courselist.html:66
+#: inginious/frontend/templates/courselist.html:60
msgid "Auto-registration"
msgstr ""
-#: inginious/frontend/templates/courselist.html:70
+#: inginious/frontend/templates/courselist.html:64
msgid "Password needed"
msgstr ""
@@ -1254,8 +1368,8 @@ msgstr ""
msgid "Register for a group"
msgstr ""
-#: inginious/frontend/templates/course_admin/student_list.html:48
-#: inginious/frontend/templates/course_admin/student_list.html:208
+#: inginious/frontend/templates/course_admin/student_list.html:49
+#: inginious/frontend/templates/course_admin/student_list.html:209
#: inginious/frontend/templates/group.html:59
#: inginious/frontend/templates/group.html:68
msgid "Groups"
@@ -1336,22 +1450,25 @@ msgstr ""
msgid "Open menu"
msgstr ""
-#: inginious/frontend/templates/course_admin/audience_edit.html:16
-#: inginious/frontend/templates/course_admin/danger_zone.html:16
-#: inginious/frontend/templates/course_admin/settings.html:16
-#: inginious/frontend/templates/course_admin/stats.html:23
-#: inginious/frontend/templates/course_admin/student_info.html:18
-#: inginious/frontend/templates/course_admin/student_list.html:16
-#: inginious/frontend/templates/course_admin/submission.html:21
-#: inginious/frontend/templates/course_admin/submissions.html:16
-#: inginious/frontend/templates/course_admin/task_edit.html:17
-#: inginious/frontend/templates/course_admin/task_list.html:17
+#: inginious/frontend/templates/course_admin/audience_edit.html:17
+#: inginious/frontend/templates/course_admin/danger_zone.html:17
+#: inginious/frontend/templates/course_admin/settings.html:17
+#: inginious/frontend/templates/course_admin/stats.html:24
+#: inginious/frontend/templates/course_admin/student_info.html:19
+#: inginious/frontend/templates/course_admin/student_list.html:17
+#: inginious/frontend/templates/course_admin/submission.html:22
+#: inginious/frontend/templates/course_admin/submissions.html:17
+#: inginious/frontend/templates/course_admin/task_list.html:18
#: inginious/frontend/templates/layout.html:142
#: inginious/frontend/templates/task.html:128
+#: inginious/frontend/templates/taskset_admin/danger_zone.html:18
+#: inginious/frontend/templates/taskset_admin/settings.html:18
+#: inginious/frontend/templates/taskset_admin/task_edit.html:19
+#: inginious/frontend/templates/taskset_admin/template.html:19
msgid "Administration"
msgstr ""
-#: inginious/frontend/templates/course_admin/stats.html:260
+#: inginious/frontend/templates/course_admin/stats.html:261
#: inginious/frontend/templates/layout.html:145
msgid "Users"
msgstr ""
@@ -1360,45 +1477,53 @@ msgstr ""
#: inginious/frontend/templates/marketplace.html:5
#: inginious/frontend/templates/marketplace.html:25
#: inginious/frontend/templates/marketplace.html:32
-#: inginious/frontend/templates/marketplace_course.html:45
+#: inginious/frontend/templates/marketplace_taskset.html:45
msgid "Marketplace"
msgstr ""
#: inginious/frontend/templates/layout.html:155
+#: inginious/frontend/templates/marketplace.html:17
+#: inginious/frontend/templates/tasksets.html:5
+#: inginious/frontend/templates/tasksets.html:10
+#: inginious/frontend/templates/tasksets.html:32
+msgid "My tasksets"
+msgstr ""
+
+#: inginious/frontend/templates/layout.html:156
#: inginious/frontend/templates/preferences/bindings.html:15
#: inginious/frontend/templates/preferences/delete.html:16
#: inginious/frontend/templates/preferences/profile.html:15
msgid "Preferences"
msgstr ""
-#: inginious/frontend/templates/layout.html:156
+#: inginious/frontend/templates/layout.html:157
msgid "Service status"
msgstr ""
-#: inginious/frontend/templates/layout.html:157
+#: inginious/frontend/templates/layout.html:158
msgid "Log out"
msgstr ""
-#: inginious/frontend/templates/layout.html:161
+#: inginious/frontend/templates/layout.html:162
msgid "English"
msgstr ""
-#: inginious/frontend/templates/layout.html:195
+#: inginious/frontend/templates/layout.html:196
msgid ""
"Powered by INGInious v.{} , a free and open-source grading "
"tool."
msgstr ""
-#: inginious/frontend/templates/layout.html:197
+#: inginious/frontend/templates/layout.html:198
msgid "Running INGInious v.{}"
msgstr ""
-#: inginious/frontend/templates/layout.html:199
+#: inginious/frontend/templates/layout.html:200
msgid "INGInious is distributed under AGPL license"
msgstr ""
-#: inginious/frontend/templates/layout.html:203
+#: inginious/frontend/templates/layout.html:204
msgid "Privacy Policy"
msgstr ""
@@ -1478,29 +1603,29 @@ msgstr ""
#: inginious/frontend/templates/marketplace.html:12
msgid ""
-"This page list all the course that are publicly avalaible. When you click"
-" import, it will create a new course on your INGInious instance. You will"
-" be admin of this new course and it will contain all the tasks and the "
-"structure of the imported course"
+"This page list all the tasksets that are publicly avalaible. When you "
+"click import, it will create a new taskset on your INGInious instance. "
+"You will be admin of this new taskset and it will contain all the tasks "
+"of the imported taskset."
msgstr ""
#: inginious/frontend/templates/marketplace.html:56
-#: inginious/frontend/templates/marketplace_course.html:68
-msgid "Import course"
+#: inginious/frontend/templates/marketplace_taskset.html:68
+msgid "Import taskset"
msgstr ""
#: inginious/frontend/templates/marketplace.html:74
-msgid "No public courses available"
+msgid "No public tasksets available"
msgstr ""
#: inginious/frontend/templates/marketplace.html:84
-msgid "Import this course"
+msgid "Import this taskset"
msgstr ""
#: inginious/frontend/templates/marketplace.html:89
msgid ""
-"This will create a new course with the ID bellow. This new course will "
-"contain the tasks and structure of the selected course"
+"This will create a new taskset with the ID bellow. This new taskset will "
+"contain the tasks and structure of the selected taskset"
msgstr ""
#: inginious/frontend/templates/marketplace.html:90
@@ -1508,38 +1633,38 @@ msgid "Course ID"
msgstr ""
#: inginious/frontend/templates/marketplace.html:94
-msgid "Create course"
+msgid "Create taskset"
msgstr ""
-#: inginious/frontend/templates/marketplace_course.html:7
+#: inginious/frontend/templates/marketplace_taskset.html:7
#: inginious/frontend/templates/task.html:13
msgid "Information"
msgstr ""
-#: inginious/frontend/templates/marketplace_course.html:11
+#: inginious/frontend/templates/marketplace_taskset.html:11
msgid "Language(s)"
msgstr ""
-#: inginious/frontend/templates/marketplace_course.html:17
+#: inginious/frontend/templates/marketplace_taskset.html:17
msgid "License"
msgstr ""
-#: inginious/frontend/templates/marketplace_course.html:23
+#: inginious/frontend/templates/marketplace_taskset.html:23
msgid "Maintainer(s)"
msgstr ""
-#: inginious/frontend/templates/marketplace_course.html:29
+#: inginious/frontend/templates/marketplace_taskset.html:29
#: inginious/frontend/templates/task.html:17
msgid "Author(s)"
msgstr ""
-#: inginious/frontend/templates/marketplace_course.html:35
+#: inginious/frontend/templates/marketplace_taskset.html:35
msgid "Link"
msgstr ""
-#: inginious/frontend/templates/marketplace_course.html:65
-#: inginious/frontend/templates/mycourses.html:110
-msgid "New course id"
+#: inginious/frontend/templates/marketplace_taskset.html:65
+#: inginious/frontend/templates/tasksets.html:75
+msgid "New taskset id"
msgstr ""
#: inginious/frontend/templates/mycourses.html:20
@@ -1548,46 +1673,26 @@ msgid ""
"find new courses on the course list page."
msgstr ""
-#: inginious/frontend/templates/mycourses.html:73
+#: inginious/frontend/templates/mycourses.html:70
msgid "LTI course"
msgstr ""
-#: inginious/frontend/templates/mycourses.html:75
+#: inginious/frontend/templates/mycourses.html:72
msgid "Hidden course"
msgstr ""
-#: inginious/frontend/templates/mycourses.html:79
+#: inginious/frontend/templates/mycourses.html:76
msgid "Administrator"
msgstr ""
-#: inginious/frontend/templates/mycourses.html:81
-msgid "Tutor"
-msgstr ""
-
-#: inginious/frontend/templates/mycourses.html:83
+#: inginious/frontend/templates/mycourses.html:78
msgid "Student"
msgstr ""
-#: inginious/frontend/templates/mycourses.html:91
+#: inginious/frontend/templates/mycourses.html:86
msgid "You are not registered to any course"
msgstr ""
-#: inginious/frontend/templates/mycourses.html:99
-msgid "Course created."
-msgstr ""
-
-#: inginious/frontend/templates/mycourses.html:104
-msgid "Failed to create the course."
-msgstr ""
-
-#: inginious/frontend/templates/mycourses.html:109
-msgid "Course"
-msgstr ""
-
-#: inginious/frontend/templates/mycourses.html:113
-msgid "Create new course"
-msgstr ""
-
#: inginious/frontend/templates/notfound.html:5
#: inginious/frontend/templates/notfound.html:10
msgid "Error 404"
@@ -1607,10 +1712,10 @@ msgstr ""
msgid "Running jobs"
msgstr ""
-#: inginious/frontend/templates/course_admin/subproblems/code.html:19
-#: inginious/frontend/templates/course_admin/subproblems/file.html:23
#: inginious/frontend/templates/queue.html:25
#: inginious/frontend/templates/queue.html:66
+#: inginious/frontend/templates/taskset_admin/subproblems/code.html:19
+#: inginious/frontend/templates/taskset_admin/subproblems/file.html:23
msgid "Type"
msgstr ""
@@ -1618,11 +1723,12 @@ msgstr ""
msgid "Agent name"
msgstr ""
-#: inginious/frontend/templates/course_admin/edit_tabs/basic.html:5
-#: inginious/frontend/templates/course_admin/settings.html:62
-#: inginious/frontend/templates/course_admin/task_edit.html:124
+#: inginious/frontend/templates/course_admin/settings.html:63
#: inginious/frontend/templates/queue.html:27
#: inginious/frontend/templates/queue.html:67
+#: inginious/frontend/templates/taskset_admin/edit_tabs/basic.html:5
+#: inginious/frontend/templates/taskset_admin/settings.html:56
+#: inginious/frontend/templates/taskset_admin/task_edit.html:122
msgid "Name"
msgstr ""
@@ -1744,8 +1850,8 @@ msgstr ""
msgid "Contact"
msgstr ""
-#: inginious/frontend/templates/course_admin/edit_tabs/basic.html:49
#: inginious/frontend/templates/task.html:24
+#: inginious/frontend/templates/taskset_admin/edit_tabs/basic.html:49
msgid "Contact link"
msgstr ""
@@ -1769,8 +1875,8 @@ msgstr ""
msgid "Not yet attempted"
msgstr ""
-#: inginious/frontend/templates/course_admin/student_info.html:73
-#: inginious/frontend/templates/course_admin/submissions.html:133
+#: inginious/frontend/templates/course_admin/student_info.html:74
+#: inginious/frontend/templates/course_admin/submissions.html:134
#: inginious/frontend/templates/task.html:52
msgid "Succeeded"
msgstr ""
@@ -1779,8 +1885,8 @@ msgstr ""
msgid "Waiting for verification"
msgstr ""
-#: inginious/frontend/templates/course_admin/student_info.html:76
-#: inginious/frontend/templates/course_admin/submissions.html:137
+#: inginious/frontend/templates/course_admin/student_info.html:77
+#: inginious/frontend/templates/course_admin/submissions.html:138
#: inginious/frontend/templates/task.html:63
msgid "Failed"
msgstr ""
@@ -1794,8 +1900,9 @@ msgstr ""
msgid "Attempts"
msgstr ""
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/submission_limit.html:7
#: inginious/frontend/templates/task.html:86
+#: inginious/frontend/templates/task_dispensers_admin/config_items/submission_limit.html:8
+#: inginious/frontend/templates/task_dispensers_admin/config_items/submission_limit.html:20
msgid "No limitation"
msgstr ""
@@ -1811,8 +1918,8 @@ msgstr ""
msgid "Category tags"
msgstr ""
-#: inginious/frontend/templates/course_admin/settings.html:49
-#: inginious/frontend/templates/course_admin/submission.html:98
+#: inginious/frontend/templates/course_admin/settings.html:50
+#: inginious/frontend/templates/course_admin/submission.html:99
#: inginious/frontend/templates/task.html:114
msgid "Tags"
msgstr ""
@@ -1829,16 +1936,17 @@ msgid ""
"modifying the \"accessible\" option in the configuration of the task."
msgstr ""
-#: inginious/frontend/templates/course_admin/stats.html:81
-#: inginious/frontend/templates/course_admin/student_list.html:185
+#: inginious/frontend/templates/course_admin/stats.html:82
+#: inginious/frontend/templates/course_admin/student_list.html:186
#: inginious/frontend/templates/course_admin/student_list_table.html:59
-#: inginious/frontend/templates/course_admin/task_dispensers/task_buttons.html:18
#: inginious/frontend/templates/task.html:141
+#: inginious/frontend/templates/task_dispensers_admin/task_buttons.html:26
msgid "View submissions"
msgstr ""
-#: inginious/frontend/templates/course_admin/task_dispensers/task_buttons.html:14
#: inginious/frontend/templates/task.html:145
+#: inginious/frontend/templates/task_dispensers_admin/task_buttons.html:20
+#: inginious/frontend/templates/taskset_admin/settings.html:127
msgid "Edit task"
msgstr ""
@@ -1859,13 +1967,15 @@ msgstr ""
msgid "For evaluation"
msgstr ""
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/evaluation_mode.html:15
#: inginious/frontend/templates/task.html:180
+#: inginious/frontend/templates/task_dispensers_admin/config_items/evaluation_mode.html:8
+#: inginious/frontend/templates/task_dispensers_admin/config_items/evaluation_mode.html:21
msgid "Last submission"
msgstr ""
-#: inginious/frontend/templates/course_admin/task_dispensers/config_items/evaluation_mode.html:9
#: inginious/frontend/templates/task.html:182
+#: inginious/frontend/templates/task_dispensers_admin/config_items/evaluation_mode.html:7
+#: inginious/frontend/templates/task_dispensers_admin/config_items/evaluation_mode.html:15
msgid "Best submission"
msgstr ""
@@ -1975,6 +2085,42 @@ msgstr ""
msgid "Task unavailable"
msgstr ""
+#: inginious/frontend/templates/tasksets.html:19
+msgid ""
+"This page lists all the tasksets you have access to. You can find "
+"instantiated courses on the course list page."
+msgstr ""
+
+#: inginious/frontend/templates/tasksets.html:56
+msgid "Edit taskset"
+msgstr ""
+
+#: inginious/frontend/templates/tasksets.html:60
+#: inginious/frontend/templates/tasksets.html:102
+msgid "Instantiate"
+msgstr ""
+
+#: inginious/frontend/templates/tasksets.html:67
+msgid "You don't own any taskset."
+msgstr ""
+
+#: inginious/frontend/templates/tasksets.html:74
+msgid "Course"
+msgstr ""
+
+#: inginious/frontend/templates/tasksets.html:78
+msgid "Create new taskset"
+msgstr ""
+
+#: inginious/frontend/templates/tasksets.html:88
+msgid "Instantiate a course"
+msgstr ""
+
+#: inginious/frontend/templates/tasksets.html:93
+#: inginious/frontend/templates/tasksets.html:96
+msgid "New courseid"
+msgstr ""
+
#: inginious/frontend/templates/unregister_modal.html:9
msgid "Unregister from {}"
msgstr ""
@@ -2004,7 +2150,7 @@ msgstr ""
#: inginious/frontend/templates/admin/admin_users.html:33
#: inginious/frontend/templates/course_admin/student_list_table.html:16
-#: inginious/frontend/templates/course_admin/submissions.html:55
+#: inginious/frontend/templates/course_admin/submissions.html:56
msgid "username"
msgstr ""
@@ -2014,11 +2160,11 @@ msgid "email address"
msgstr ""
#: inginious/frontend/templates/admin/admin_users.html:38
-#: inginious/frontend/templates/course_admin/stats.html:61
-#: inginious/frontend/templates/course_admin/student_info.html:40
-#: inginious/frontend/templates/course_admin/student_list.html:159
+#: inginious/frontend/templates/course_admin/stats.html:62
+#: inginious/frontend/templates/course_admin/student_info.html:41
+#: inginious/frontend/templates/course_admin/student_list.html:160
#: inginious/frontend/templates/course_admin/student_list_table.html:26
-#: inginious/frontend/templates/course_admin/submissions.html:78
+#: inginious/frontend/templates/course_admin/submissions.html:79
msgid "Download CSV"
msgstr ""
@@ -2027,10 +2173,11 @@ msgid "Bindings"
msgstr ""
#: inginious/frontend/templates/admin/admin_users.html:102
-#: inginious/frontend/templates/course_admin/edit_tabs/files.html:19
-#: inginious/frontend/templates/course_admin/edit_tabs/files.html:53
-#: inginious/frontend/templates/course_admin/subproblems/multiple_choice_templates.html:31
-#: inginious/frontend/templates/course_admin/task_dispensers/section_menu.html:21
+#: inginious/frontend/templates/task_dispensers_admin/section_menu.html:21
+#: inginious/frontend/templates/task_dispensers_admin/util.html:131
+#: inginious/frontend/templates/taskset_admin/edit_tabs/files.html:19
+#: inginious/frontend/templates/taskset_admin/edit_tabs/files.html:53
+#: inginious/frontend/templates/taskset_admin/subproblems/multiple_choice_templates.html:31
msgid "Delete"
msgstr ""
@@ -2056,8 +2203,8 @@ msgid "Add user"
msgstr ""
#: inginious/frontend/templates/admin/admin_users.html:168
-#: inginious/frontend/templates/course_admin/edit_tabs/subproblems.html:22
-#: inginious/frontend/templates/course_admin/task_dispensers/util.html:103
+#: inginious/frontend/templates/task_dispensers_admin/util.html:98
+#: inginious/frontend/templates/taskset_admin/edit_tabs/subproblems.html:22
msgid "Add"
msgstr ""
@@ -2074,330 +2221,326 @@ msgstr ""
msgid "Identifier :"
msgstr ""
-#: inginious/frontend/templates/course_admin/audience_edit.html:18
-#: inginious/frontend/templates/course_admin/student_list.html:43
-#: inginious/frontend/templates/course_admin/student_list.html:78
-#: inginious/frontend/templates/course_admin/student_list.html:110
+#: inginious/frontend/templates/course_admin/audience_edit.html:19
+#: inginious/frontend/templates/course_admin/student_list.html:44
+#: inginious/frontend/templates/course_admin/student_list.html:79
+#: inginious/frontend/templates/course_admin/student_list.html:111
msgid "Audiences"
msgstr ""
-#: inginious/frontend/templates/course_admin/audience_edit.html:20
-#: inginious/frontend/templates/course_admin/audience_edit.html:26
+#: inginious/frontend/templates/course_admin/audience_edit.html:21
+#: inginious/frontend/templates/course_admin/audience_edit.html:27
msgid "Edit audience {}"
msgstr ""
-#: inginious/frontend/templates/course_admin/audience_edit.html:44
-#: inginious/frontend/templates/course_admin/audience_edit.html:63
-#: inginious/frontend/templates/course_admin/student_list.html:67
-#: inginious/frontend/templates/course_admin/student_list.html:257
-#: inginious/frontend/templates/course_admin/student_list.html:276
+#: inginious/frontend/templates/course_admin/audience_edit.html:45
+#: inginious/frontend/templates/course_admin/audience_edit.html:64
+#: inginious/frontend/templates/course_admin/student_list.html:68
+#: inginious/frontend/templates/course_admin/student_list.html:258
+#: inginious/frontend/templates/course_admin/student_list.html:277
msgid "Add student"
msgstr ""
-#: inginious/frontend/templates/course_admin/audience_edit.html:51
+#: inginious/frontend/templates/course_admin/audience_edit.html:52
msgid "Choose student :"
msgstr ""
-#: inginious/frontend/templates/course_admin/audience_edit.html:55
-#: inginious/frontend/templates/course_admin/settings.html:70
-#: inginious/frontend/templates/course_admin/settings.html:76
-#: inginious/frontend/templates/course_admin/student_list.html:65
+#: inginious/frontend/templates/course_admin/audience_edit.html:56
+#: inginious/frontend/templates/course_admin/settings.html:71
+#: inginious/frontend/templates/course_admin/student_list.html:66
+#: inginious/frontend/templates/taskset_admin/settings.html:64
msgid "Enter something here to search for a user"
msgstr ""
-#: inginious/frontend/templates/course_admin/audience_edit.html:74
-#: inginious/frontend/templates/course_admin/task_edit.html:46
+#: inginious/frontend/templates/course_admin/audience_edit.html:75
+#: inginious/frontend/templates/taskset_admin/task_edit.html:44
msgid "Basic settings"
msgstr ""
-#: inginious/frontend/templates/course_admin/audience_edit.html:78
-#: inginious/frontend/templates/course_admin/audience_edit.html:82
+#: inginious/frontend/templates/course_admin/audience_edit.html:79
+#: inginious/frontend/templates/course_admin/audience_edit.html:83
msgid "Audience description"
msgstr ""
-#: inginious/frontend/templates/course_admin/audience_edit.html:85
+#: inginious/frontend/templates/course_admin/audience_edit.html:86
msgid "Delete audience"
msgstr ""
-#: inginious/frontend/templates/course_admin/audience_edit.html:92
+#: inginious/frontend/templates/course_admin/audience_edit.html:93
msgid "Tutor list"
msgstr ""
-#: inginious/frontend/templates/course_admin/audience_edit.html:124
+#: inginious/frontend/templates/course_admin/audience_edit.html:125
msgid "Add tutor"
msgstr ""
-#: inginious/frontend/templates/course_admin/audience_edit.html:137
+#: inginious/frontend/templates/course_admin/audience_edit.html:138
msgid "Student list"
msgstr ""
-#: inginious/frontend/templates/course_admin/audience_edit.html:153
+#: inginious/frontend/templates/course_admin/audience_edit.html:154
msgid "Remove student"
msgstr ""
-#: inginious/frontend/templates/course_admin/audience_edit.html:164
-#: inginious/frontend/templates/course_admin/student_list.html:435
+#: inginious/frontend/templates/course_admin/audience_edit.html:165
+#: inginious/frontend/templates/course_admin/student_list.html:436
msgid "Update"
msgstr ""
-#: inginious/frontend/templates/course_admin/danger_zone.html:41
+#: inginious/frontend/templates/course_admin/danger_zone.html:42
msgid "Archive data"
msgstr ""
-#: inginious/frontend/templates/course_admin/danger_zone.html:44
-#: inginious/frontend/templates/course_admin/danger_zone.html:138
-#: inginious/frontend/templates/course_admin/danger_zone.html:151
-#: inginious/frontend/templates/course_admin/danger_zone.html:159
-#: inginious/frontend/templates/course_admin/danger_zone.html:167
+#: inginious/frontend/templates/course_admin/danger_zone.html:45
+#: inginious/frontend/templates/course_admin/danger_zone.html:139
+#: inginious/frontend/templates/course_admin/danger_zone.html:152
+#: inginious/frontend/templates/course_admin/danger_zone.html:160
+#: inginious/frontend/templates/course_admin/danger_zone.html:168
msgid "Delete course"
msgstr ""
-#: inginious/frontend/templates/course_admin/danger_zone.html:52
-#: inginious/frontend/templates/course_admin/danger_zone.html:63
+#: inginious/frontend/templates/course_admin/danger_zone.html:53
+#: inginious/frontend/templates/course_admin/danger_zone.html:64
msgid "Archive course data"
msgstr ""
-#: inginious/frontend/templates/course_admin/danger_zone.html:55
+#: inginious/frontend/templates/course_admin/danger_zone.html:56
msgid ""
"
This will reset and backup all course data (submissions, audiences, "
"groups, user statistics) from the database.
To confirm your will, "
"please type the course id below :