diff options
Diffstat (limited to 'choice_seeker')
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)), +) |