summaryrefslogtreecommitdiff
path: root/choice_seeker
diff options
context:
space:
mode:
Diffstat (limited to 'choice_seeker')
-rw-r--r--choice_seeker/README.rst4
-rw-r--r--choice_seeker/__init__.py0
-rw-r--r--choice_seeker/allotter/__init__.py2
-rw-r--r--choice_seeker/allotter/admin.py7
-rw-r--r--choice_seeker/allotter/forms.py110
-rw-r--r--choice_seeker/allotter/management/__init__.py0
-rw-r--r--choice_seeker/allotter/management/commands/__init__.py0
-rw-r--r--choice_seeker/allotter/management/commands/loadexam.py40
-rw-r--r--choice_seeker/allotter/management/commands/loadoptions.py60
-rw-r--r--choice_seeker/allotter/management/commands/loadusers.py65
-rw-r--r--choice_seeker/allotter/models.py136
-rw-r--r--choice_seeker/allotter/templatetags/__init__.py0
-rw-r--r--choice_seeker/allotter/templatetags/range_filter.py27
-rw-r--r--choice_seeker/allotter/tests.py16
-rw-r--r--choice_seeker/allotter/urls.py11
-rw-r--r--choice_seeker/allotter/views.py260
-rwxr-xr-xchoice_seeker/manage.py14
-rw-r--r--choice_seeker/settings.py182
-rw-r--r--choice_seeker/template/allotter/apply.html109
-rw-r--r--choice_seeker/template/allotter/complete.html60
-rw-r--r--choice_seeker/template/allotter/details.html19
-rw-r--r--choice_seeker/template/allotter/hello.html11
-rw-r--r--choice_seeker/template/allotter/login.html21
-rw-r--r--choice_seeker/template/allotter/register.html17
-rw-r--r--choice_seeker/template/base.html23
-rw-r--r--choice_seeker/urls.py18
26 files changed, 1212 insertions, 0 deletions
diff --git a/choice_seeker/README.rst b/choice_seeker/README.rst
new file mode 100644
index 0000000..af82358
--- /dev/null
+++ b/choice_seeker/README.rst
@@ -0,0 +1,4 @@
+
+**Just got started**
+
+http://wiki.fossee.in/JAM
diff --git a/choice_seeker/__init__.py b/choice_seeker/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/choice_seeker/__init__.py
diff --git a/choice_seeker/allotter/__init__.py b/choice_seeker/allotter/__init__.py
new file mode 100644
index 0000000..b48e5a0
--- /dev/null
+++ b/choice_seeker/allotter/__init__.py
@@ -0,0 +1,2 @@
+import os
+
diff --git a/choice_seeker/allotter/admin.py b/choice_seeker/allotter/admin.py
new file mode 100644
index 0000000..0c14e78
--- /dev/null
+++ b/choice_seeker/allotter/admin.py
@@ -0,0 +1,7 @@
+from allotter.models import Exam, Option, Application, Profile
+from django.contrib import admin
+
+admin.site.register(Exam)
+admin.site.register(Option)
+admin.site.register(Application)
+admin.site.register(Profile)
diff --git a/choice_seeker/allotter/forms.py b/choice_seeker/allotter/forms.py
new file mode 100644
index 0000000..65e4cf6
--- /dev/null
+++ b/choice_seeker/allotter/forms.py
@@ -0,0 +1,110 @@
+
+from django import forms
+from allotter.models import Profile
+from django.forms.extras.widgets import SelectDateWidget
+
+from django.utils.encoding import *
+
+from django.contrib.auth import authenticate
+from django.contrib.auth.models import User
+
+from string import digits
+
+BIRTH_YEAR_CHOICES = ('1986','1987','1988','1989','1990','1991')
+
+
+class UserLoginForm(forms.Form):
+
+ ##Registration Number as Username
+ username = forms.IntegerField(label="Registration Number",
+ help_text="As on your Examination ID Card")
+
+ ##Application number as password
+ password = forms.CharField(label = "Application Number",
+ max_length=10, help_text="As on your Examination ID Card")
+
+ dob = forms.DateField(label="Date of Birth",
+ widget=SelectDateWidget(years=BIRTH_YEAR_CHOICES),
+ initial=datetime.date.today)
+
+ def clean_username(self):
+ u_name = self.cleaned_data["username"]
+
+ if not u_name:
+ raise forms.ValidationError("Enter an username.")
+
+ ##Verifies whether username contains only digits and is not
+ ##longer than 7, i.e Username == Registration Number.
+ if str(u_name).strip(digits) or len(str(u_name)) != 7:
+ msg = "Invalid Registration Number"
+ raise forms.ValidationError(msg)
+
+ ##Verifying whether the user already exists in the database
+ ##Raising error otherwise
+ try:
+ User.objects.get(username__exact = u_name)
+ return u_name
+ except User.DoesNotExist:
+ raise forms.ValidationError("Entered Registration Number haven't appeared for JAM Exam.")
+
+ def clean_password(self):
+
+ pwd = self.cleaned_data['password']
+
+ ##Verifying the length of application number and whether it contains
+ ##only digits.
+
+ if str(pwd).strip(digits) and len(pwd) != 5:
+ msg = "Not a valid Application Number"
+ raise forms.ValidationError(msg)
+
+ return pwd
+
+ def clean(self):
+ super(UserLoginForm, self).clean()
+ u_name, pwd = self.cleaned_data.get('username'), self.cleaned_data.get('password')
+ dob = self.cleaned_data['dob']
+ try:
+ current_user = User.objects.get(username__exact = u_name)
+ profile = current_user.get_profile()
+ if profile.dob != dob:
+ raise forms.ValidationError("Date of Birth doesn't match.")
+ except User.DoesNotExist:
+ raise forms.ValidationError("Correct the following errors and try logging in again.")
+
+
+ ##Authentication part
+ user = authenticate(username = u_name, password = pwd)
+ if not user:
+ raise forms.ValidationError("Application Number or Registration Number doesn't match.")
+ return user
+
+
+class UserDetailsForm(forms.Form):
+
+ def __init__(self, user, *args, **kwargs):
+ self.user = user
+ super(UserDetailsForm, self).__init__(*args, **kwargs)
+
+ email = forms.EmailField(label="Email Address",
+ help_text="Enter a valid email id if you have any.")
+ phone_number = forms.IntegerField(label="Phone number",
+ help_text="10 digit number with code")
+
+
+ def clean_phone_number(self):
+ pno = self.cleaned_data['phone_number']
+ if str(pno).strip(digits) or len(str(pno)) != 10:
+ raise forms.ValidationError("Not a valid phone number")
+ return pno
+
+ def save(self):
+ cleaned_data = self.cleaned_data
+ user_profile = self.user.get_profile()
+
+ user_profile.secondary_email = self.cleaned_data['email']
+ user_profile.phone_number = self.cleaned_data['phone_number']
+
+ user_profile.save()
+
+
diff --git a/choice_seeker/allotter/management/__init__.py b/choice_seeker/allotter/management/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/choice_seeker/allotter/management/__init__.py
diff --git a/choice_seeker/allotter/management/commands/__init__.py b/choice_seeker/allotter/management/commands/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/choice_seeker/allotter/management/commands/__init__.py
diff --git a/choice_seeker/allotter/management/commands/loadexam.py b/choice_seeker/allotter/management/commands/loadexam.py
new file mode 100644
index 0000000..2fedb66
--- /dev/null
+++ b/choice_seeker/allotter/management/commands/loadexam.py
@@ -0,0 +1,40 @@
+from csv import reader
+from django.core.management.base import BaseCommand, CommandError
+from allotter.models import Exam
+
+class Command(BaseCommand):
+ args = '<file_name...>'
+ help = "Give the filename of the csv file that has all the exam code and exam name relation"
+
+ def handle(self, *args, **options):
+
+ clean_exam()
+
+ for fname in args:
+ load_exam(fname)
+
+ self.stdout.write('Done\n')
+
+
+def clean_exam():
+ """Removes all the objects from the database, required as if not done there might be a case of multile entries"""
+ data = Exam.objects.all()
+ data.delete()
+
+def load_exam(filename):
+ """Load exam code and exam name from the given csv file. The file should
+ declare a list of "exam_code;exam_name".
+ """
+ try:
+ csvFile = open(filename, 'rb')
+ except IOError as (errno,strerror):
+ print "I/O error({0}): {1}".format(errno, strerror)
+
+ csvReader = reader(csvFile, delimiter=";")
+
+ for data in csvReader:
+ new_exam = Exam.objects.create()
+ new_exam.exam_code = data[0]
+ new_exam.exam_name = data[1]
+ new_exam.save()
+ print "Added ({0} : {1})".format(data[0], data[1]) \ No newline at end of file
diff --git a/choice_seeker/allotter/management/commands/loadoptions.py b/choice_seeker/allotter/management/commands/loadoptions.py
new file mode 100644
index 0000000..f7c01e6
--- /dev/null
+++ b/choice_seeker/allotter/management/commands/loadoptions.py
@@ -0,0 +1,60 @@
+from optparse import make_option
+from csv import reader
+from django.core.management.base import BaseCommand
+from allotter.models import Exam, Option
+
+class Command(BaseCommand):
+ option_list = BaseCommand.option_list + (
+ make_option('--pcc',metavar='Paper course code file name', type=str),
+ make_option('--cc',metavar='Course code file name', type=str),
+ )
+ help = "Give the filenames of the csv files that has all the option code, name and exam code relation"
+
+ def handle(self, *args, **options):
+
+ clean_option()
+
+ load_option(options)
+
+ self.stdout.write('Done\n')
+
+
+def clean_option():
+ """Removes all the objects from the database, required as if not done there might be a case of multiple entries"""
+ data = Option.objects.all()
+ data.delete()
+
+def load_option(options):
+ """Load option code and option name from the given csv file. The file should
+ declare a list of "exam_code,option_code,option,code".
+ """
+ paperCourseFileName=options.get('pcc')
+ courseCodeFileName=options.get('cc')
+ try:
+ paperCourseFile = open(paperCourseFileName, 'rb')
+ except IOError as (errno,strerror):
+ print "I/O error({0}): {1}".format(errno, strerror)
+
+ try:
+ courseCodeFile = open(courseCodeFileName, 'rb')
+ except IOError as (errno,strerror):
+ print "I/O error({0}): {1}".format(errno, strerror)
+
+ paperReader = reader(paperCourseFile, delimiter=",")
+ courseReader = reader(courseCodeFile, delimiter=",")
+
+ courseDict = {}
+
+ for data in courseReader:
+ courseDict[int(data[0])]=data[1]
+
+ for data in paperReader:
+ exam = Exam.objects.get(exam_code=data[0])
+ for value in data[1:len(data)]:
+ try:
+ new_option = Option.objects.get(opt_code=value)
+ except Option.DoesNotExist:
+ new_option = Option(opt_name=courseDict[int(value)],opt_code=value)
+ new_option.save()
+ new_option.exam.add(exam)
+ print "Added (option {0} with code {1} and exam {2})".format(courseDict[int(value)],value,exam) \ No newline at end of file
diff --git a/choice_seeker/allotter/management/commands/loadusers.py b/choice_seeker/allotter/management/commands/loadusers.py
new file mode 100644
index 0000000..99fd075
--- /dev/null
+++ b/choice_seeker/allotter/management/commands/loadusers.py
@@ -0,0 +1,65 @@
+from optparse import make_option
+from datetime import datetime
+from csv import DictReader
+from django.core.management.base import BaseCommand
+from allotter.models import Exam, Application, User, Profile
+
+class Command(BaseCommand):
+ option_list = BaseCommand.option_list + (
+ make_option('--usdf',metavar='User details file name', type=str),
+ )
+ help = "Give the filename of the csv files that has all the details of the users"
+
+ def handle(self, *args, **options):
+
+ clean_users()
+
+ load_users(options)
+
+ self.stdout.write('Done\n')
+
+
+def clean_users():
+ """Removes all the objects from the database, required as if not done there might be a case of multiple entries"""
+ User.objects.filter(is_superuser=False).delete()
+
+def load_users(options):
+ """Load option code and option name from the given csv file. The file should
+ declare a list of "exam_code,option_code,option,code".
+ """
+ userDetailsFileName=options.get('usdf')
+ try:
+ userDetailsFile = open(userDetailsFileName, 'rb')
+ except IOError as (errno,strerror):
+ print "I/O error({0}): {1}".format(errno, strerror)
+
+
+ userReader = DictReader(userDetailsFile, delimiter=":")
+
+
+ for data in userReader:
+ appno = data['AppNo.']
+ regno = data['Reg.No.']
+ new_user = User.objects.create_user(regno, password=appno, email="")
+ application = Application(user=new_user)
+ application.np = int(data['NP'])
+ if data['P1'].strip():
+ application.first_paper = Exam.objects.get(exam_code=data['P1'])
+ try:
+ application.second_paper = Exam.objects.get(exam_code=data['P2'])
+ except:
+ pass
+ else:
+ application.first_paper = Exam.objects.get(exam_code=data['P2'])
+
+
+ application.nat = data['Nat']
+ application.gender = data['Gdr']
+ application.cent = data['Cent']
+ application.cgy = data['Cgy']
+ application.save()
+ dob = datetime.strptime(data['DOB'], "%d/%m/%y")
+ new_profile = Profile(user=new_user, application=application)
+ new_profile.dob = dob
+ new_profile.save()
+ print "Added user with {0} and {1} with dob as {2}".format(appno,regno,dob) \ No newline at end of file
diff --git a/choice_seeker/allotter/models.py b/choice_seeker/allotter/models.py
new file mode 100644
index 0000000..9da8213
--- /dev/null
+++ b/choice_seeker/allotter/models.py
@@ -0,0 +1,136 @@
+from django.db import models
+from django.contrib.auth.models import User
+
+##EXAMINATION_SUBJECTS = (
+## ("Physics", "Physics"),
+## ("Mathematics", "Mathematics"),
+## ("Chemistry", "Chemistry"),
+## )
+
+##CATEGORIES = (
+## ("GEN", "GEN"),
+## ("OBC", "OBC(Non-Creamy Layer)"),
+## ("SC", "SC"),
+## ("ST", "ST"),
+## )
+
+##AVAILABLE_OPTIONS = (
+## ("MScChem", "M.Sc Chemisty"),
+## ("M.Sc-Physics-IIT-Bombay", "M.Sc Physics IIT Bombay"),
+## ("MScMath","M.Sc Mathematics"),
+## ("MscHist", "M.Sc History"),
+## ("MSc-PhD Dual-Degree-IIT-Bombay", "MSc-PhD Dual Degree IIT Bombay"),
+## ("M.Sc Physics-IIT-Madras", "M.Sc Physics IIT Madras"),
+## ("M.Sc-Physics-IIT-Guwahati", "M.Sc Physics IIT Guwahati"),
+## ("M.Sc-Physics-IIT-KGP", "M.Sc Physics IIT KGP"),
+## ("M.Sc-Physics-IIT-Roorkee", "M.Sc Physics IIT Roorkee"),
+##)
+
+##GENDER_CHOICES = (
+## ("M", "Male"),
+## ("F", "Female"),)
+
+##APPLICATION_STATUS = (
+## ("I", "Incomplete"),
+## ("Submitted", "Submitted"))
+
+##BIRTH_YEAR_CHOICES = ('1989', '1990', '1991')
+
+class Exam(models.Model):
+ """
+ Table for Examination Codes and Subject names.
+ """
+ ##PH for Physics, CY for Chemistry
+ exam_code = models.CharField(max_length=100,
+ verbose_name=u"Test Paper code",
+ help_text=u"Unique code for the Test")
+
+ exam_name = models.CharField(max_length=100,
+ verbose_name=u"Test Paper",
+ help_text=u"Subject name of the Test")
+
+ def __unicode__(self):
+ return self.exam_name
+
+
+class Option(models.Model):
+ """
+ Options Table, Foreign Keyed with Examination.
+ """
+
+ opt_name = models.CharField(max_length=100,
+ verbose_name=u"Programme name",
+ help_text=u"Programme Title")
+
+ opt_code = models.IntegerField(max_length=3,
+ verbose_name=u"Programme Code")
+
+ opt_location = models.CharField(max_length=30,
+ verbose_name=u"Programme Location",
+ help_text=u"Offered by which IIT")
+
+ exam = models.ManyToManyField(Exam)
+
+ class Meta:
+ verbose_name_plural = "Options"
+
+ def __unicode__(self):
+ return unicode(self.opt_code)
+
+
+class Application(models.Model):
+ """An application for the student - one per student
+ """
+ user = models.OneToOneField(User)
+
+ ##To be filled by applicant
+ options_selected = models.CharField(max_length=5000,help_text="CSV formatted list of options", blank=True)
+
+ ##Prefilled fields
+ np = models.IntegerField(max_length=2, help_text="Number of Test Papers")
+
+ ##Mandatory First Subject
+ first_paper = models.ForeignKey(Exam, related_name="first_paper")
+
+ ##Second subject can be left blank or null
+ second_paper = models.ForeignKey(Exam, related_name="second_paper", blank=True, null=True)
+
+ nat = models.CharField(max_length=10, verbose_name="Nationality")
+
+ gender = models.CharField(max_length=2, verbose_name="Gender")
+
+ cent = models.IntegerField(max_length=10, verbose_name="Center Code")
+
+ cgy = models.CharField(max_length=10, verbose_name="Category")
+
+ pd = models.BooleanField(verbose_name="Physical Disability", default=False, blank=True)
+
+ submitted = models.BooleanField(verbose_name="Submission Status", default=False)
+
+ def __unicode__(self):
+ u = self.user
+ return u'Application for {0}'.format(u.username)
+
+class Profile(models.Model):
+
+ user = models.OneToOneField(User)
+
+ #Used for verification purposes
+ dob = models.DateField(verbose_name=u"Date of Birth",
+ help_text=u"Date of birth as given in the application")
+
+ secondary_email = models.EmailField(verbose_name=u"Secondary Email",
+ help_text=u"Email address read from user after authentication")
+
+ phone_number = models.IntegerField(max_length=10, verbose_name="Phone Number",
+ help_text=u"Phone number read from user after authentication")
+
+ #Application for the Profile
+ application = models.ForeignKey(Application)
+
+ def __unicode__(self):
+ u = self.user
+ return u'User Profile {0}'.format(u.username)
+
+
+
diff --git a/choice_seeker/allotter/templatetags/__init__.py b/choice_seeker/allotter/templatetags/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/choice_seeker/allotter/templatetags/__init__.py
diff --git a/choice_seeker/allotter/templatetags/range_filter.py b/choice_seeker/allotter/templatetags/range_filter.py
new file mode 100644
index 0000000..1ce43d8
--- /dev/null
+++ b/choice_seeker/allotter/templatetags/range_filter.py
@@ -0,0 +1,27 @@
+##Credits : http://djangosnippets.org/snippets/1357/
+
+from django.template import Library
+
+register = Library()
+
+@register.filter
+def get_range( value ):
+ """
+ Filter - returns a list containing range made from given value
+ Usage (in template):
+
+ <ul>{% for i in 3|get_range %}
+ <li>{{ i }}. Do something</li>
+ {% endfor %}</ul>
+
+ Results with the HTML:
+ <ul>
+ <li>0. Do something</li>
+ <li>1. Do something</li>
+ <li>2. Do something</li>
+ </ul>
+
+ Instead of 3 one may use the variable set in the views
+ """
+ return range( value )
+
diff --git a/choice_seeker/allotter/tests.py b/choice_seeker/allotter/tests.py
new file mode 100644
index 0000000..501deb7
--- /dev/null
+++ b/choice_seeker/allotter/tests.py
@@ -0,0 +1,16 @@
+"""
+This file demonstrates writing tests using the unittest module. These will pass
+when you run "manage.py test".
+
+Replace this with more appropriate tests for your application.
+"""
+
+from django.test import TestCase
+
+
+class SimpleTest(TestCase):
+ def test_basic_addition(self):
+ """
+ Tests that 1 + 1 always equals 2.
+ """
+ self.assertEqual(1 + 1, 2)
diff --git a/choice_seeker/allotter/urls.py b/choice_seeker/allotter/urls.py
new file mode 100644
index 0000000..6556c62
--- /dev/null
+++ b/choice_seeker/allotter/urls.py
@@ -0,0 +1,11 @@
+from django.conf.urls.defaults import patterns, url
+
+urlpatterns = patterns('allotter.views',
+ url(r'^login/$', 'user_login'),
+ url(r'^logout/$', 'user_logout'),
+ url(r'^(?P<reg_no>\d+)/apply/$', 'apply'),
+ url(r'^(?P<reg_no>\d+)/details/$', 'submit_details'),
+ url(r'^(?P<reg_no>\d+)/get_pdf/$', 'generate_pdf'),
+ url(r'^(?P<reg_no>\d+)/submit/$', 'submit_options', name='submit_options'),
+ url(r'^(?P<reg_no>\d+)/complete/$', 'complete_allotment', name='complete_allotment'),
+)
diff --git a/choice_seeker/allotter/views.py b/choice_seeker/allotter/views.py
new file mode 100644
index 0000000..fb819fc
--- /dev/null
+++ b/choice_seeker/allotter/views.py
@@ -0,0 +1,260 @@
+from django.contrib.auth import login, logout, authenticate
+from django.contrib.auth.decorators import login_required
+from django.shortcuts import render, redirect, get_object_or_404
+
+from django.http import Http404
+from django.http import HttpResponse
+from django.http import HttpResponseRedirect
+
+from django.core.urlresolvers import reverse
+
+from django.contrib.auth.models import User
+from allotter.models import Profile, Option, Exam
+from allotter.forms import UserLoginForm, UserDetailsForm
+
+from itertools import chain
+
+#Reportlab libraries
+from reportlab.platypus import Table, TableStyle, SimpleDocTemplate, Paragraph, Spacer
+from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
+from reportlab.lib import colors
+from reportlab.lib.units import inch
+from reportlab.lib.enums import TA_JUSTIFY
+
+import time
+
+def user_login(request):
+ """
+ Verify the user credentials and log the user in.
+ """
+
+ user = request.user
+ if user.is_authenticated():
+ status = user.get_profile().application.submitted #Getting the submission status
+ if status: #If already submitted, takes to Completion Page
+ return HttpResponseRedirect(reverse('allotter.views.complete_allotment', args=(user.username,)))
+ else: #Otherwise to Option Choosing Page
+ return HttpResponseRedirect(reverse('allotter.views.apply', args=(user.username,)))
+
+ if request.method == "POST":
+ form = UserLoginForm(request.POST)
+ if form.is_valid():
+ user = form.cleaned_data
+ login(request, user)
+ status = user.get_profile().application.submitted #Getting the submission status
+ if status:
+ return HttpResponseRedirect(reverse('allotter.views.complete_allotment', args=(user.username,)))
+ else:
+ return HttpResponseRedirect(reverse('allotter.views.submit_details', args=(user.username,)))
+ else:
+ context = {"form": form}
+ return render(request, 'allotter/login.html', context)
+ else:
+ form = UserLoginForm()
+ context = {"form": form}
+ return render(request, 'allotter/login.html', context)
+
+
+@login_required
+def submit_details(request, reg_no):
+ """
+ Get the secondary email address, phone number and save it to the Profile.
+ """
+ user = request.user
+
+ if request.method == "POST":
+ form = UserDetailsForm(user, request.POST)
+ if form.is_valid():
+ data = form.cleaned_data
+ form.save()
+ return redirect("/allotter/apply/")
+ else:
+ return render(request, 'allotter/details.html', {'form':form})
+
+ else:
+ form = UserDetailsForm(request.user)
+ context = {"form": form}
+ return render(request, 'allotter/details.html', context)
+
+def get_details(user, error_message = ""):
+ """
+ Retrieves the information about Test paper(s) and options available
+ and returns them in a dictionary(context) for passing to the Template.
+ """
+ user_profile = user.get_profile()
+ user_application = user_profile.application
+ np = user_application.np #Number of Papers
+ first_paper = user_application.first_paper #First Paper Name
+ options_available_first = Option.objects.filter(exam__exam_name=first_paper).distinct() #Options for First paper
+ oafl = len(options_available_first)
+ if np == 2: #If written two exams
+ second_paper = user_application.second_paper
+ options_available_second = Option.objects.filter(exam__exam_name=second_paper).distinct()
+ oasl = len(options_available_second)
+ context = {'user': user, 'first_paper': first_paper,
+ 'options_available_first' : options_available_first,
+ 'second_paper': second_paper,
+ 'options_available_second' : options_available_second,
+ 'np' : np, 'options_range': range(1, oafl + oasl + 1, 1),
+ 'error_message': error_message}
+ else: #If written only one exam
+ context = {'user': user, 'first_paper': first_paper,
+ 'options_available_first' : options_available_first,
+ 'options_range': range(1, oafl + 1, 1),
+ 'np' : np, 'error_message' : error_message}
+ return context
+
+@login_required
+def apply(request, reg_no):
+ """
+ Displays the application page for an authenticated user.
+ """
+ user = request.user
+ if not(user.is_authenticated()):
+ return redirect('/allotter/login/')
+
+ context = get_details(user)
+
+ return render(request, 'allotter/apply.html', context)
+
+
+def user_logout(request):
+ ##Logouts the user.
+ logout(request)
+ return redirect ('/allotter/login/')
+
+#TODO: Extensive Testing
+
+@login_required
+def submit_options(request, reg_no):
+ """
+ Gets the Options and their preference number through the POST object and
+ stores them as list(sorted according to preferences). Options with None are
+ ignored.
+ """
+ user = get_object_or_404(User, username=reg_no)
+ user_profile = user.get_profile()
+ user_application = user_profile.application
+ np = user_application.np
+ first_paper = user_application.first_paper #First Paper Name
+ options_available_first = Option.objects.filter(exam__exam_name=first_paper).distinct() #Options for First paper
+
+ if np == 2: #If qualified for second paper
+ second_paper = user_application.second_paper #Second Paper Name
+ options_available_second = Option.objects.filter(exam__exam_name=second_paper).distinct() #Options for second paper
+ options_available_list = chain(options_available_first, options_available_second) #chaining the two lists
+ else:
+ options_available_list = options_available_first
+
+ options_chosen_list = [] #Initializing empty list for storing options
+ for option in options_available_list:
+ option_pref = request.POST[unicode(option.opt_code)]
+ options_chosen_list.append([option_pref, str(option.opt_code)]) #[preference, option code]
+
+ options_chosen_list.sort() #Sorting by preference
+ options_code_list = []
+ for opt in options_chosen_list:
+ if int(opt[0]): #ignoring the options for which None was marked
+ options_code_list.append(opt[1])
+
+ user_application.options_selected = options_code_list #Saving the data in model
+ user_application.submitted = True #Submission Status
+ user_application.save()
+ return HttpResponseRedirect(reverse('allotter.views.complete_allotment', args=(reg_no,)))
+
+def complete_allotment(request, reg_no):
+ """
+ Passes the chosen options queryset to the Completion Page Template
+ """
+ user = get_object_or_404(User, username=reg_no)
+ sec_email = user.get_profile().secondary_email
+ options_chosen = get_chosen_options(user)
+ context = {'username': reg_no, 'email': sec_email,
+ 'options_chosen': options_chosen}
+
+ return render(request, 'allotter/complete.html', context)
+
+
+def get_chosen_options(user):
+ """
+ Reads the options submitted by the user in the Application page
+ """
+ user_profile = user.get_profile()
+ user_application = user_profile.application
+ np = user_application.np
+ ocl = eval(user_application.options_selected)
+ chosen_options = []
+ for oc in ocl:
+ chosen_options.append(Option.objects.get(opt_code=int(oc)))
+ return chosen_options
+
+
+@login_required
+def generate_pdf(request, reg_no):
+ """
+ The Ugly code for generating the pdf using ReportLab.
+ """
+
+ user = get_object_or_404(User, username=reg_no)
+ user_profile = user.get_profile()
+ user_application = user_profile.application
+ np = user_application.np
+
+ response = HttpResponse(mimetype='application/pdf')
+ response['Content-Disposition'] = 'attachment; filename=JAM2012_Allottment.pdf'
+
+ elements = []
+ doc = SimpleDocTemplate(response)
+
+ formatted_time = time.ctime()
+ styles = getSampleStyleSheet()
+ styles.add(ParagraphStyle(name='Justify', alignment=TA_JUSTIFY))
+
+ ptext = '<font size=15>JAM 2012 Allotment.</font>'
+ elements.append(Paragraph(ptext, styles["Justify"]))
+ elements.append(Spacer(4, 20))
+
+ ptext = '<font size=12>Registration Number: %s</font>' % reg_no
+ elements.append(Paragraph(ptext, styles["Normal"]))
+ elements.append(Spacer(1, 12))
+
+ ptext = '<font size=12>Number of Papers Eligible: %s</font>' % np
+ elements.append(Paragraph(ptext, styles["Normal"]))
+ elements.append(Spacer(1, 12))
+
+ ptext = '<font size=12>No options were chosen.</font>'
+ elements.append(Paragraph(ptext, styles["Normal"]))
+ elements.append(Spacer(1, 12))
+
+ data = []
+ options = get_chosen_options(user) ##Put a check to show when the options chosen is empty
+
+ if not(options):
+ doc.build(elements)
+ return response
+
+ ptext = '<font size=12>Following are the options in order of preference</font>'
+ elements.append(Paragraph(ptext, styles["Normal"]))
+ elements.append(Spacer(1, 12))
+
+ counter = 1
+ for opt in options:
+ data.append([counter, opt.opt_code, opt.opt_location, opt.opt_name])
+ counter = counter + 1
+
+ t = Table(data)
+ t.setStyle(TableStyle([('GRID',(0,0),(3,len(options)),1,colors.black),
+ ('TEXTCOLOR',(0,0),(0,-1),colors.green)]))
+
+ elements.append(t)
+
+ ptext = '<font size=12>%s</font>' % formatted_time
+ elements.append(Paragraph(ptext, styles["Normal"]))
+ elements.append(Spacer(1, 12))
+
+ doc.build(elements)
+
+ return response
+
+
+
diff --git a/choice_seeker/manage.py b/choice_seeker/manage.py
new file mode 100755
index 0000000..3e4eedc
--- /dev/null
+++ b/choice_seeker/manage.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+from django.core.management import execute_manager
+import imp
+try:
+ imp.find_module('settings') # Assumed to be in the same directory.
+except ImportError:
+ import sys
+ sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__)
+ sys.exit(1)
+
+import settings
+
+if __name__ == "__main__":
+ execute_manager(settings)
diff --git a/choice_seeker/settings.py b/choice_seeker/settings.py
new file mode 100644
index 0000000..dd71bc4
--- /dev/null
+++ b/choice_seeker/settings.py
@@ -0,0 +1,182 @@
+# Django settings for aloha project.
+
+import os
+PROJECT_DIR = os.path.dirname(__file__)
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+ ('Primal Pappachan', 'primal@fossee.in'),
+)
+
+AUTHORS = (
+ ('Primal Pappachan', 'primal@fossee.in'),
+ ('Parth Buch', 'parth@fossee.in'),
+)
+
+MANAGERS = ADMINS
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
+ 'NAME': 'jam', # Or path to database file if using sqlite3.
+ 'USER': '', # Not used with sqlite3.
+ 'PASSWORD': '', # Not used with sqlite3.
+ 'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
+ 'PORT': '', # Set to empty string for default. Not used with sqlite3.
+ }
+}
+
+URL_ROOT = ''
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# On Unix systems, a value of None will cause Django to use the same
+# timezone as the operating system.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'Asia/Kolkata'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'en-us'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# If you set this to False, Django will not format dates, numbers and
+# calendars according to the current locale
+USE_L10N = True
+
+# Absolute filesystem path to the directory that will hold user-uploaded files.
+# Example: "/home/media/media.lawrence.com/media/"
+MEDIA_ROOT = ''
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash.
+# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
+MEDIA_URL = ''
+
+# Absolute path to the directory static files should be collected to.
+# Don't put anything in this directory yourself; store your static files
+# in apps' "static/" subdirectories and in STATICFILES_DIRS.
+# Example: "/home/media/media.lawrence.com/static/"
+STATIC_ROOT = os.path.join(PROJECT_DIR, "static")
+
+# URL prefix for static files.
+# Example: "http://media.lawrence.com/static/"
+STATIC_URL = '/static/'
+
+# URL prefix for admin static files -- CSS, JavaScript and images.
+# Make sure to use a trailing slash.
+# Examples: "http://foo.com/static/admin/", "/static/admin/".
+ADMIN_MEDIA_PREFIX = '/static/admin/'
+
+# Additional locations of static files
+STATICFILES_DIRS = (
+ # Put strings here, like "/home/html/static" or "C:/www/django/static".
+ # Always use forward slashes, even on Windows.
+ # Don't forget to use absolute paths, not relative paths.
+)
+
+# List of finder classes that know how to find static files in
+# various locations.
+STATICFILES_FINDERS = (
+ 'django.contrib.staticfiles.finders.FileSystemFinder',
+ 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
+# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
+)
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = '5(yt_egghk$&w1qsxy9remz^+!^5m-99gf$9+yam^56xt2#7&$'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+ 'django.template.loaders.filesystem.Loader',
+ 'django.template.loaders.app_directories.Loader',
+# 'django.template.loaders.eggs.Loader',
+)
+
+MIDDLEWARE_CLASSES = (
+ 'django.middleware.common.CommonMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'debug_toolbar.middleware.DebugToolbarMiddleware',
+)
+
+ROOT_URLCONF = 'aloha.urls'
+
+TEMPLATE_DIRS = (
+ # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+ # Always use forward slashes, even on Windows.
+ # Don't forget to use absolute paths, not relative paths.
+ os.path.join(PROJECT_DIR, "template")
+)
+
+INSTALLED_APPS = (
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.sites',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+ # Uncomment the next line to enable the admin:
+ 'django.contrib.admin',
+ # Uncomment the next line to enable admin documentation:
+ # 'django.contrib.admindocs',
+ #Third-Party Apps
+ 'south',
+ 'django_extensions',
+ 'debug_toolbar',
+
+ 'allotter',
+)
+
+
+# A sample logging configuration. The only tangible logging
+# performed by this configuration is to send an email to
+# the site admins on every HTTP 500 error.
+# See http://docs.djangoproject.com/en/dev/topics/logging for
+# more details on how to customize your logging configuration.
+LOGGING = {
+ 'version': 1,
+ 'disable_existing_loggers': False,
+ 'handlers': {
+ 'mail_admins': {
+ 'level': 'ERROR',
+ 'class': 'django.utils.log.AdminEmailHandler'
+ }
+ },
+ 'loggers': {
+ 'django.request': {
+ 'handlers': ['mail_admins'],
+ 'level': 'ERROR',
+ 'propagate': True,
+ },
+ }
+}
+
+AUTH_PROFILE_MODULE = "allotter.Profile"
+LOGIN_URL = '/allotter/login'
+
+#For developing locally
+INTERNAL_IPS = ('127.0.0.1')
+
+DEBUG_TOOLBAR_PANELS = (
+ 'debug_toolbar.panels.version.VersionDebugPanel',
+ 'debug_toolbar.panels.timer.TimerDebugPanel',
+ 'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel',
+ 'debug_toolbar.panels.headers.HeaderDebugPanel',
+ 'debug_toolbar.panels.request_vars.RequestVarsDebugPanel',
+ 'debug_toolbar.panels.template.TemplateDebugPanel',
+ 'debug_toolbar.panels.sql.SQLDebugPanel',
+ 'debug_toolbar.panels.signals.SignalDebugPanel',
+ 'debug_toolbar.panels.logger.LoggingPanel',
+)
diff --git a/choice_seeker/template/allotter/apply.html b/choice_seeker/template/allotter/apply.html
new file mode 100644
index 0000000..201e1f2
--- /dev/null
+++ b/choice_seeker/template/allotter/apply.html
@@ -0,0 +1,109 @@
+{% extends "base.html" %}
+
+{% load range_filter %}
+
+{% block title %} JAM 2012 Application form {% endblock %}
+
+{% block content %}
+
+<p> Welcome to JAM 2012 allotment! </p>
+
+<p>Read the following instructions carefully before continuing. </p>
+
+<hr/>
+Choose the options as per your preference
+
+<h3>You are eligible for {{first_paper}}
+
+{% comment %}
+Checking if there is second paper and displaying its name.
+{% endcomment %}
+
+{% if np == 2 %}
+
+and {{second_paper}}
+
+{% endif %}
+
+</h3>
+
+<h4>For the paper(s) in which you are in the merit list, the following
+options are available to you. Please rank your choices.</h4>
+
+<p> Preferences will be assigned to options in the ascending order. Make sure it's
+<b>None</b> for all options after your last option(so that it is not considered).</p>
+
+<hr/>
+
+{% comment %}
+</h3>
+Listing the options for first test paper.
+{% endcomment %}
+
+<h3>Options available for {{first_paper}}</h3>
+
+{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
+
+<form action="/allotter/{{user.username}}/submit/" method="post">
+{% csrf_token %}
+
+<table>
+<tr>
+<td><p>Programme Code </p></td>
+<td><p>Programme Name </p></td>
+<td><p>Insitute </p></td>
+<td><p>Preference </p></td>
+</tr>
+
+{% for option in options_available_first %}
+ <tr>
+ <td><p> {{ option.opt_code }} </p></td>
+ <td><p> {{option.opt_name }} </p></td>
+ <td><p> {{option.opt_location }} </p></td>
+ <td><select name="{{option.opt_code}}">
+ {% for i in options_range %}
+ <option value="{{i}}" selected="selected">Preference {{i}}</option>
+ {% endfor %}
+ <option value="0" selected="selected">None</option>
+ </select>
+ </td>
+ </tr>
+{% endfor %}
+</table>
+
+{% if np == 2 %}
+
+<h3>Options available for {{second_paper}} </h3>
+
+<table>
+<tr>
+<td><p>Programme Code </p></td>
+<td><p>Programme Name </p></td>
+<td><p>Insitute </p></td>
+<td><p>Preference </p></td>
+</tr>
+
+{% for option in options_available_second %}
+ <tr>
+ <td><p> {{option.opt_code }} </p></td>
+ <td><p> {{option.opt_name }} </p></td>
+ <td><p> {{option.opt_location }} </p></td>
+ <td><select name="{{option.opt_code}}">
+ {% for i in options_range %}
+ <option value="{{i}}" selected="selected">Preference {{i}}</option>
+ {% endfor %}
+ <option value="0" selected="selected">None</option>
+ </select>
+ </td>
+ </tr>
+{% endfor %}
+</table>
+
+{% endif %}
+
+<p><label for="check">I am responsible for my own choices.</label>
+<input type="checkbox" name="check" id="check" /></p>
+<input type="submit" name="save" value="Save" />
+</form>
+
+{% endblock content %}
diff --git a/choice_seeker/template/allotter/complete.html b/choice_seeker/template/allotter/complete.html
new file mode 100644
index 0000000..26ee46d
--- /dev/null
+++ b/choice_seeker/template/allotter/complete.html
@@ -0,0 +1,60 @@
+{% extends "base.html" %}
+
+{% block title %}Options saved.{% endblock %}
+
+{% block content %}
+
+<h2> The following options have been saved. Please verify them before logging out.</h2>
+
+<h3> Please keep in mind that, the next time you login you will be redirected to this page straightaway.</h3>
+
+{% if options_chosen %}
+<table>
+<tr>
+<td><p>Programme Code </p></td>
+<td><p>Programme Name </p></td>
+<td><p>Insitute </p></td>
+<td><p>Preference </p></td>
+</tr>
+
+{% for option in options_chosen %}
+ <tr>
+ <td><p> {{ forloop.counter }} </p></td>
+ <td><p> {{ option.opt_code }} </p></td>
+ <td><p> {{ option.opt_name }} </p></td>
+ <td><p> {{ option.opt_location }} </p></td>
+ </tr>
+{% endfor %}
+
+</table>
+
+{% if email %}
+
+An email with the list of options has been sent {{ email }} for book-keeping purposes.
+
+{% endif %}
+
+{% else %}
+
+<h3> No Options were chosen, Press Edit Options to go back and select options otherwise Press logout to exit the allotment process </h3>
+
+{% endif %}
+
+<form id="apply" action="/allotter/{{username}}/apply/" method="post">
+{% csrf_token %}
+<input type="submit" name="apply" value="Edit Options" />
+</form>
+
+<form id ="get_pdf" action="/allotter/{{username}}/get_pdf/" method="post">
+{% csrf_token %}
+<input type="submit" name="get_pdf" value="Generate PDF" />
+</form>
+
+<form id="logout" action="/allotter/logout/" method="post">
+{% csrf_token %}
+<input type="submit" name="logout" value="Quit Allotment" />
+</form>
+
+{% endblock content %}
+
+
diff --git a/choice_seeker/template/allotter/details.html b/choice_seeker/template/allotter/details.html
new file mode 100644
index 0000000..5beeb85
--- /dev/null
+++ b/choice_seeker/template/allotter/details.html
@@ -0,0 +1,19 @@
+{% extends "base.html" %}
+
+{% block title %}Details form {% endblock %}
+
+{% block content %}
+Please provide the following details.
+<form action="/allotter/details/" method="post">
+{% csrf_token %}
+
+<table>
+{{ form.as_table }}
+</table>
+
+<h2> These details cannot be changed once entered. </h2>
+
+<input type="submit" value="Submit" />
+</form>
+
+{% endblock content %}
diff --git a/choice_seeker/template/allotter/hello.html b/choice_seeker/template/allotter/hello.html
new file mode 100644
index 0000000..0d07c7f
--- /dev/null
+++ b/choice_seeker/template/allotter/hello.html
@@ -0,0 +1,11 @@
+{% extends "base.html" %}
+
+{% block title %} Welcome Note {% endblock %}
+
+{% block content %}
+<h2>Hello, there</h2>
+
+<p> Welcome <strong>{{user.first_name.title}} {{user.last_name.title}}</strong>,
+to JAM 2012 allotment! </p>
+
+{% endblock content %}
diff --git a/choice_seeker/template/allotter/login.html b/choice_seeker/template/allotter/login.html
new file mode 100644
index 0000000..72769a0
--- /dev/null
+++ b/choice_seeker/template/allotter/login.html
@@ -0,0 +1,21 @@
+{% extends "base.html" %}
+
+{% block title %}Login{% endblock title %}
+
+{% block content %}
+<h2> Welcome to the Allotment.
+Please login to proceed.</h2>
+
+<form action="" method="post">
+{% csrf_token %}
+
+<table>
+{{ form.as_table }}
+</table>
+
+<br/>
+<input type="submit" value="Login" />
+</form>
+<!-- <a href="{{URL_ROOT}}/exam/forgotpassword/">Forgot Password</a> <br /> -->
+
+{% endblock content %}
diff --git a/choice_seeker/template/allotter/register.html b/choice_seeker/template/allotter/register.html
new file mode 100644
index 0000000..d7d04b9
--- /dev/null
+++ b/choice_seeker/template/allotter/register.html
@@ -0,0 +1,17 @@
+{% extends "base.html" %}
+
+{% block title %}Registration form {% endblock %}
+
+{% block content %}
+Please provide the following details.
+<form action="" method="post">
+{% csrf_token %}
+
+<table>
+{{ form.as_table }}
+</table>
+
+<input type="submit" value="Register" />
+</form>
+
+{% endblock content %}
diff --git a/choice_seeker/template/base.html b/choice_seeker/template/base.html
new file mode 100644
index 0000000..cd937ba
--- /dev/null
+++ b/choice_seeker/template/base.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <style>
+ input.required, error {
+ border: 2px solid red;
+ }
+ </style>
+ <title>{% block title %} {% endblock %}</title>
+</head>
+
+<body>
+ <div id="sidebar">
+ {% block sidebar %}{% endblock %}
+ </div>
+
+ <div id="content">
+ {% block content %}{% endblock %}
+ {% block main_content %}{% endblock %}
+ </div>
+</body>
+</html>
diff --git a/choice_seeker/urls.py b/choice_seeker/urls.py
new file mode 100644
index 0000000..f5fdfe0
--- /dev/null
+++ b/choice_seeker/urls.py
@@ -0,0 +1,18 @@
+from django.conf.urls.defaults import patterns, include, url
+
+from django.contrib import admin
+
+admin.autodiscover()
+
+urlpatterns = patterns('',
+ url(r'^allotter/', include('allotter.urls')),
+ # Examples:
+ # url(r'^$', 'aloha.views.home', name='home'),
+ # url(r'^aloha/', include('aloha.foo.urls')),
+
+ # Uncomment the admin/doc line below to enable admin documentation:
+ # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
+
+ # Uncomment the next line to enable the admin:
+ url(r'^admin/', include(admin.site.urls)),
+)