Merge small LESS refactorings.
diff --git a/app/soc/modules/gsoc/callback.py b/app/soc/modules/gsoc/callback.py
index 15e411a..e3ff14a 100644
--- a/app/soc/modules/gsoc/callback.py
+++ b/app/soc/modules/gsoc/callback.py
@@ -88,6 +88,7 @@
     self.views.append(document.EditDocumentPage())
     self.views.append(document.EventsPage())
     self.views.append(duplicates.DuplicatesPage())
+    self.views.append(grading_record_details.GradingGroupCreate())
     self.views.append(grading_record_details.GradingRecordDetails())
     self.views.append(grading_record_details.GradingRecordsOverview())
     self.views.append(homepage.Homepage())
diff --git a/app/soc/modules/gsoc/logic/survey.py b/app/soc/modules/gsoc/logic/survey.py
index d572287..d5bcbce 100644
--- a/app/soc/modules/gsoc/logic/survey.py
+++ b/app/soc/modules/gsoc/logic/survey.py
@@ -18,8 +18,14 @@
 
 from google.appengine.ext import db
 
+from soc.modules.gsoc.models.grading_project_survey import GradingProjectSurvey
+from soc.modules.gsoc.models.project_survey import ProjectSurvey
 
-def getSurveysForProgram(model, program, surveys, limit=1000):
+MIDTERM_ID = 'midterm'
+FINAL_ID = 'final'
+
+
+def getSurveysForProgram(model, program, surveys):
   """Return the survey entity for a given program and the survey link id.
 
   Args:
@@ -30,13 +36,26 @@
   """
   q = db.Query(model)
   q.filter('scope', program)
-  link_id_operator = 'link_id'
+
   if isinstance(surveys, list):
-    if len(surveys) > 1:
-      link_id_operator += ' IN'
-    else:
-      surveys = surveys[0]
+    q.filter('link_id IN', surveys)
+    return q.fetch(1000)
+  else:
+    q.filter('link_id', surveys)
+    return q.get()
 
-  q.filter(link_id_operator, surveys)
 
-  return q.fetch(limit)
+def getMidtermProjectSurveyForProgram(program):
+  return getSurveysForProgram(ProjectSurvey, program, MIDTERM_ID)
+
+
+def getMidtermGradingProjectSurveyForProgram(program):
+  return getSurveysForProgram(GradingProjectSurvey, program, MIDTERM_ID)
+
+
+def getFinalProjectSurveyForProgram(program):
+  return getSurveysForProgram(ProjectSurvey, program, FINAL_ID)
+
+
+def getFinalGradingProjectSurveyForProgram(program):
+  return getSurveysForProgram(GradingProjectSurvey, program, FINAL_ID)
diff --git a/app/soc/modules/gsoc/views/admin.py b/app/soc/modules/gsoc/views/admin.py
index 9fb66fe..c479e97 100644
--- a/app/soc/modules/gsoc/views/admin.py
+++ b/app/soc/modules/gsoc/views/admin.py
@@ -14,8 +14,6 @@
 
 """Module for the admin pages."""
 
-import logging
-
 from google.appengine.api import taskqueue
 from google.appengine.api import users
 from google.appengine.ext import db
@@ -39,7 +37,7 @@
 from soc.modules.gsoc.logic import project as project_logic
 from soc.modules.gsoc.logic.proposal import getProposalsToBeAcceptedForOrg
 from soc.modules.gsoc.models.grading_project_survey import GradingProjectSurvey
-from soc.modules.gsoc.models.organization import GSoCOrganization
+from soc.modules.gsoc.models.grading_survey_group import GSoCGradingSurveyGroup
 from soc.modules.gsoc.models.profile import GSoCProfile
 from soc.modules.gsoc.models.profile import GSoCStudentInfo
 from soc.modules.gsoc.models.project import GSoCProject
