summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.sampleenv13
-rw-r--r--Dockerfile11
-rw-r--r--README_production.md205
-rw-r--r--README_production.rst254
-rw-r--r--docker/Dockerfile_codeserver20
-rw-r--r--docker/Dockerfile_django23
-rw-r--r--docker/Files/000-default.conf18
-rw-r--r--docker/Files/Docker-script.sh7
-rw-r--r--docker/Files/Start-codeserver.sh6
-rw-r--r--docker/docker-compose.yml41
-rw-r--r--[-rwxr-xr-x]manage.py0
-rw-r--r--online_test/settings.py21
-rw-r--r--requirements/requirements-codeserver.txt1
-rw-r--r--tasks.py197
-rw-r--r--[-rwxr-xr-x]yaksh/code_server.py0
-rw-r--r--[-rwxr-xr-x]yaksh/docs/sample.sh0
-rw-r--r--yaksh/models.py14
-rw-r--r--yaksh/pipeline/user.py7
-rw-r--r--yaksh/settings.py11
-rw-r--r--yaksh/static/yaksh/js/requesthandler.js6
-rw-r--r--yaksh/templates/exam.html4
-rw-r--r--yaksh/templates/yaksh/question.html38
-rw-r--r--yaksh/views.py7
23 files changed, 620 insertions, 284 deletions
diff --git a/.sampleenv b/.sampleenv
new file mode 100644
index 0000000..a31ec1f
--- /dev/null
+++ b/.sampleenv
@@ -0,0 +1,13 @@
+# Django settings
+SECRET_KEY=dUmMy_s3cR3t_k3y
+#DB_ENGINE=mysql
+#DB_NAME=yaksh
+#DB_USER=root
+#DB_PASSWORD=root
+#DB_HOST=yaksh-db
+#DB_PORT=3306
+# Yaksh settings
+N_CODE_SERVERS=5
+#SERVER_POOL_PORT=53579
+#SERVER_HOST_NAME=http://yaksh-codeserver
+#SERVER_TIMEOUT=4
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 6a7d894..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,11 +0,0 @@
-FROM ubuntu:16.04
-MAINTAINER FOSSEE <pythonsupport@fossee.in>
-
-# Update Packages and Install Python & net-tools
-RUN apt-get update && \
-apt-get install -y software-properties-common && \
-echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | debconf-set-selections && \
-add-apt-repository -y ppa:webupd8team/java && \
-apt-get update && \
-apt-get install -y oracle-java8-installer && \
-apt-get install -y sudo python net-tools git python3-pip vim libmysqlclient-dev scilab build-essential python3-numpy python3-scipy ipython3 ipython3-notebook python3-pandas python3-nose
diff --git a/README_production.md b/README_production.md
deleted file mode 100644
index 8b79785..0000000
--- a/README_production.md
+++ /dev/null
@@ -1,205 +0,0 @@
-Production Deployment
-======================
-
-This README provides documentation to help deploy Yaksh in a production environment. If you wish to take Yaksh on a trial run, here is a [Quickstart Guide] (https://github.com/FOSSEE/online_test/blob/master/README.md)
-
-#### Pre-Requisite
-
- 1. Ensure [pip](https://pip.pypa.io/en/latest/installing.html) is installed
- 1. Install dependencies using
- pip install -r requirements.txt
- 1. Install MySql Server
- 1. Install Python MySql support
- 1. Install Apache Server for deployment
-
-#### Configure MySql server
-
- 1. Create a database named ``yaksh`` by following the steps below
-
- $> mysql -u root -p
- mysql> create database yaksh
-
- 1. Add a user named ```yaksh_user``` and give access to it on the database ```yaksh``` by following the steps below
-
- 1. mysql> grant usage on yaksh.* to yaksh_user@localhost identified by 'mysecretpassword';
-
- 1. mysql> grant all privileges on yaksh.* to yaksh_user@localhost;
-
- 1. Add `DATABASE_PASSWORD = 'mysecretpassword'` and `DATABASE_USER = 'yaksh_user'` to online_test/settings.py
-
-To deploy this app follow the steps below:
-
- 1. Clone this repository and cd to the cloned repo.
- $ git clone https://github.com/FOSSEE/online_test.git
-
- 1. Run:
- python manage.py syncdb
-
- 1. Add questions by editing the "docs/sample_questions.py" or any other file in the same format and then run the following:
-
- python manage.py load_exam docs/sample_questions.py
-
- Note that you can supply multiple Python files as arguments and all of
- those will be added to the database.
-
- 1. First run the python server provided. This ensures that the code is executed in a safe environment. Do this like so:
-
- $ sudo python yaksh/code_server.py
-
- Put this in the background once it has started since this will not
- return back the prompt. It is important that the server be running
- *before* students start attempting the exam. Using sudo is
- necessary since the server is run as the user "nobody". This runs
- on the ports configured in the settings.py file in the variable
- "SERVER_PORTS". The "SERVER_TIMEOUT" also can be changed there.
- This is the maximum time allowed to execute the submitted code.
- Note that this will likely spawn multiple processes as "nobody"
- depending on the number of server ports specified.
-
- 1. The ```wsgi.py``` script should make it
- easy to deploy this using mod_wsgi. You will need to add a line of the form:
-
- WSGIScriptAlias / "/online_test/wsgi.py"
-
- to your apache.conf. For more details see the Django docs here:
-
- https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/
-
- 1. Go to http://desired_host_or_ip:desired_port/admin
-
- 1. Login with your credentials and look at the questions and modify if
- needed. Create a new Quiz, set the date and duration or
- activate/deactivate the quiz.
-
- 1. Now ask users to login at:
-
- http://host:port/exam
-
- And you should be all set.
-
- 1. Note that the directory "output" will contain directories, one for each
- user. Users can potentially write output into these that can be used
- for checking later.
-
- 1. As Moderator user you can visit http://host/exam/monitor to view
- results and user data interactively. You could also "grade" the
- papers manually if needed.
-
- 1. You may dump the results and user data using the results2csv and
- dump_user_data commands.
-
- 1. The file docs/sample_questions.py is a template that you can use for your own questions.
-
- 1. Sometimes you might be in the situation where you are not hosted as
- "host.org/exam/" but as "host.org/foo/exam/" for whatever reason. In
- this case edit "settings.py" and set the "URL_ROOT" to the root you
- have to serve at. In the above example for "host.org/foo/exam" set
- URL_ROOT='/foo'.
-
-#### Installation & Usage
-
-To install this app follow the steps below:
-
- 1. Clone this repository and cd to the cloned repo.
- ```$ git clone https://github.com/FOSSEE/online_test.git```
-
- 1. Run:
-
- python manage.py syncdb
-
- 1. Add questions by editing the "docs/sample_questions.py" or any other file in the same format and then run the following:
-
- python manage.py load_exam docs/sample_questions.py
-
- Note that you can supply multiple Python files as arguments and all of
- those will be added to the database.
-
- 1. First run the python server provided. This ensures that the code is executed in a safe environment. Do this like so:
-
- $ sudo python yaksh/code_server.py
-
- Put this in the background once it has started since this will not
- return back the prompt. It is important that the server be running
- *before* students start attempting the exam. Using sudo is
- necessary since the server is run as the user "nobody". This runs
- on the ports configured in the settings.py file in the variable
- "SERVER_PORTS". The "SERVER_TIMEOUT" also can be changed there.
- This is the maximum time allowed to execute the submitted code.
- Note that this will likely spawn multiple processes as "nobody"
- depending on the number of server ports specified.
-
- You can also use a Dockerized code server (see below)
-
- 1. Now, Run:
-
- python manage.py runserver <desired_ip>:<desired_port>
-
- 1. Go to http://desired_host_or_ip:desired_port/admin
-
- 1. Login with your credentials and look at the questions and modify if
- needed. Create a new Quiz, set the date and duration or
- activate/deactivate the quiz.
-
- 1. Now ask users to login at:
-
- http://host:port/exam
-
- And you should be all set.
-
- 1. Note that the directory "output" will contain directories, one for each
- user. Users can potentially write output into these that can be used
- for checking later.
-
- 1. As admin user you can visit http://host/exam/monitor to view
- results and user data interactively. You could also "grade" the
- papers manually if needed.
-
- 1. You may dump the results and user data using the results2csv and
- dump_user_data commands.
-
- 1. The file docs/sample_questions.py is a template that you can use for your own questions.
-
- 1. Sometimes you might be in the situation where you are not hosted as
- "host.org/exam/" but as "host.org/foo/exam/" for whatever reason. In
- this case edit "settings.py" and set the "URL_ROOT" to the root you
- have to serve at. In the above example for "host.org/foo/exam" set
- URL_ROOT='/foo'.
-
-#### Using Dockerized Code Server
-
- 1. Install [Docker](https://github.com/FOSSEE/online_test/blob/master/README.md)
-
- 1. Got to the directory where the project is located
- cd /path/to/online_test
-
- 1. Create a docker image. This may take a few minutes
- docker build -t yaksh_code_server .
-
- 1. Check if the image has been created using the output of,
- docker images
-
- 1. Run the invoke script using the command ```invoke start```
- The command will create and run a new docker container (that is running the code_server.py within it), it will also bind the ports of the host with those of the container
-
-#### Additional commands available
-
-We provide several convenient commands for you to use:
-
- - load_exam : load questions and a quiz from a python file. See
- docs/sample_questions.py
-
- - load_questions_xml : load questions from XML file, see
- docs/sample_questions.xml use of this is deprecated in favor of
- load_exam.
-
- - results2csv : Dump the quiz results into a CSV file for further
- processing.
-
- - dump_user_data : Dump out relevalt user data for either all users or
- specified users.
-
-For more information on these do this:
-
- $ python manage.py help [command]
-
-where [command] is one of the above.
diff --git a/README_production.rst b/README_production.rst
new file mode 100644
index 0000000..a9bd55b
--- /dev/null
+++ b/README_production.rst
@@ -0,0 +1,254 @@
+Production Deployment
+=====================
+
+This README provides documentation to help deploy Yaksh in a production
+environment. If you wish to take Yaksh on a trial run, here is a
+`Quickstart Guide <https://github.com/FOSSEE/online\_test/blob/master/README.rst>`__
+
+###################
+Deploying Locally
+###################
+
+Follow these steps to deploy locally on the server. For deployment instructions using Docker see `Deploying Multiple Dockers <https://github.com/FOSSEE/online_test/blob/add-docker-compose-test/README_production.rst#deploying-multiple-dockers>`__
+
+Pre-Requisite
+^^^^^^^^^^^^^
+
+1. Ensure `pip <https://pip.pypa.io/en/latest/installing.html>`__ is
+ installed
+2. Install dependencies, Run;
+
+ ::
+
+ pip install -r requirements/requirements-py2.txt # For Python 2
+
+ pip3 install -r requirements/requirements-py3.txt # For Python 3
+
+3. Install MySql Server
+4. Install Python MySql support
+5. Install Apache Server for deployment
+
+6. Create a database named ``yaksh`` by following the steps below
+
+ ::
+
+ $> mysql -u root -p
+ $> mysql> create database yaksh
+
+7. Add a user named ``yaksh_user`` and give access to it on the database
+ ``yaksh`` by following the steps below
+
+ ::
+
+ mysql> grant usage on yaksh to yaksh_user@localhost identified
+ by 'mysecretpassword';
+
+ mysql> grant all privileges on yaksh to yaksh_user@localhost;
+
+8. Add ``DATABASE_PASSWORD = 'mysecretpassword'`` and
+ ``DATABASE_USER = 'yaksh_user'`` to online\_test/settings.py
+
+
+Installation & Usage
+^^^^^^^^^^^^^^^^^^^^
+
+To install this app follow the steps below:
+
+1. Clone this repository and cd to the cloned repo.
+
+ ::
+
+ $ git clone https://github.com/FOSSEE/online_test.git
+
+2. Rename the ``.sampleenv`` to ``.env``
+
+3. In the ``.env`` file, uncomment the following and replace the values (please keep the remaining settings as is);
+
+ ::
+
+ DB_ENGINE=mysql # Or psycopg (postgresql), sqlite3 (SQLite)
+ DB_NAME=yaksh
+ DB_USER=root
+ DB_PASSWORD=mypassword # Or the password used while creating a Database
+ DB_PORT=3306
+
+4. Run:
+
+ ::
+
+ $ python manage.py makemigrations yaksh
+
+ $ python manage.py migrate yaksh
+
+5. Run the python server provided. This ensures that the code is
+ executed in a safe environment. Do this like so:
+
+ ::
+
+ $ sudo python -m yaksh.code_server # For Python 2.x
+
+
+ $ sudo python3 -m yaksh.code_server # For Python 3.x
+
+ Put this in the background once it has started since this will not
+ return back the prompt. It is important that the server be running
+ *before* students start attempting the exam. Using sudo is necessary
+ since the server is run as the user "nobody". This runs the number
+ ports configured in the settings.py file in the variable
+ "N\_CODE\_SERVERS". The "SERVER\_TIMEOUT" also can be changed there.
+ This is the maximum time allowed to execute the submitted code. Note
+ that this will likely spawn multiple processes as "nobody" depending
+ on the number of server ports specified.
+
+ You can also use a Dockerized code server, see `Dockerized Code Server <https://github.com/FOSSEE/online_test/blob/add-docker-compose-test/README_production.rst#using-dockerized-code-server>`__
+
+
+6. The ``wsgi.py`` script should make it easy to deploy this using
+ mod\_wsgi. You will need to add a line of the form:
+
+ ::
+
+ WSGIScriptAlias / "/online_test/wsgi.py"
+
+ to your apache.conf. For more details see the Django docs here:
+
+ https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/
+
+7. Create a Superuser/Administrator:
+
+ ::
+
+ python manage.py createsuperuser
+
+8. Go to http://desired\_host\_or\_ip:desired\_port/exam
+
+ And you should be all set.
+
+9. Note that the directory "output" will contain directories, one for
+ each user. Users can potentially write output into these that can be
+ used for checking later.
+
+10. As admin user you can visit http://desired\_host\_or\_ip/exam/monitor to view results and user data interactively. You could also "grade" the papers manually if needed.
+
+.. _dockerized-code-server:
+
+Using Dockerized Code Server
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+1. Install
+ `Docker <https://docs.docker.com/engine/installation/>`__
+
+2. Go to the directory where the project is located
+
+ ::
+
+ cd /path/to/online_test
+
+3. Create a docker image. This may take a few minutes,
+
+ ::
+
+ docker build -t yaksh_code_server -f ./docker/Dockerfile_codeserver
+
+4. Check if the image has been created using the output of ``docker
+ images``
+
+5. Run the invoke script using the command ``invoke start`` The command
+ will create and run a new docker container (that is running the
+ code\_server.py within it), it will also bind the ports of the host
+ with those of the container
+
+6. You can use ``invoke --list`` to get a list of all the available commands
+
+
+.. _deploying-multiple-dockers:
+
+######################################
+Deploying Multiple Dockers
+######################################
+
+Follow these steps to deploy and run the Django Server, MySQL instance and Code Server in seperate Docker instances.
+
+1. Install `Docker <https://docs.docker.com/engine/installation/>`__
+
+2. Install `Docker Compose <https://docs.docker.com/compose/install/>`__
+
+3. Rename the ``.sampleenv`` to ``.env``
+
+4. In the ``.env`` file, uncomment all the values and keep the default values as is.
+
+5. Go to the ``docker`` directory where the project is located:
+
+ ::
+
+ cd /path/to/online_test/docker
+
+6. Build the docker images
+
+ ::
+
+ invoke build
+
+7. Run the containers and scripts necessary to deploy the web
+ application
+
+ ::
+
+ invoke begin
+
+8. Make sure that all the containers are ``Up`` and stable
+
+ ::
+
+ invoke status
+
+8. Run the containers and scripts necessary to deploy the web
+ application, ``--fixtures`` allows you to load fixtures.
+
+ ::
+
+ invoke deploy --fixtures
+
+10. Stop the containers, you can use ``invoke restart`` to restart the containers without removing them
+
+ ::
+
+ invoke halt
+
+11. Remove the containers
+
+ ::
+
+ invoke remove
+
+12. You can use ``invoke --list`` to get a list of all the available commands
+
+
+.. _add-commands:
+
+######################################
+Additional commands available
+######################################
+
+We provide several convenient commands for you to use:
+
+- load\_exam : load questions and a quiz from a python file. See
+ docs/sample\_questions.py
+
+- load\_questions\_xml : load questions from XML file, see
+ docs/sample\_questions.xml use of this is deprecated in favor of
+ load\_exam.
+
+- results2csv : Dump the quiz results into a CSV file for further
+ processing.
+
+- dump\_user\_data : Dump out relevalt user data for either all users
+ or specified users.
+
+For more information on these do this:
+
+::
+
+ $ python manage.py help [command]
+
+where [command] is one of the above.
diff --git a/docker/Dockerfile_codeserver b/docker/Dockerfile_codeserver
new file mode 100644
index 0000000..1f030d5
--- /dev/null
+++ b/docker/Dockerfile_codeserver
@@ -0,0 +1,20 @@
+FROM ubuntu:16.04
+
+MAINTAINER FOSSEE <pythonsupport@fossee.in>
+
+RUN apt-get update && \
+apt-get install git python3-pip libmysqlclient-dev sudo default-jre default-jdk -y
+
+VOLUME /Sites/online_test
+
+ADD Files/requirements-* /tmp/
+
+RUN pip3 install -r /tmp/requirements-codeserver.txt && mkdir -p /Sites/online_test/yaksh_data/output /Sites/online_test/yaksh_data/data
+
+WORKDIR /Sites/online_test
+
+ADD Files/Start-codeserver.sh /Sites
+
+EXPOSE 53579
+
+CMD [ "/bin/bash" , "/Sites/Start-codeserver.sh" ]
diff --git a/docker/Dockerfile_django b/docker/Dockerfile_django
new file mode 100644
index 0000000..daddc45
--- /dev/null
+++ b/docker/Dockerfile_django
@@ -0,0 +1,23 @@
+FROM ubuntu:16.04
+
+MAINTAINER FOSSEE <pythonsupport@fossee.in>
+
+RUN apt-get update -y && apt-get install git python3-pip vim libmysqlclient-dev sudo -y
+
+RUN apt-get install apache2 libapache2-mod-wsgi-py3 python3-django -y && mkdir -p /Sites/online_test
+
+VOLUME /Sites/online_test
+
+ADD Files/requirements-* /tmp/
+
+RUN cd /Sites/online_test && pip3 install -r /tmp/requirements-py3.txt
+
+ADD Files/000-default.conf /etc/apache2/sites-enabled/
+
+ADD Files/Docker-script.sh /Sites/Docker-script.sh
+
+EXPOSE 80
+
+WORKDIR /Sites/online_test
+
+CMD [ "/bin/bash" , "/Sites/Docker-script.sh" ]
diff --git a/docker/Files/000-default.conf b/docker/Files/000-default.conf
new file mode 100644
index 0000000..6bcb382
--- /dev/null
+++ b/docker/Files/000-default.conf
@@ -0,0 +1,18 @@
+<VirtualHost *:80>
+ ServerName localhost
+ ServerAdmin webmaster@localhost
+ DocumentRoot /Sites/online_test
+ WSGIDaemonProcess yaksh threads=5 python-path=/usr/local/lib/python3.5/dist-packages home=/Sites/online_test
+ WSGIScriptAlias / /Sites/online_test/online_test/wsgi.py
+ WSGIProcessGroup yaksh
+ Alias /static/ /Sites/online_test/yaksh/static/
+ <Directory /Sites/online_test >
+ WSGIProcessGroup yaksh
+ WSGIApplicationGroup %{GLOBAL}
+ Order deny,allow
+ Allow from all
+ Require all granted
+ </Directory>
+ ErrorLog ${APACHE_LOG_DIR}/error.log
+ CustomLog ${APACHE_LOG_DIR}/access.log combined
+</VirtualHost>
diff --git a/docker/Files/Docker-script.sh b/docker/Files/Docker-script.sh
new file mode 100644
index 0000000..dc35a3f
--- /dev/null
+++ b/docker/Files/Docker-script.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+chown -R www-data /Sites/online_test
+chown -R www-data /Sites/online_test/yaksh
+chown -R nobody /Sites/online_test/yaksh_data
+chmod -R 664 /Sites/online_test
+chmod -R +X /Sites
+/usr/sbin/apache2ctl -D FOREGROUND
diff --git a/docker/Files/Start-codeserver.sh b/docker/Files/Start-codeserver.sh
new file mode 100644
index 0000000..019a31d
--- /dev/null
+++ b/docker/Files/Start-codeserver.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+chown -R nobody /Sites/online_test/yaksh_data
+chmod -R a+rwX yaksh_data/output
+chmod -R a+rX yaksh_data/data
+chmod -R o-w yaksh_data/data
+/usr/bin/sudo -su nobody python3 -m yaksh.code_server
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
new file mode 100644
index 0000000..7ee3860
--- /dev/null
+++ b/docker/docker-compose.yml
@@ -0,0 +1,41 @@
+version: '2'
+services:
+ yaksh-django:
+ build:
+ context: .
+ dockerfile: Dockerfile_django
+ image: yaksh.django
+ container_name: yaksh_django
+ volumes:
+ - ../.:/Sites/online_test
+ depends_on:
+ - yaksh-db
+ - yaksh-codeserver
+ links:
+ - yaksh-db
+ - yaksh-codeserver
+ ports:
+ - 8000:80
+ restart: always
+
+
+ yaksh-codeserver:
+ build:
+ context: .
+ dockerfile: Dockerfile_codeserver
+ image: yaksh.codeserver
+ container_name: yaksh_codeserver
+ volumes:
+ - ..:/Sites/online_test
+ restart: always
+
+
+ yaksh-db:
+ image: mariadb:10.2
+ container_name: yaksh_db
+ volumes:
+ - ./mysql:/var/lib/mysql
+ environment:
+ MYSQL_ROOT_PASSWORD: root
+ MYSQL_DATABASE: yaksh
+ restart: always
diff --git a/manage.py b/manage.py
index 1addc41..1addc41 100755..100644
--- a/manage.py
+++ b/manage.py
diff --git a/online_test/settings.py b/online_test/settings.py
index 790083e..c55a056 100644
--- a/online_test/settings.py
+++ b/online_test/settings.py
@@ -8,8 +8,10 @@ For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.6/ref/settings/
"""
from yaksh.pipeline.settings import AUTH_PIPELINE
-# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
+from decouple import config
+
+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
# The directory where user data can be saved. This directory will be
@@ -21,12 +23,11 @@ OUTPUT_DIR = os.path.join(BASE_DIR, "yaksh_data", "output")
# See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = '0=fsi3g5dw*7ze1cyh441_e^5^$2ay@&z(5(n7mhir0xb267=6'
+SECRET_KEY = config('SECRET_KEY', default='dUmMy_s3cR3t_k3y')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
-
ALLOWED_HOSTS = []
URL_ROOT = ''
@@ -64,11 +65,17 @@ WSGI_APPLICATION = 'online_test.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.6/ref/settings/#databases
-
DATABASES = {
'default': {
- 'ENGINE': 'django.db.backends.sqlite3',
- 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+ 'ENGINE': 'django.db.backends.{0}'.format(
+ config('DB_ENGINE', default='sqlite3')
+ ),
+ 'NAME': config('DB_NAME', default=os.path.join(BASE_DIR, 'db.sqlite3')),
+ # The following settings are not used with sqlite3:
+ 'USER': config('DB_USER', default=''),
+ 'PASSWORD': config('DB_PASSWORD', default=''),
+ 'HOST': config('DB_HOST', default='localhost'), # Empty for localhost through domain sockets or '1$
+ 'PORT': config('DB_PORT', default=''),
},
}
@@ -100,6 +107,8 @@ MEDIA_URL = "/data/"
MEDIA_ROOT = os.path.join(BASE_DIR, "yaksh_data", "data")
+STATIC_ROOT='yaksh/static/'
+
# Set this varable to <True> if smtp-server is not allowing to send email.
EMAIL_USE_TLS = False
diff --git a/requirements/requirements-codeserver.txt b/requirements/requirements-codeserver.txt
index a4f419c..e9585fa 100644
--- a/requirements/requirements-codeserver.txt
+++ b/requirements/requirements-codeserver.txt
@@ -1,4 +1,5 @@
pytest
+python-decouple
six
requests
tornado
diff --git a/tasks.py b/tasks.py
index 68d9967..8396723 100644
--- a/tasks.py
+++ b/tasks.py
@@ -1,7 +1,13 @@
from __future__ import print_function
+import os
+import sys
+import shutil
+from distutils.dir_util import copy_tree
+from distutils.file_util import copy_file
+
import invoke
from invoke import task
-import os
+
from yaksh.settings import SERVER_POOL_PORT
SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
@@ -9,6 +15,7 @@ TARGET_CONTAINER_NAME = 'yaksh_code_server'
SRC_IMAGE_NAME = 'fossee/yaksh_codeserver'
CHECK_FILE = 'server_running.txt'
CHECK_FILE_PATH = os.path.join(SCRIPT_DIR, 'yaksh_data', CHECK_FILE)
+OS_NAME = sys.platform
def create_dir(path):
@@ -19,14 +26,36 @@ def remove_check_file(path):
if os.path.isfile(path):
os.remove(path)
+def remove_dir(path):
+ if os.path.isdir(path):
+ shutil.rmtree(path)
+
+def run_as(os_name):
+ if os_name.startswith('linux') or os_name == 'darwin' or os_name.startswith('freebsd'):
+ return 'sudo'
+ else: # For os_name = 'Win32'
+ return None
+
+def get_cmd(run_as_cmd, base_cmd):
+ if run_as_cmd:
+ return '{0} {1}'.format(run_as_cmd, base_cmd)
+ else:
+ return base_cmd
+
@task
def setupdb(ctx):
print("** Setting up & migrating database **")
ctx.run("python manage.py makemigrations")
ctx.run("python manage.py migrate")
+ print("** Done! Migrations complete **")
+
+@task
+def loadfixtures(ctx):
+ print("** Loading fixtures into database **")
ctx.run("python manage.py loaddata demo_fixtures.json")
+ print("** Done! Loaded fixtures into database **")
-@task(setupdb)
+@task(setupdb, loadfixtures)
def serve(ctx):
print("** Running the Django web server. Press Ctrl-C to Exit **")
ctx.run("python manage.py runserver")
@@ -34,16 +63,22 @@ def serve(ctx):
@task
def clean(ctx):
print("** Discarding database **")
- ctx.run("rm -rf {0}".format(os.path.join(SCRIPT_DIR, 'db.sqlite3')))
+ remove_check_file(os.path.join(SCRIPT_DIR, 'db.sqlite3'))
@task
def getimage(ctx, image=SRC_IMAGE_NAME):
try:
- result = ctx.run("sudo docker inspect {0}".format(image), hide=True)
+ base_cmd = "docker inspect {0}".format(image)
+ run_as_cmd = run_as(OS_NAME)
+ cmd = get_cmd(run_as_cmd, base_cmd)
+ ctx.run(cmd, hide=True)
except invoke.exceptions.Failure:
print("The docker image {0} does not exist locally".format(image))
print("\n** Pulling latest image <{0}> from docker hub **".format(image))
- ctx.run("sudo docker pull {0}".format(image))
+ base_cmd = "docker pull {0}".format(image)
+ run_as_cmd = run_as(OS_NAME)
+ cmd = get_cmd(run_as_cmd, base_cmd)
+ ctx.run(cmd)
print("\n** Done! Successfully pulled latest image <{0}> **".format(image))
@task
@@ -52,10 +87,12 @@ def start(ctx, ports=SERVER_POOL_PORT, image=SRC_IMAGE_NAME, unsafe=False,
if unsafe:
with ctx.cd(SCRIPT_DIR):
print("** Initializing local code server **")
- ctx.run("sudo python{0} -m yaksh.code_server".format(
- version
- )
+ base_cmd = "python{0} -m yaksh.code_server".format(
+ version
)
+ run_as_cmd = run_as(OS_NAME)
+ cmd = get_cmd(run_as_cmd, base_cmd)
+ ctx.run(cmd)
else:
cmd_params = {'ports': ports,
'image': SRC_IMAGE_NAME,
@@ -74,39 +111,149 @@ def start(ctx, ports=SERVER_POOL_PORT, image=SRC_IMAGE_NAME, unsafe=False,
create_dir(os.path.join(SCRIPT_DIR, 'yaksh_data', 'data'))
create_dir(os.path.join(SCRIPT_DIR, 'yaksh_data', 'output'))
- ctx.run('cp -r {0} {1}'.format(
- os.path.join(SCRIPT_DIR, 'yaksh'),
- os.path.join(SCRIPT_DIR, 'yaksh_data')
- )
+ copy_tree(
+ os.path.join(SCRIPT_DIR, 'yaksh'),
+ os.path.join(SCRIPT_DIR, 'yaksh_data', 'yaksh')
)
- ctx.run('cp {0} {1}'.format(
- os.path.join(SCRIPT_DIR, 'requirements', 'requirements-codeserver.txt'),
- os.path.join(SCRIPT_DIR, 'yaksh_data')
- )
+
+ copy_file(
+ os.path.join(SCRIPT_DIR, 'requirements', 'requirements-codeserver.txt'),
+ os.path.join(SCRIPT_DIR, 'yaksh_data')
)
print("** Initializing code server within docker container **")
- ctx.run(
- "sudo docker run \
+ base_cmd = "docker run \
-dp {ports}:{ports} --name={name} \
-v {vol_mount}:{vol_mount} \
-w {vol_mount} \
{image} {command}".format(**cmd_params)
- )
+ run_as_cmd = run_as(OS_NAME)
+ cmd = get_cmd(run_as_cmd, base_cmd)
+ ctx.run(cmd)
while not os.path.isfile(CHECK_FILE_PATH):
print("** Checking code server status. Press Ctrl-C to exit **\r", end="")
- print("** Code server is up and running successfully **")
+ print("\n** Code server is up and running successfully **")
@task
def stop(ctx, container=TARGET_CONTAINER_NAME, hide=True):
- result = ctx.run("sudo docker ps -q --filter='name={0}'".format(container))
+ base_filter_cmd = "docker ps -q --filter='name={0}'".format(container)
+ run_as_cmd = run_as(OS_NAME)
+ cmd = get_cmd(run_as_cmd, base_filter_cmd)
+ result = ctx.run(cmd)
+
remove_check_file(CHECK_FILE_PATH)
if result.stdout:
- print ("** Discarding the docker container <{0}>".format(container))
- ctx.run("sudo docker stop {0}".format(container))
- ctx.run("sudo docker rm {0}".format(container))
- print ("** Done! Discarded the docker container <{0}>".format(container))
+ print ("** Stopping the docker container <{0}> **".format(container))
+ base_stop_cmd = "docker stop {0}".format(container)
+ cmd = get_cmd(run_as_cmd, base_stop_cmd)
+ ctx.run(cmd)
+ print ("** Done! Stopped the docker container <{0}> **".format(container))
+
+ print ("** Discarding the docker container <{0}> **".format(container))
+ base_rm_cmd = "docker rm {0}".format(container)
+ cmd = get_cmd(run_as_cmd, base_rm_cmd)
+ ctx.run(cmd)
+ print ("** Done! Discarded the docker container <{0}> **".format(container))
else:
print("** Docker container <{0}> not found **".format(container))
+
+# Docker compose based deployment
+@task
+def build(ctx):
+ run_as_cmd = run_as(OS_NAME)
+
+ copy_tree(
+ os.path.join(SCRIPT_DIR, 'requirements'),
+ os.path.join(SCRIPT_DIR, 'docker', 'Files')
+ )
+
+ base_build_cmd = "docker-compose build --no-cache"
+ cmd = get_cmd(run_as_cmd, base_build_cmd)
+ print ("** Building docker images **")
+ ctx.run(cmd)
+ print ("** Done! Built the docker images **")
+
+ base_build_cmd = "docker pull mariadb:10.2 "
+ cmd = get_cmd(run_as_cmd, base_build_cmd)
+ print ("** Pulling maria-db base image **")
+ ctx.run(cmd)
+ print ("** Done! Pulled maria-db base image **")
+
+@task
+def begin(ctx):
+ print("** Initializing docker containers **")
+ base_cmd = "docker-compose up -d"
+ run_as_cmd = run_as(OS_NAME)
+ cmd = get_cmd(run_as_cmd, base_cmd)
+ ctx.run(cmd)
+ print ("** Done! Initialized the docker containers **")
+
+@task
+def deploy(ctx, fixtures=False, static=True):
+ run_as_cmd = run_as(OS_NAME)
+
+ print("** Setting up & migrating database **")
+ base_make_migrate_cmd = "docker exec -i yaksh_django" \
+ " python3 manage.py makemigrations"
+ cmd = get_cmd(run_as_cmd, base_make_migrate_cmd)
+ ctx.run(cmd)
+
+ base_migrate_cmd = "docker exec -i yaksh_django" \
+ " python3 manage.py migrate"
+ cmd = get_cmd(run_as_cmd, base_migrate_cmd)
+ ctx.run(cmd)
+ print("** Done! Migrations complete **")
+
+ if fixtures:
+ base_fixture_cmd = "docker exec -i yaksh_django" \
+ " python3 manage.py loaddata demo_fixtures.json"
+ cmd = get_cmd(run_as_cmd, base_fixture_cmd)
+ print("** Loading fixtures into database **")
+ ctx.run(cmd)
+ print("** Done! Loaded fixtures into database **")
+
+ if static:
+ base_static_cmd = "docker exec -i yaksh_django python3 manage.py collectstatic"
+ cmd = get_cmd(run_as_cmd, base_static_cmd)
+ print ("** Setting up static assets **")
+ ctx.run(cmd)
+ print ("** Done! Set up static assets **")
+
+@task
+def status(ctx):
+ run_as_cmd = run_as(OS_NAME)
+ base_cmd = "docker-compose ps"
+ cmd = get_cmd(run_as_cmd, base_cmd)
+ print ("** Fetching container status **")
+ ctx.run(cmd)
+
+@task
+def halt(ctx):
+ run_as_cmd = run_as(OS_NAME)
+ base_cmd = "docker-compose stop"
+ cmd = get_cmd(run_as_cmd, base_cmd)
+ print ("** Stopping containers **")
+ ctx.run(cmd)
+ print ("** Done! Stopped containers **")
+
+@task
+def restart(ctx):
+ run_as_cmd = run_as(OS_NAME)
+ base_cmd = "docker-compose restart"
+ cmd = get_cmd(run_as_cmd, base_cmd)
+ print ("** Restarting containers **")
+ ctx.run(cmd)
+ print ("** Done! Restarted containers **")
+
+@task(halt)
+def remove(ctx):
+ run_as_cmd = run_as(OS_NAME)
+ base_cmd = "docker-compose rm --force"
+ cmd = get_cmd(run_as_cmd, base_cmd)
+ sql_dir = os.path.join(SCRIPT_DIR, 'docker', 'mysql')
+ print ("** Removing containers **")
+ remove_dir(sql_dir)
+ ctx.run(cmd)
+ print ("** Done! Removed containers **")
diff --git a/yaksh/code_server.py b/yaksh/code_server.py
index 75dd9b2..75dd9b2 100755..100644
--- a/yaksh/code_server.py
+++ b/yaksh/code_server.py
diff --git a/yaksh/docs/sample.sh b/yaksh/docs/sample.sh
index e935cb3..e935cb3 100755..100644
--- a/yaksh/docs/sample.sh
+++ b/yaksh/docs/sample.sh
diff --git a/yaksh/models.py b/yaksh/models.py
index 208c855..d4a73fa 100644
--- a/yaksh/models.py
+++ b/yaksh/models.py
@@ -29,10 +29,10 @@ import tempfile
from textwrap import dedent
from ast import literal_eval
from .file_utils import extract_files, delete_files
-from yaksh.code_server import(submit,
- get_result as get_result_from_code_server
- )
-from yaksh.settings import SERVER_POOL_PORT
+from yaksh.code_server import (
+ submit, get_result as get_result_from_code_server
+)
+from yaksh.settings import SERVER_POOL_PORT, SERVER_HOST_NAME
from django.conf import settings
from django.forms.models import model_to_dict
@@ -330,7 +330,7 @@ class Quiz(models.Model):
duration=30, active=True,
attempts_allowed=-1, time_between_attempts=0,
description='Yaksh Demo quiz', pass_criteria=0,
- creator=user
+ creator=user, instructions="<b>This is a demo quiz.</b>"
)
return demo_quiz
@@ -1718,7 +1718,7 @@ class AnswerPaper(models.Model):
elif question.type == 'code' or question.type == "upload":
user_dir = self.user.profile.get_user_dir()
- url = 'http://localhost:%s' % server_port
+ url = '{0}:{1}'.format(SERVER_HOST_NAME, server_port)
submit(url, uid, json_data, user_dir)
result = {'uid': uid, 'status': 'running'}
return result
@@ -1753,7 +1753,7 @@ class AnswerPaper(models.Model):
server_port=server_port
)
if question.type == "code":
- url = 'http://localhost:%s' % server_port
+ url = '{0}:{1}'.format(SERVER_HOST_NAME, server_port)
check_result = get_result_from_code_server(url, result['uid'],
block=True
)
diff --git a/yaksh/pipeline/user.py b/yaksh/pipeline/user.py
index 4aecd95..d5ec292 100644
--- a/yaksh/pipeline/user.py
+++ b/yaksh/pipeline/user.py
@@ -1,8 +1,11 @@
from yaksh.models import Profile
-#from django.contrib.auth.models import User
+
def save_profile(backend, user, response, *args, **kwargs):
if not hasattr(user, 'profile'):
profile = Profile.objects.create(user=user)
profile.roll_number = profile.id
- profile.save()
+ else:
+ profile = Profile.objects.get(user=user)
+ profile.is_email_verified = True
+ profile.save()
diff --git a/yaksh/settings.py b/yaksh/settings.py
index d500d93..d895d19 100644
--- a/yaksh/settings.py
+++ b/yaksh/settings.py
@@ -2,16 +2,21 @@
settings for yaksh app.
"""
+from decouple import config
+
# The number of code server processes to run..
-N_CODE_SERVERS = 5
+N_CODE_SERVERS = config('N_CODE_SERVERS', default=5, cast=int)
# The server pool port. This is the server which returns available server
# ports so as to minimize load. This is some random number where no other
# service is running. It should be > 1024 and less < 65535 though.
-SERVER_POOL_PORT = 53579
+SERVER_POOL_PORT = config('SERVER_POOL_PORT', default=55555, cast=int)
+
+SERVER_HOST_NAME = config('SERVER_HOST_NAME', default='http://localhost')
+#'localhost'
# Timeout for the code to run in seconds. This is an integer!
-SERVER_TIMEOUT = 4
+SERVER_TIMEOUT = config('SERVER_TIMEOUT', default=4, cast=int)
# The root of the URL, for example you might be in the situation where you
# are not hosted as host.org/exam/ but as host.org/foo/exam/ for whatever
diff --git a/yaksh/static/yaksh/js/requesthandler.js b/yaksh/static/yaksh/js/requesthandler.js
index f98ab12..ec2391a 100644
--- a/yaksh/static/yaksh/js/requesthandler.js
+++ b/yaksh/static/yaksh/js/requesthandler.js
@@ -37,7 +37,9 @@ function unlock_screen() {
}
function show_solution() {
- document.getElementById("solution").style.display = "block";
+ var solution = document.getElementById("solution");
+ solution.style.display = "block";
+ solution.className ="well well-sm";
document.getElementById("skip_ex").style.visibility = "visible";
}
@@ -164,7 +166,7 @@ if (question_type == 'upload' || question_type == 'code') {
var data = $(this).serializeArray();
}
else if (question_type == "upload"){
- var data = new FormData(getElementById("code"));
+ var data = new FormData(document.getElementById("code"));
}
ajax_check_code($(this).attr("action"), "POST", "html", data, null)
e.preventDefault(); // To stop the default form submission.
diff --git a/yaksh/templates/exam.html b/yaksh/templates/exam.html
index ce1d3b8..012adbe 100644
--- a/yaksh/templates/exam.html
+++ b/yaksh/templates/exam.html
@@ -60,6 +60,10 @@
{% else %}
{% if qid.id == question.id %}
<li class="active"><a style="width:25%" data-toggle="tooltip" title="{{ qid.description|striptags }}">{{ forloop.counter }}</a></li>
+ {% elif qid in paper.get_questions_answered %}
+ <li><a style="background-color:#B4B8BA; width:25%" href="#" data-toggle="tooltip"
+ onclick="call_skip('{{ URL_ROOT }}/exam/{{ question.id }}/skip/{{ qid.id }}/{{ paper.attempt_number }}/{{ module.id }}/{{ paper.question_paper.id }}/{{course.id}}/')"
+ title="{{ qid.description }}">{{ forloop.counter }}</a></li>
{% else %}
<li class="disabled"><a style="width:25%" data-toggle="tooltip" title="{{ qid.description|striptags }}">{{ forloop.counter }}</a></li>
{% endif %}
diff --git a/yaksh/templates/yaksh/question.html b/yaksh/templates/yaksh/question.html
index 96c8e6e..9d6ce48 100644
--- a/yaksh/templates/yaksh/question.html
+++ b/yaksh/templates/yaksh/question.html
@@ -151,21 +151,6 @@ question_type = "{{ question.type }}"
{% endblock %}
{% block main %}
- <p id="status"></p>
- {% if notification %}
- {% if question.type == "code" %}
- <div id="notification" class="alert alert-success" role="alert">
- <strong>Note:</strong> {{ notification }}
- </div>
- {% else %}
- <div id="notification" class="alert alert-warning" role="alert">
- <strong>Note:</strong> {{ notification }}
- </div>
- {% endif %}
- {% else %}
- <div id="notification" role="alert">
- </div>
- {% endif %}
<form id="code" action="{{URL_ROOT}}/exam/{{ question.id }}/check/{{ paper.attempt_number }}/{{ module.id }}/{{ paper.question_paper.id }}/{{course.id}}/" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type=hidden name="question_id" id="question_id" value={{ question.id }}></input>
@@ -204,22 +189,20 @@ question_type = "{{ question.type }}"
<h5><a href="{{f_name.file.url}}">{{f_name.file.name}}</a></h5>
{% endfor %}
{% endif %}
-
+ </div>
+ <br/>
{% if quiz.is_exercise %}
{% if can_skip %}
- <div id="solution">
+ <div class = "well well-sm" id="solution">
{% else %}
<div id="solution" style="display:none">
{% endif %}
{% if question.solution %}
<h4><u> Solution by teacher</u></h4>
- {% else %}
- <h4><u> No solution provided by teacher </u></h4>
{% endif%}
<font size=3 face=arial> {{ question.solution|safe }} </font>
</div>
{% endif %}
- </div>
<div class="panel-body">
{% if question.type == "mcq" %}
{% for test_case in test_cases %}
@@ -311,6 +294,21 @@ question_type = "{{ question.type }}"
</div>
</div>
<br/>
+ <p id="status"></p>
+ {% if notification %}
+ {% if question.type == "code" %}
+ <div id="notification" class="alert alert-success" role="alert">
+ <strong>Note:</strong> {{ notification }}
+ </div>
+ {% else %}
+ <div id="notification" class="alert alert-warning" role="alert">
+ <strong>Note:</strong> {{ notification }}
+ </div>
+ {% endif %}
+ {% else %}
+ <div id="notification" role="alert">
+ </div>
+ {% endif %}
{% if question.type == 'code' or question.type == 'upload' %}
<div class="row" id="error_panel"></div>
{% endif %}
diff --git a/yaksh/views.py b/yaksh/views.py
index 99f81b8..49249ca 100644
--- a/yaksh/views.py
+++ b/yaksh/views.py
@@ -35,7 +35,7 @@ except ImportError:
from io import BytesIO as string_io
import re
# Local imports.
-from yaksh.code_server import get_result as get_result_from_code_server, SERVER_POOL_PORT
+from yaksh.code_server import get_result as get_result_from_code_server
from yaksh.models import (
Answer, AnswerPaper, AssignmentUpload, Course, FileUpload, FloatTestCase,
HookTestCase, IntegerTestCase, McqTestCase, Profile,
@@ -51,6 +51,7 @@ from yaksh.forms import (
UploadFileForm, get_object_form, FileForm, QuestionPaperForm, LessonForm,
LessonFileForm, LearningModuleForm, ExerciseForm
)
+from yaksh.settings import SERVER_POOL_PORT, SERVER_HOST_NAME
from .settings import URL_ROOT
from .file_utils import extract_files, is_csv
from .send_emails import send_user_mail, generate_activation_key, send_bulk_mail
@@ -784,7 +785,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None,
)
if current_question.type in ['code', 'upload']:
if paper.time_left() <= 0 and not paper.question_paper.quiz.is_exercise:
- url = 'http://localhost:%s' % SERVER_POOL_PORT
+ url = '{0}:{1}'.format(SERVER_HOST_NAME, SERVER_POOL_PORT)
result_details = get_result_from_code_server(url, uid, block=True)
result = json.loads(result_details.get('result'))
next_question, error_message, paper = _update_paper(request, uid,
@@ -809,7 +810,7 @@ def check(request, q_id, attempt_num=None, questionpaper_id=None,
@csrf_exempt
def get_result(request, uid, course_id, module_id):
result = {}
- url = 'http://localhost:%s' % SERVER_POOL_PORT
+ url = '{0}:{1}'.format(SERVER_HOST_NAME, SERVER_POOL_PORT)
result_state = get_result_from_code_server(url, uid)
result['status'] = result_state.get('status')
if result['status'] == 'done':