Merge branch 'master' of https://code.google.com/p/soc
diff --git a/app/soc/content/css/v2/gci/style.css b/app/soc/content/css/v2/gci/style.css
index 65f5834..db03710 100644
--- a/app/soc/content/css/v2/gci/style.css
+++ b/app/soc/content/css/v2/gci/style.css
@@ -2264,6 +2264,9 @@
 	.block-leaderboard-top-scores .leaderboard-top-tasks {
 		width: 70px;
 	}
+	.block-leaderboard-top-organization .leaderboard-top-organization {
+		width: 90px;
+	}
 .block-leaderboard-all-scores {/*Leaderboard all scores*/
 
 }
diff --git a/app/soc/models/profile.py b/app/soc/models/profile.py
index 48fea37..498b671 100644
--- a/app/soc/models/profile.py
+++ b/app/soc/models/profile.py
@@ -506,6 +506,12 @@
     else:
       return self.name()
 
+  def full_name(self):
+    """Property which returns given name followed by surname and separated
+    by a single space character.
+    """
+    return '%s %s' % (self.given_name, self.surname)
+
   def shipping_name(self):
     """Property recipient_name that returns shipping name if shipping
     address is set else the given name and surname.
diff --git a/app/soc/modules/gci/logic/program.py b/app/soc/modules/gci/logic/program.py
index 3f97317..ddac7db 100644
--- a/app/soc/modules/gci/logic/program.py
+++ b/app/soc/modules/gci/logic/program.py
@@ -17,6 +17,8 @@
 """GCI logic for program.
 """
 
+from soc.modules.gci.models import profile as profile_model
+
 
 def getMostRecentProgram(data):
   """Returns the most recent program.
@@ -25,3 +27,19 @@
     The program link_id for the most recent gsoc program.
   """
   return data.site.latest_gci
+
+
+def getWinnersForProgram(program):
+  """Returns the Grand Prize Winners for the specified program.
+
+  Args:
+    program: GCIProgram instance for which to retrieve the winners
+
+  Returns:
+    a list of GCIProfile instances containing winners of the program
+  """
+  student_keys = profile_model.GCIStudentInfo.all(keys_only=True).filter(
+      'is_winner', True).filter('program', program).fetch(1000)
+
+  return profile_model.GCIProfile.get(
+      [key.parent() for key in student_keys])
diff --git a/app/soc/modules/gci/models/profile.py b/app/soc/modules/gci/models/profile.py
index f924b60..0259edf 100644
--- a/app/soc/modules/gci/models/profile.py
+++ b/app/soc/modules/gci/models/profile.py
@@ -16,7 +16,6 @@
 
 """This module contains the  GCIProfile Model."""
 
-
 from google.appengine.ext import db
 from google.appengine.ext import blobstore
 
@@ -48,6 +47,7 @@
   Parent:
     soc.modules.gci.models.profile.GCIProfile
   """
+
   #: number of tasks completed by the student
   number_of_completed_tasks = db.IntegerProperty(default=0)
 
@@ -80,3 +80,10 @@
 
   #: Stores whether the student id form is verified by the program host.
   student_id_form_verified = db.BooleanProperty(default=False)
+
+  #: GCIOrganiztion for which the student is a winner
+  winner_for = db.ReferenceProperty(
+      required=False, collection_name='winners')
+
+  #: Whether the student is a grand prize winner of the program
+  is_winner = db.BooleanProperty(default=False, required=False)
diff --git a/app/soc/modules/gci/models/program.py b/app/soc/modules/gci/models/program.py
index b5d9e35..b658f03 100644
--- a/app/soc/modules/gci/models/program.py
+++ b/app/soc/modules/gci/models/program.py
@@ -25,6 +25,16 @@
 import soc.models.program
 
 