@@ -406,6 +404,7 @@
     """
     mentor_evaluations = MentorEvaluationsDashboard(data)
     student_evaluations = StudentEvaluationsDashboard(data)
+    evaluation_group = EvaluationGroupDashboard(data)
 
     r = data.redirect
     r.program()
@@ -434,6 +433,13 @@
             'link': '',
             'subpage_links': student_evaluations.getSubpagesLink(),
         },
+        {
+            'name': 'evaluation_group',
+            'description': ugettext('Manage the results of the evaluation'),
+            'title': 'Evalutation Group',
+            'link': '',
+            'subpage_links': evaluation_group.getSubpagesLink(),
+        },
     ]
 
     super(EvaluationsDashboard, self).__init__(data, subpages)
@@ -631,21 +637,32 @@
     Args:
       data: The RequestData object
     """
+    r = data.redirect
+    r.program()
+
     subpages = [
         {
             'name': 'edit_evaluation_group',
             'description': ugettext('Create evaluation group'),
             'title': 'Create',
-            'link': '#'
-        },
-        {
-            'name': 'view_evaluation_group',
-            'description': ugettext('View evaluation group'),
-            'title': 'View',
-            'link': '#'
+            'link': r.urlOf('gsoc_grading_group')
         },
     ]
 
+    q = GSoCGradingSurveyGroup.all()
+    q.filter('program', data.program)
+
+    for group in q:
+      r.id(group.key().id())
+      subpages.append(
+        {
+            'name': 'view_evaluation_group_%s' % group.key().id(),
+            'description': ugettext('View this group'),
+            'title': 'View %s' % group.name,
+            'link': r.urlOf('gsoc_grading_record_overview')
+        }
+      )
+
     super(EvaluationGroupDashboard, self).__init__(data, subpages)
 
   def context(self):
diff --git a/app/soc/modules/gsoc/views/grading_record_details.py b/app/soc/modules/gsoc/views/grading_record_details.py
index 5d34eee..42fb702 100644
--- a/app/soc/modules/gsoc/views/grading_record_details.py
+++ b/app/soc/modules/gsoc/views/grading_record_details.py
@@ -21,6 +21,8 @@
 
 from django import http
 
+from melange.request import exception
+
 from soc.views import forms
 from soc.views.helper import lists
 from soc.views.helper import url_patterns
@@ -28,12 +30,79 @@
 from soc.views.template import Template
 
 from soc.modules.gsoc.logic import grading_record
+from soc.modules.gsoc.logic import survey
 from soc.modules.gsoc.models.grading_record import GSoCGradingRecord
+from soc.modules.gsoc.models.grading_survey_group import GSoCGradingSurveyGroup
 from soc.modules.gsoc.views import forms as gsoc_forms
 from soc.modules.gsoc.views.base import GSoCRequestHandler
 from soc.modules.gsoc.views.helper import url_patterns as gsoc_url_patterns
 from soc.modules.gsoc.views.helper.url_patterns import url
 
+class GradingGroupCreate(GSoCRequestHandler):
+  """View to display GradingRecord details.
+  """
+
+  def djangoURLPatterns(self):
+    return [
+        url(r'grading_records/group/%s$' % url_patterns.PROGRAM,
+         self, name='gsoc_grading_group'),
+    ]
+
+  def checkAccess(self, data, check, mutator):
+    check.isHost()
+
+  def context(self, data, check, mutator):
+    return {
+        'page_name': 'Grading Group Create Page',
+        'err': bool(data.GET.get('err', False))
+        }
+
+  def post(self, data, check, mutator):
+    """Handles the POST request when creating a Grading Group"""
+    student_survey = None
+    grading_survey = None
+    survey_type = None
+
+    if 'midterm' in data.POST:
+      student_survey = survey.getMidtermProjectSurveyForProgram(data.program)
+      grading_survey = survey.getMidtermGradingProjectSurveyForProgram(
+          data.program)
+      survey_type = 'Midterm'
+    elif 'final' in data.POST:
+      student_survey = survey.getFinalProjectSurveyForProgram(data.program)
+      grading_survey = survey.getFinalGradingProjectSurveyForProgram(
+          data.program)
+      survey_type = 'Final'
+    else:
+      raise exception.BadRequest('No valid evaluation type present')
+
+    if not student_survey or not grading_survey:
+      data.redirect.program()
+      return data.redirect.to('gsoc_grading_group', extra=['err=1'])
+
+    q = GSoCGradingSurveyGroup.all()
+    q.filter('student_survey', student_survey)
+    q.filter('grading_survey', grading_survey)
+
+    existing_group = q.get()
+
+    if existing_group:
+      data.redirect.id(existing_group.key().id())
+    else:
+      props = {
+          'name': '%s - %s Evaluation' %(data.program.name, survey_type),
+          'program': data.program,
+          'grading_survey': grading_survey,
+          'student_survey': student_survey,
+      }
+      new_group = GSoCGradingSurveyGroup(**props)
+      new_group.put()
+      data.redirect.id(new_group.key().id())
+
+    return data.redirect.to('gsoc_grading_record_overview')
+
+  def templatePath(self):
+    return 'modules/gsoc/grading_record/create_group.html'
 
 class GradingRecordsOverview(GSoCRequestHandler):
   """View to display all GradingRecords for a single group."""