+class WinnerSelectionType(object):
+  """Enumerates all winner selection types for GCI Programs.
+  """
+
+  ORG_NOMINATED = 'Nominated by Organizations'
+  GLOBAL_RANKING = 'Global ranking'
+
+WINNER_SELECTION_TYPES = [
+    WinnerSelectionType.ORG_NOMINATED, WinnerSelectionType.GLOBAL_RANKING]
+
 
 class GCIProgramMessages(soc.models.program.ProgramMessages):
   """The GCIProgramMessages model.
@@ -49,6 +59,12 @@
   nr_simultaneous_tasks.help_text = ugettext(
       'Number of tasks students can work on simultaneously in the program.')
 
+  #: Determines what winner selection model is used for the program
+  winner_selection_type = db.StringProperty(required=True,
+      verbose_name=ugettext('Winner selection type'),
+      choices=WINNER_SELECTION_TYPES,
+      default=WinnerSelectionType.ORG_NOMINATED)
+
   #: Required property containing the number of winners to be selected in
   #: the program. Defaults to 10
   nr_winners = db.IntegerProperty(
diff --git a/app/soc/modules/gci/views/common_templates.py b/app/soc/modules/gci/views/common_templates.py
index dfc9ceb..5c87587 100644
--- a/app/soc/modules/gci/views/common_templates.py
+++ b/app/soc/modules/gci/views/common_templates.py
@@ -16,12 +16,15 @@
 
 """This module contains the templates which are used across the views."""
 
+import re
 
 from soc.views.base_templates import ProgramSelect
 from soc.views.template import Template
 
+from soc.modules.gci.logic import program as program_logic
 from soc.modules.gci.logic import ranking as ranking_logic
 from soc.modules.gci.logic.ranking import winnersForProgram
+from soc.modules.gci.views import forms
 from soc.modules.gci.views.helper import url_names
 
 
@@ -94,7 +97,7 @@
     return 'v2/modules/gci/common_templates/_program_select.html'
 
 
-class Winners(Template):
+class GlobalRankingWinners(Template):
   """Templates to display winners of the program.
   """
 
@@ -107,3 +110,68 @@
 
   def templatePath(self):
     return 'v2/modules/gci/common_templates/_winners.html'
+
+
+class OrgNominatedWinners(Template):
+  """Template to display Grand Prize Winners of the program in
+  which each organization nominates students who receive the award.
+  """
+
+  class Winner(object):
+    """Representation of a single winner used by the template."""
+
+    def __init__(self, profile):
+      self.profile = profile
+      self.avatar_name = None
+      self.avatar_prefix = None
+
+      if profile.avatar:
+        avatar_groups = re.findall(forms.RE_AVATAR_COLOR, profile.avatar)
+        # Being a bit pessimistic
+        if avatar_groups:
+          # We only want the first match, so pick group[0]
+          name, prefix = avatar_groups[0]
+          self.avatar_name = '%s-%s.jpg' % (name, prefix)
+          self.avatar_prefix = prefix
+
+    def organization(self):
+      """Returns GCIOrganization associated with the winner."""
+      return self.profile.student_info.winner_for
+
+    def avatarPrefix(self):
+      """Returns avatar prefix associated with the winner."""
+      return self.avatar_prefix
+
+    def avatarName(self):
+      """Returns avatar name associated with the winner."""
+      return self.avatar_name
+
+  def context(self):
+    winners = self._getWinnersForProgram(self.data.program)
+
+    return {
+        'winners': winners,
+        }
+
+  def templatePath(self):
+    return 'v2/modules/gci/common_templates/_org_nominated_winners.html'
+
+  def _getWinnersForProgram(self, program):
+    """Returns the Grand Prize Winners for the specified program.
+
+    Args:
+      program: GCIProgram instance for which to retrieve the winners.
+
+    Returns:
+      a list containing GCIProfile instances which represent the winners
+      ordered by the first name.
+    """
+    winners = []
+
+    profiles = program_logic.getWinnersForProgram(program)
+    for profile in profiles:
+      winners.append(OrgNominatedWinners.Winner(profile))
+
+    winners.sort(key=lambda o: o.profile.given_name.lower())
+
+    return winners
diff --git a/app/soc/modules/gci/views/homepage.py b/app/soc/modules/gci/views/homepage.py
index ba54ef1..983d28d 100644
--- a/app/soc/modules/gci/views/homepage.py
+++ b/app/soc/modules/gci/views/homepage.py
@@ -22,6 +22,7 @@
 
 from soc.modules.gci.logic import organization as org_logic
 from soc.modules.gci.logic import task as task_logic
+from soc.modules.gci.models import program as program_model
 from soc.modules.gci.views import common_templates
 from soc.modules.gci.views.base import GCIRequestHandler
 from soc.modules.gci.views.helper.url_patterns import url
@@ -247,6 +248,27 @@
         context['featured_task'] = FeaturedTask(self.data, featured_task)
 
     if self.data.is_host or self.data.timeline.winnersAnnounced():
-      context['winners'] = common_templates.Winners(self.data)
+      context['winners'] = self._getWinnersTemplate(
+          self.data.program.winner_selection_type)
 
     return context
+
+  def _getWinnersTemplate(self, winner_selection_type):
+    """Factory method that returns a template to displays the Grand
+    Prize Winners of the program with the specified winner selection type.
+
+    Args:
+      winner_selection_type: the specified WinnerSelectionType.
+
+    Returns:
+      a template appropriate for the specified winner selection type.
+    """
+    if (winner_selection_type ==
+        program_model.WinnerSelectionType.ORG_NOMINATED):
+      return common_templates.OrgNominatedWinners(self.data)
+    elif (winner_selection_type ==
+        program_model.WinnerSelectionType.GLOBAL_RANKING):
+      return common_templates.GlobalRankingWinners(self.data)
+    else:
+      raise ValueError(
+         'Invalid value of winner_selection_type %s' % winner_selection_type)
diff --git a/app/soc/modules/gci/views/leaderboard.py b/app/soc/modules/gci/views/leaderboard.py
index 88cac3d..ff239d7 100644
--- a/app/soc/modules/gci/views/leaderboard.py
+++ b/app/soc/modules/gci/views/leaderboard.py
@@ -144,7 +144,7 @@
             self.data, url_names.GCI_LEADERBOARD),
         }
     if self.data.is_host or self.data.timeline.winnersAnnounced():
-      context['winners'] = common_templates.Winners(self.data)
+      context['winners'] = common_templates.GlobalRankingWinners(self.data)
 
     return context
 
diff --git a/app/soc/modules/gci/views/profile.py b/app/soc/modules/gci/views/profile.py
index 8fc7926..35bec29 100644
--- a/app/soc/modules/gci/views/profile.py
+++ b/app/soc/modules/gci/views/profile.py
@@ -59,13 +59,15 @@
 GCI_PROFILE_EXCLUDE = ['automatic_task_subscription', 'notify_comments',
                        'photo_url']
 
+GCI_STUDENT_EXCLUDE = ['is_winner', 'winner_for']
+
 PROFILE_EXCLUDE = profile.PROFILE_EXCLUDE + GCI_PROFILE_EXCLUDE
 
 PREFILL_PROFILE_EXCLUDE = profile.PREFILL_PROFILE_EXCLUDE + GCI_PROFILE_EXCLUDE
 
-STUDENT_EXCLUDE = PROFILE_EXCLUDE
+STUDENT_EXCLUDE = PROFILE_EXCLUDE + GCI_STUDENT_EXCLUDE
 
-PREFILL_STUDENT_EXCLUDE = PREFILL_PROFILE_EXCLUDE
+PREFILL_STUDENT_EXCLUDE = PREFILL_PROFILE_EXCLUDE + GCI_STUDENT_EXCLUDE
 
 SHOW_STUDENT_EXCLUDE = STUDENT_EXCLUDE + [
     'birth_date',
@@ -256,7 +258,7 @@
         'number_of_completed_tasks', 'task_closed', 'parental_form_mail',
         'consent_form', 'consent_form_verified', 'consent_form_two',
         'student_id_form', 'major', 'student_id_form_verified', 'degree',
-        'school', 'school_type', 'program',
+        'school', 'school_type', 'program', 'is_winner', 'winner_for'
     ]
     widgets = forms.choiceWidgets(
         model, ['school_country', 'school_type', 'degree'])
diff --git a/app/soc/templates/v2/modules/gci/common_templates/_org_nominated_winners.html b/app/soc/templates/v2/modules/gci/common_templates/_org_nominated_winners.html
new file mode 100644
index 0000000..761dfe5
--- /dev/null
+++ b/app/soc/templates/v2/modules/gci/common_templates/_org_nominated_winners.html
@@ -0,0 +1,14 @@
+<div class="block block-leaderboard-scores block-leaderboard-top-scores">
+  <div class="block-title">Winners</div>
+  <div class="block-content">
+    <table border="0" cellpadding="0" cellspacing="0">
+      {% for winner in winners %}
+      <tr>
+        <td class="leaderboard-top-color-block blue"><img src="/soc/content/{{ app_version }}/images/gci/avatars/{{ winner.avatarPrefix }}/{{ winner.avatarName }}"></img></td>
+        <td class="leaderboard-top-name">{{ winner.profile.full_name|escape }}</td>
+        <td class="leaderboard-top-organization">{{ winner.organization.name|escape }}</td>
+      </tr>
+      {% endfor %}
+    </table>
+  </div>
+</div>
diff --git a/tests/profile_utils.py b/tests/profile_utils.py
index 694a339..e11065a 100644
--- a/tests/profile_utils.py
+++ b/tests/profile_utils.py
@@ -410,9 +410,15 @@
 
     self.createProfile()
 
-    properties = {'key_name': self.profile.key().name(), 'parent': self.profile,
-                  'school': None, 'number_of_completed_tasks': 0,
-                  'program': self.program}
+    properties = {
+        'key_name': self.profile.key().name(),
+        'parent': self.profile,
+        'school': None,
+        'number_of_completed_tasks': 0,
+        'program': self.program,
+        'is_winner': False,
+        'winner_for': None
+    }
     properties.update(kwargs)
 
     self.profile.student_info = self.seed(GCIStudentInfo, properties)