diff --git a/app/soc/modules/seeder/logic/seeder.py b/app/soc/modules/seeder/logic/seeder.py
index 5062a2f..2d2dae1 100644
--- a/app/soc/modules/seeder/logic/seeder.py
+++ b/app/soc/modules/seeder/logic/seeder.py
@@ -22,6 +22,7 @@
 from google.appengine.ext import db
 from google.appengine.ext.db import _ReverseReferenceProperty
 from google.appengine.ext.db import ReferenceProperty
+from google.appengine.ext import ndb
 
 from mapreduce.control import start_map
 
@@ -68,6 +69,12 @@
   pass
 
 
+class KeyPropertyNotSpecifiedError(Error):
+  """Raised when the value of a KeyProperty is not specifed while should.
+  """
+  pass
+
+
 class Logic(object):
   """Contains logic for data seeding operations.
   """
@@ -436,12 +443,25 @@
       return result
 
     # If the property has choices, choose one of them randomly
-    if prop.choices:
+    if hasattr(prop, 'choices') and prop.choices:
       return prop.choices[random.randint(0, len(prop.choices)-1)]
 
+    # Special handling for ndb
+    property_class_name = prop.__class__.__name__
+    #print prop_name, vars(prop)
+    if hasattr(prop, '_repeated') and prop._repeated:
+      property_class_name = 'ListProperty'
+    if isinstance(prop, ndb.StringProperty) and hasattr(prop, '_validator') \
+        and prop._validator:
+      name = prop._validator.__name__
+      # Deduce which provider to use from the name of validator
+      property_class_name = name[0].upper() + name[1:name.find('_')] + 'Property'
+    if isinstance(prop, ndb.KeyProperty):
+      raise KeyPropertyNotSpecifiedError(
+          "The value of %s needs to be specified in properties" % prop_name)
     # Use relavant data provider to generate other properties
     # automatically
-    return self.genRandomValueForPropertyClass(prop.__class__)
+    return self.genRandomValueForPropertyClass(property_class_name)
 
   def seed_properties(self, model_class, properties=None, recurse=True,
           auto_seed_optional_properties=True):
@@ -466,10 +486,12 @@
       properties = {}
     else:
       properties = properties.copy()
-
-    items = model_class.properties().items()
-    if 'link_id' in model_class.properties().keys():
-      items += [('key_name', None)]
+    if issubclass(model_class, ndb.Model):
+      items = model_class._properties.items()
+    else:
+      items = model_class.properties().items()
+      if 'link_id' in model_class.properties().keys():
+        items += [('key_name', None)]
 
     # Produce all properties of model_class
     for prop_name, prop in items:
@@ -508,15 +530,16 @@
       data.put()
     return data
 
-  def genRandomValueForPropertyClass(self, property_class):
-    """Generates a value for property_class randomly.
+  def genRandomValueForPropertyClass(self, property_class_name):
+    """Generates a value for property_class_name randomly.
 
-    The generator uses any of the data provider of property_class
-    starting with 'Random'.
+    The generator uses any of the data provider of property_class_name
+    starting with 'Random' if there is any;
+    otherwise the first provider is used.
     """
     value = None
     providers_dict = seeder_providers_logic.getProviders()
-    providers_list = providers_dict[property_class.__name__]
+    providers_list = providers_dict[property_class_name]
     provider_class = None
     for provider_class in providers_list:
       if provider_class.__name__.startswith('Random'):
diff --git a/app/soc/templates/modules/gci/admin/component.html b/app/soc/templates/modules/gci/admin/component.html
deleted file mode 100644
index 27aa421..0000000
--- a/app/soc/templates/modules/gci/admin/component.html
+++ /dev/null
@@ -1,21 +0,0 @@
-{% comment %}
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-{% endcomment %}
-
-<div id="{{ name }}-component" class="block-text">
-  <h4 id="title-section-{{ name }}">{{ title|safe }}</h4>
-  <div class="block-content">
-    {% block component_content %}
-    {% endblock component_content %}
-  </div>
-</div>
diff --git a/app/soc/templates/modules/gci/admin/list_component.html b/app/soc/templates/modules/gci/admin/list_component.html
deleted file mode 100644
index 9ceacb9..0000000
--- a/app/soc/templates/modules/gci/admin/list_component.html
+++ /dev/null
@@ -1,18 +0,0 @@
-{% extends "modules/gci/dashboard/component.html" %}
-{% comment %}
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-{% endcomment %}
-
-{% block component_content %}
-  {% include "soc/list/lists.html" %}
-{% endblock component_content %}
diff --git a/app/soc/templates/modules/gsoc/accept_proposals/proposal_duplicate.html b/app/soc/templates/modules/gsoc/accept_proposals/proposal_duplicate.html
deleted file mode 100644
index 7a7b9db..0000000
--- a/app/soc/templates/modules/gsoc/accept_proposals/proposal_duplicate.html
+++ /dev/null
@@ -1,38 +0,0 @@
-{% comment %}
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-{% endcomment %}
-
-<li>
-  Student:<strong> {{ duplicate.student.name }}
-  </strong>
-  <a href="mailto:{{ duplicate.student.email }}">{{ duplicate.student.email }}</a>
-  <ul>
-  {% for key, org in orgs.items %}
-  <li>Organization:
-    <a href="{{ org.link }}">{{ org.name }}</a>
-    Admins: {% for oa in org.admins %}
-    <a href="mailto: {{ oa.email }}">"{{ oa.name }}" &lt;{{ oa.email }}&gt;</a>{% if not forloop.last%},  {% endif %}
-    {% endfor %}
-    <ul>
-    {% for proposal in org.proposals %}
-      <li>Proposal:
-        <a href="{{ proposal.link }}">
-          {{ proposal.title }}
-        </a>
-      </li>
-    {% endfor %}
-    </ul>
-  </li>
-  {% endfor %}
-  </ul>
-</li>
diff --git a/app/soc/templates/modules/gsoc/accepted_orgs/_project_list.html b/app/soc/templates/modules/gsoc/accepted_orgs/_project_list.html
deleted file mode 100644
index c03341b..0000000
--- a/app/soc/templates/modules/gsoc/accepted_orgs/_project_list.html
+++ /dev/null
@@ -1,20 +0,0 @@
-{% comment %}
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-{% endcomment %}
-
-<div id="accepted-org-list-component" class="block block-text">
-  <h4 id="title-section-accepted-org-list">Accepted organizations</h4>
-  <div class="block-content">
-    {% include "soc/list/lists.html" %}
-  </div>
-</div>
diff --git a/app/soc/templates/modules/gsoc/admin/_slots_list.html b/app/soc/templates/modules/gsoc/admin/_slots_list.html
deleted file mode 100644
index c03341b..0000000
--- a/app/soc/templates/modules/gsoc/admin/_slots_list.html
+++ /dev/null
@@ -1,20 +0,0 @@
-{% comment %}
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-{% endcomment %}
-
-<div id="accepted-org-list-component" class="block block-text">
-  <h4 id="title-section-accepted-org-list">Accepted organizations</h4>
-  <div class="block-content">
-    {% include "soc/list/lists.html" %}
-  </div>
-</div>
diff --git a/app/soc/templates/modules/gsoc/grading_record/create_group.html b/app/soc/templates/modules/gsoc/grading_record/create_group.html
new file mode 100644
index 0000000..973ff02
--- /dev/null
+++ b/app/soc/templates/modules/gsoc/grading_record/create_group.html
@@ -0,0 +1,42 @@
+{% extends "modules/gsoc/base.html" %}
+{% comment %}
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+{% endcomment %}
+
+{% block stylesheets %}
+  {{ block.super }}
+  <link rel="stylesheet" type="text/css" media="screen" href="/soc/content/{{ app_version }}/css/v2/gsoc/forms.css" />
+  <link rel="stylesheet" type="text/css" media="screen" href="/soc/content/{{ app_version }}/css/v2/gsoc/uniform.default.css" />
+{% endblock stylesheets %}
+
+{% block page_content %}
+<form action="" method="post" id="form" class="form-register">
+  {% if err %}
+    <div id="flash-message" class="flash-error">
+    <p>
+      Make sure both the evaluation for the mentor and the student exists before attempting to
+      create an evaluation group.
+    </p>
+    </div>
+  {% endif %}
+
+  <br/>
+  <div id="form-register-fieldset-button-row" class="row button-row">
+    <input id="form-register-submit" type="submit" name="midterm" value="Create Midterm Evaluation group" class="submit" />
+  </div>
+  <div id="form-register-fieldset-button-row" class="row button-row">
+    <input id="form-register-submit" type="submit" name="final" value="Create Final Evaluation group" class="submit" />
+  </div>
+</form>
+<!-- end form -->
+{% endblock page_content %}
\ No newline at end of file
diff --git a/app/soc/templates/modules/gsoc/notification/new_connection_message.html b/app/soc/templates/modules/gsoc/notification/new_connection_message.html
deleted file mode 100644
index 47b0b75..0000000
--- a/app/soc/templates/modules/gsoc/notification/new_connection_message.html
+++ /dev/null
@@ -1,26 +0,0 @@
-{% extends "v2/soc/notification/base.html" %}
-{% comment %}
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-{% endcomment %}
-{% block content %}
-<p>
-{{ message_sender }} has sent the following message:
-</p>
-<div id="message">
-<p>
-{{ message_content|safe }}
-</p>
-</div>
-<p>
-You can see this message online or post a reply <a href={{ view_connection_url }}>here</a>.
-{% endblock %}
diff --git a/app/soc/templates/modules/gsoc/statistic/base.html b/app/soc/templates/modules/gsoc/statistic/base.html
deleted file mode 100644
index 430fc85..0000000
--- a/app/soc/templates/modules/gsoc/statistic/base.html
+++ /dev/null
@@ -1,69 +0,0 @@
-{% extends "modules/gsoc/base.html" %}
-{% comment %}
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-{% endcomment %}
-
-{% block stylesheets %}
-  {{ block.super }}
-  <link rel="stylesheet" type="text/css" media="screen" href="/soc/content/{{ app_version }}/css/gsoc/buttons.css" />
-  <link rel="stylesheet" type="text/css" media="screen" href="/soc/content/{{ app_version }}/css/gsoc/forms.css" />
-  <link rel="stylesheet" type="text/css" media="screen" href="/soc/content/{{ app_version }}/css/gsoc/uniform.default.css" />
-{% endblock stylesheets %}
-
-{% block usermenu %}
-  {% if manage_actions %}
-  {{ manage_actions.render }}
-  {% endif %}
-{% endblock usermenu %}
-
-
-{% block page_content %}
-
-<script src="https://www.google.com/jsapi" type="text/javascript"></script>
-
-<div id='statistic-select-div' class='selector'>
-<span>Statistic name</span>
-<select id='statistic-select' style='opacity: 0'>
-  {% for statistic in statistics %}
-  <option id='{{ statistic.name }}'>{{ statistic.human_name }}</option>
-  {% endfor %}
-</select>
-</div>
-<div id='statistic-select-div' class='selector'>
-<span>Visualization</span>
-<select id='statistic-visualization-select' style='opacity: 0'>
-</select>
-</div>
-<div id='statistic-presentation-div'>
-
-</div>
-
-{% endblock page_content %}
-
-{% block dependencies %}
-  [
-    dep.uniform,
-    dep.counter,
-    dep.melange.action,
-    null,
-    tc("/soc/content/{{ app_version }}/js/templates/modules/gsoc/statistic.js", {
-      'urls': '{{ fetch_urls|escapejs }}',
-      'manage_urls': '{{ manage_urls|escapejs }}',
-      'visualizations': '{{ visualizations|escapejs }}',
-      'visibilities': '{{ visibilities|escapejs }}'
-      }),
-    function () {
-      melange.action.createCluetip();
-    }
-  ]
-{% endblock dependencies %}
diff --git a/app/summerofcode/callback.py b/app/summerofcode/callback.py
index 0139ad0..ab70e4f 100644
--- a/app/summerofcode/callback.py
+++ b/app/summerofcode/callback.py
@@ -14,8 +14,6 @@
 
 """Module containing the Summer Of Code callback."""
 
-from summerofcode.views import project_manage
-
 
 class Callback(object):
   """Callback object that handles interaction between the core."""
diff --git a/tests/app/soc/modules/gsoc/views/test_grading_record_details.py b/tests/app/soc/modules/gsoc/views/test_grading_record_details.py
index 05386d8..72b25b7 100644
--- a/tests/app/soc/modules/gsoc/views/test_grading_record_details.py
+++ b/tests/app/soc/modules/gsoc/views/test_grading_record_details.py
@@ -28,6 +28,80 @@
 GRADING_SURVEY_GROUP_NAME = 'Test Grading Survey Group'
 
 
+class GradingGroupCreateTest(test_utils.GSoCDjangoTestCase):
+  """Test GradingGroupSurvey creation page.
+  """
+
+  def setUp(self):
+    self.init()
+    self.data.createHost()
+
+    evaluation_helper = survey_utils.SurveyHelper(self.gsoc, self.dev_test)
+    midterm_prop = {'link_id': 'midterm'}
+    self.grading_survey = evaluation_helper.createMentorEvaluation(
+            override=midterm_prop)
+    self.student_survey = evaluation_helper.createStudentEvaluation(
+            override=midterm_prop)
+
+  def testCreateGradingSurveyGroupGet(self):
+    """Tests the GET request to the create page.
+    """
+    response = self.get(self.getUrl())
+    self.assertResponseOK(response)
+    self.assertGSoCTemplatesUsed(response)
+    self.assertTemplateUsed(
+        response, 'modules/gsoc/grading_record/create_group.html')
+
+  def testCreateGradingSurveyGroup(self):
+    """Tests the request to create a group.
+    """
+    # Create a group for the midterm eval.
+    response = self.buttonPost(self.getUrl(), 'midterm')
+
+    group = gsg_model.GSoCGradingSurveyGroup.all().get()
+    self.assertResponseRedirect(response, self.getOverviewUrl(group))
+
+    expected_name = '%s - Midterm Evaluation' %self.program.name
+    self.assertEqual(group.name, expected_name)
+    self.assertEqual(group.program.key(), self.program.key())
+    self.assertEqual(group.grading_survey.key(), self.grading_survey.key())
+    self.assertEqual(group.student_survey.key(), self.student_survey.key())
+
+  def testCreateGradingSurveyGroupRedirectsWhenExists(self):
+    """Tests that the create page redirects when the group already exists.
+    """
+    properties = {
+        'name': GRADING_SURVEY_GROUP_NAME,
+        'program': self.program,
+        'grading_survey': self.grading_survey,
+        'student_survey': self.student_survey,
+    }
+    group = self.seed(gsg_model.GSoCGradingSurveyGroup, properties)
+
+    response = self.buttonPost(self.getUrl(), 'midterm')
+    self.assertResponseRedirect(response, self.getOverviewUrl(group))
+
+  def testCreateGradingSurveyGroupErrorsWhenEvalDoesNotExist(self):
+    """Tests that the create page returns an error when the group can not be
+    created.
+    """
+    response = self.buttonPost(self.getUrl(), 'final')
+    self.assertResponseRedirect(response, self.getUrl() + '?err=1')
+
+  def testCreateGradingSurveyGroupBadRequestOnInvalidInput(self):
+    """Tests that a BadRequest response is returned on invalid input data.
+    """
+    response = self.buttonPost(self.getUrl(), 'notARealButton')
+    self.assertResponseBadRequest(response)
+
+  def getUrl(self):
+    return '/gsoc/grading_records/group/%s' % self.program.key().name()
+
+  def getOverviewUrl(self, group):
+    return '/gsoc/grading_records/overview/%s/%d' % (
+        self.program.key().name(), group.key().id())
+
+
 class GradingRecordsOverviewTest(test_utils.GSoCDjangoTestCase):
   """Test grading records overview list page.
   """
@@ -61,7 +135,7 @@
   def testGradingRecordsOverviewGet(self):
     grading_survey_group = self.createGradingSurveyGroup()
     url = '/gsoc/grading_records/overview/%s/%d' % (
-        self.program.key().name(), grading_survey_group.key().id(),)
+        self.program.key().name(), grading_survey_group.key().id())
     response = self.get(url)
     self.assertResponseOK(response)
     self.assertGradingRecordsOverviewTemplatesUsed(response)
diff --git a/tests/app/soc/modules/seeder/__init__.py b/tests/app/soc/modules/seeder/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/app/soc/modules/seeder/__init__.py
diff --git a/tests/app/soc/modules/seeder/logic/__init__.py b/tests/app/soc/modules/seeder/logic/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/app/soc/modules/seeder/logic/__init__.py
diff --git a/tests/app/soc/modules/seeder/logic/ndb_models.py b/tests/app/soc/modules/seeder/logic/ndb_models.py
new file mode 100644
index 0000000..c4fb704
--- /dev/null
+++ b/tests/app/soc/modules/seeder/logic/ndb_models.py
@@ -0,0 +1,34 @@
+# Copyright 2013 the Melange authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""ndb model classes for seeder testing."""
+
+from google.appengine.ext import ndb
+
+from melange.appengine import db as db_util
+
+
+class NdbDummyModel(ndb.Model):
+  """A ndb dummy model class for seeder testing."""
+  boolean = ndb.BooleanProperty(required=True)
+  name = ndb.StringProperty(required=True)
+  link = ndb.StringProperty(required=True, validator=db_util.link_validator)
+  email = ndb.StringProperty(required=True, validator=db_util.email_validator)
+  numbers = ndb.IntegerProperty(repeated=True)
+
+
+class NdbKeyProperty(ndb.Model):
+  """A ndb model class with KeyProperty for seeder testing."""
+  name = ndb.StringProperty(required=True)
+  key = ndb.KeyProperty(required=True)
diff --git a/tests/app/soc/modules/seeder/logic/test_seeder.py b/tests/app/soc/modules/seeder/logic/test_seeder.py
new file mode 100644
index 0000000..9e46c6e
--- /dev/null
+++ b/tests/app/soc/modules/seeder/logic/test_seeder.py
@@ -0,0 +1,47 @@
+# Copyright 2013 the Melange authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Tests for soc.modules.seeder.logic.seeder."""
+
+import unittest
+
+from google.appengine.ext import ndb
+
+from soc.modules.seeder.logic.seeder import logic as seeder_logic
+from soc.modules.seeder.logic import seeder
+
+from tests.app.soc.modules.seeder.logic import ndb_models
+
+
+class SeederTest(unittest.TestCase):
+  """Unit tests for seeder logic."""
+
+  def testSeedingNdbDummyModel(self):
+    """Tests that the entity can be seeded properly."""
+    entity = seeder_logic.seed(ndb_models.NdbDummyModel)
+    self.assertIsNotNone(entity)
+
+  def testSeedingNdbKeyPropertyNotSpecified(self):
+    """Tests exception raised when any ndb KeyProperty is not specified."""
+    with self.assertRaises(seeder.KeyPropertyNotSpecifiedError):
+      seeder_logic.seed(ndb_models.NdbKeyProperty)
+
+  def testSeedingNdbKeyPropertySpecified(self):
+    """Tests the entity can be seeded properly if ndb KeyProperty specified."""
+    ndb_dummy_entity = seeder_logic.seed(ndb_models.NdbDummyModel)
+    entity = seeder_logic.seed(ndb_models.NdbKeyProperty,
+        properties={'key': ndb_dummy_entity.key})
+    self.assertIsNotNone(entity)
+    self.assertIsInstance(entity.key, ndb.Key)
+    self.assertIsInstance(entity._properties['key'], ndb.KeyProperty)
diff --git a/tests/run.py b/tests/run.py
index d0be80e..77b4c5d 100644
--- a/tests/run.py
+++ b/tests/run.py
@@ -116,6 +116,7 @@
       'soc.modules.soc_core.callback',
       'soc.modules.gsoc.callback',
       'soc.modules.gci.callback',
+      'summerofcode.callback'
       ]
 
   current_core.registerModuleCallbacks(callback_module_names)