Bring Nathaniel's recent work onto master.
diff --git a/app/mapreduce.yaml b/app/mapreduce.yaml
index 7976e50..699fe32 100644
--- a/app/mapreduce.yaml
+++ b/app/mapreduce.yaml
@@ -32,13 +32,12 @@
- name: entity_kind
value: soc.modules.gsoc.models.organization.GSoCOrganization
-- name: GSoCConvertProfile
+- name: GSoCConvertProgramScopedModel
mapper:
input_reader: mapreduce.input_readers.DatastoreKeyInputReader
- handler: soc.mapreduce.convert_profile.process
+ handler: soc.mapreduce.convert_program_scoped_model.process
params:
- name: entity_kind
- value: soc.modules.gsoc.models.profile.GSoCProfile
- name: GSoCConvertProjectMentors
mapper:
@@ -203,4 +202,4 @@
handler: soc.mapreduce.convert_program.process
params:
- name: entity_kind
- value: soc.modules.gsoc.models.program.GCIProgram
+ value: soc.modules.gci.models.program.GCIProgram
diff --git a/app/soc/logic/site.py b/app/soc/logic/site.py
index ce79280..1e94425 100644
--- a/app/soc/logic/site.py
+++ b/app/soc/logic/site.py
@@ -23,7 +23,7 @@
def singleton():
"""Return singleton Site settings entity, since there is always only one."""
- return site.Site.get_or_insert('site', link_id='site')
+ return site.Site.get_or_insert('site')
def xsrfSecretKey(settings):
diff --git a/app/soc/mapreduce/convert_profile.py b/app/soc/mapreduce/convert_profile.py
deleted file mode 100644
index 2f17554..0000000
--- a/app/soc/mapreduce/convert_profile.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# Copyright 2011 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.
-
-"""GSoCProfile updating MapReduce.
-"""
-
-
-import logging
-
-from google.appengine.ext import db
-
-from mapreduce import operation
-
-from soc.models.user import User
-from soc.modules.gsoc.models.profile import GSoCProfile
-
-
-def process(profile_key):
- def convert_profile_txn():
- profile = db.get(profile_key)
- if not profile:
- logging.error("Missing profile for key '%s'." % profile_key)
- return False
- profile._fix_name(commit=False)
- profile.is_student = bool(profile.student_info)
- profile.org_admin_for = list(set(profile.org_admin_for))
- profile.mentor_for = list(set(profile.org_admin_for + profile.mentor_for))
- profile.is_org_admin = bool(profile.org_admin_for)
- profile.is_mentor = bool(profile.mentor_for)
- profile.put()
- return (profile.is_student, profile.is_org_admin, profile.is_mentor)
-
- result = db.run_in_transaction(convert_profile_txn)
-
- if not result:
- yield operation.counters.Increment("missing_profile")
- return
-
- is_student, is_admin, is_mentor = result
-
- if is_student:
- yield operation.counters.Increment("student_profiles_converted")
-
- if is_admin:
- yield operation.counters.Increment("admin_profiles_converted")
- elif is_mentor:
- yield operation.counters.Increment("mentor_profiles_converted")
-
- if is_mentor:
- yield operation.counters.Increment("only_mentor_profiles_converted")
-
- yield operation.counters.Increment("profiles_converted")
diff --git a/app/soc/mapreduce/convert_program.py b/app/soc/mapreduce/convert_program.py
index b93a0a0..3313c7a 100644
--- a/app/soc/mapreduce/convert_program.py
+++ b/app/soc/mapreduce/convert_program.py
@@ -22,5 +22,5 @@
program.program_id = program.link_id
program.sponsor = program.scope
- yield operation.db.Put(survey)
+ yield operation.db.Put(program)
yield operation.counters.Increment("program_updated")
diff --git a/app/soc/mapreduce/convert_program_scoped_model.py b/app/soc/mapreduce/convert_program_scoped_model.py
new file mode 100644
index 0000000..076d55d
--- /dev/null
+++ b/app/soc/mapreduce/convert_program_scoped_model.py
@@ -0,0 +1,57 @@
+# 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.
+
+"""MapReduce that can be used to update models for which scope is defined
+as program. It will copy the scope property and save it under program property.
+
+Currently, it can be run for GCIOrganization, GCIProfile, GSoCOrganization,
+GSoCProfile.
+"""
+
+import logging
+
+from google.appengine.ext import db
+from mapreduce import operation
+
+from soc.modules.gci.models.organization import GCIOrganization
+from soc.modules.gci.models.profile import GCIProfile
+from soc.modules.gci.models.program import GCIProgram
+from soc.modules.gsoc.models.organization import GSoCOrganization
+from soc.modules.gsoc.models.profile import GSoCProfile
+from soc.modules.gsoc.models.program import GSoCProgram
+
+
+def get_model_class(kind):
+ return globals()[kind]
+
+def process(entity_key):
+ def convert_entity_txn():
+ entity = db.get(entity_key)
+ if not entity:
+ logging.error('Missing entity for key %s.' % entity_key)
+ return False
+
+ # assign program property by its key
+ model_class = get_model_class(entity.kind())
+ entity.program = model_class.scope.get_value_for_datastore(entity)
+
+ db.put(entity)
+ return True
+
+ result = db.run_in_transaction(convert_entity_txn)
+
+ if result:
+ yield operation.counters.Increment('updated_profile')
+ else:
+ yield operation.counters.Increment('missing_profile')
diff --git a/app/soc/models/organization.py b/app/soc/models/organization.py
index 9789848..7b61ab0 100644
--- a/app/soc/models/organization.py
+++ b/app/soc/models/organization.py
@@ -12,20 +12,28 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""This module contains the Organization Model.
-"""
-
+"""This module contains the Organization Model."""
from google.appengine.ext import db
from django.utils.translation import ugettext
import soc.models.group
+import soc.models.program
class Organization(soc.models.group.Group):
- """Organization details.
- """
+ """Organization details."""
+
+ #: A reference to program entity to which the organization corresponds.
+ #: A single organization profile corresponds only to one program.
+ #: A new one has to exist for each program if the organization still
+ #: participates.
+ # TODO(daniel): make this field required when it is updated for
+ # all existing entities
+ program = db.ReferenceProperty(
+ reference_class=soc.models.program.Program, required=False,
+ collection_name='organizations')
#: Optional development mailing list.
dev_mailing_list = db.StringProperty(required=False,
diff --git a/app/soc/models/profile.py b/app/soc/models/profile.py
index ca04d34..ddab17f 100644
--- a/app/soc/models/profile.py
+++ b/app/soc/models/profile.py
@@ -121,6 +121,16 @@
user = db.ReferenceProperty(reference_class=soc.models.user.User,
required=True, collection_name='roles')
+ #: A reference to program entity to which the profile corresponds.
+ #: Each profile is created for exactly one program. If the same
+ #: user participates in more of them, a separate profile must be created
+ #: for each.
+ # TODO(daniel): make this field required when it is updated for
+ # all existing entities
+ program = db.ReferenceProperty(
+ reference_class=soc.models.program.Program, required=False,
+ collection_name='profiles')
+
#: Required field storing publicly-displayed name. Can be a real name
#: (though this is not recommended), or a nick name or some other public
#: alias. Public names can be any valid UTF-8 text.
diff --git a/app/soc/models/seed_db.py b/app/soc/models/seed_db.py
index f6aa6e2..3e88914 100644
--- a/app/soc/models/seed_db.py
+++ b/app/soc/models/seed_db.py
@@ -61,7 +61,6 @@
site_properties = {
'key_name': 'site',
- 'link_id': 'site',
'latest_gsoc': 'google/gsoc2009',
'latest_gci': 'google/gci2009',
}
diff --git a/app/soc/models/site.py b/app/soc/models/site.py
index 9ccef4f..7ecc993 100644
--- a/app/soc/models/site.py
+++ b/app/soc/models/site.py
@@ -19,11 +19,10 @@
from django.utils.translation import ugettext
-import soc.models.linkable
import soc.models.program
-class Site(soc.models.linkable.Linkable):
+class Site(db.Model):
"""Model of a Site, which stores per site configuration.
The Site Model stores configuration information unique to the Melange
diff --git a/app/soc/modules/gci/views/org_profile.py b/app/soc/modules/gci/views/org_profile.py
index 2004bea..e03447b 100644
--- a/app/soc/modules/gci/views/org_profile.py
+++ b/app/soc/modules/gci/views/org_profile.py
@@ -133,6 +133,7 @@
if not data.organization:
org_id = data.GET['org_id']
form.cleaned_data['scope'] = data.program
+ form.cleaned_data['program'] = data.program
form.cleaned_data['link_id'] = org_id
key_name = '%s/%s' % (data.program.key().name(), org_id)
entity = form.create(key_name=key_name)
diff --git a/app/soc/modules/gsoc/logic/project.py b/app/soc/modules/gsoc/logic/project.py
index 2a693a3..24e18af 100644
--- a/app/soc/modules/gsoc/logic/project.py
+++ b/app/soc/modules/gsoc/logic/project.py
@@ -42,7 +42,7 @@
query.filter('is_featured', True)
query.filter('program', program)
if current_timeline == 'coding_period':
- project_status = 'accepted'
+ project_status = project_model.STATUS_ACCEPTED
else:
project_status = 'completed'
query.filter('status', project_status)
@@ -102,7 +102,7 @@
query must be constructed.
"""
q = getProjectsQuery(keys_only, ancestor, **properties)
- q.filter('status', 'accepted')
+ q.filter('status', project_model.STATUS_ACCEPTED)
return q
@@ -144,7 +144,7 @@
This is a special query needed to build evaluation lists.
"""
q = getProjectsQuery(keys_only, ancestor, **properties)
- q.filter('status IN', ['accepted', 'failed', 'completed'])
+ q.filter('status IN', [project_model.STATUS_ACCEPTED, 'failed', 'completed'])
return q
@@ -158,7 +158,7 @@
should be queried.
"""
q = getProjectsQueryForOrgs(orgs)
- q.filter('status IN', ['accepted', 'failed', 'completed'])
+ q.filter('status IN', [project_model.STATUS_ACCEPTED, 'failed', 'completed'])
return q
diff --git a/app/soc/modules/gsoc/logic/proposal.py b/app/soc/modules/gsoc/logic/proposal.py
index 23b2cc9..fcdb010 100644
--- a/app/soc/modules/gsoc/logic/proposal.py
+++ b/app/soc/modules/gsoc/logic/proposal.py
@@ -42,7 +42,7 @@
# check if there are already slots taken by this org
query = proposal_model.GSoCProposal.all()
query.filter('org', org_entity)
- query.filter('status', 'accepted')
+ query.filter('status', proposal_model.STATUS_ACCEPTED)
slots_left_to_assign = max(0, org_entity.slots - query.count())
if slots_left_to_assign == 0:
diff --git a/app/soc/modules/gsoc/models/project.py b/app/soc/modules/gsoc/models/project.py
index 6616878..54c45a2 100644
--- a/app/soc/modules/gsoc/models/project.py
+++ b/app/soc/modules/gsoc/models/project.py
@@ -12,21 +12,33 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""This module contains the GSoCProject Model.
-"""
-
+"""This module contains the GSoCProject Model."""
from google.appengine.ext import db
from django.utils.translation import ugettext
-from soc.modules.gsoc.models.code_sample import GSoCCodeSample
-from soc.modules.gsoc.models.profile import GSoCProfile
+from soc.modules.gsoc.models import code_sample as code_sample_model
+from soc.modules.gsoc.models import profile as profile_model
import soc.modules.gsoc.models.proposal
import soc.models.program
import soc.models.organization
+# constants with possible statuses of projects
+
+# the project has been accepted into the program
+STATUS_ACCEPTED = 'accepted'
+
+# the project has failed one of evaluations
+STATUS_FAILED = 'failed'
+
+# the project has been withdrawn
+STATUS_WITHDRAWN = 'withdrawn'
+
+# the project has been marked as invalid
+STATUS_INVALID = 'invalid'
+
class GSoCProject(db.Model):
"""Model for a GSoC project used in the GSoC workflow.
@@ -47,7 +59,7 @@
'Short abstract, summary, or snippet;'
' 500 characters or less, plain text displayed publicly')
- #: Optional, text field containing all kinds of information about this project
+ #: Text field containing all kinds of information about this project
public_info = db.TextProperty(
required=False, default ='',
verbose_name=ugettext('Additional information'))
@@ -77,20 +89,18 @@
mentors = db.ListProperty(item_type=db.Key, default=[], required=True)
def getMentors(self):
- """Returns a list of GSoCProfile entities which
+ """Returns a list of profile_model.GSoCProfile entities which
are mentors for this project.
+
+ Returns:
+ list of mentors for this project
"""
- return [m for m in GSoCProfile.get(self.mentors) if m]
+ return [m for m in profile_model.GSoCProfile.get(self.mentors) if m]
#: The status of this project
- #: accepted: This project has been accepted into the program
- #: failed: This project has failed an evaluation.
- #: withdrawn: This project has been withdrawn from the program by a Program
- #: Administrator or higher.
- #: invalid: This project has been marked as invalid because it was deleted
- status = db.StringProperty(
- required=True, default='accepted',
- choices=['accepted', 'failed', 'withdrawn', 'invalid'])
+ status = db.StringProperty(required=True, default=STATUS_ACCEPTED,
+ choices=[STATUS_ACCEPTED, STATUS_FAILED,
+ STATUS_WITHDRAWN, STATUS_INVALID])
#: List of all processed GradingRecords which state a pass for this project.
#: This property can be used to determine how many evaluations someone has
@@ -109,9 +119,9 @@
required=True, collection_name='student_projects')
#: Program in which this project has been created
- program = db.ReferenceProperty(reference_class=soc.models.program.Program,
- required=True,
- collection_name='projects')
+ program = db.ReferenceProperty(
+ reference_class=soc.models.program.Program, required=True,
+ collection_name='projects')
#: Proposal to which this project corresponds to
proposal = db.ReferenceProperty(
@@ -123,15 +133,22 @@
code_samples_submitted = db.BooleanProperty(default=False)
def codeSamples(self):
- """Returns GSoCCodeSample entities uploaded for this project.
+ """Returns code_sample.GSoCCodeSample entities uploaded for this project.
+
+ Returns:
+ code sample entities for this project
"""
- query = GSoCCodeSample.all()
+ query = code_sample_model.GSoCCodeSample.all()
query.ancestor(self)
return query.fetch(1000)
def countCodeSamples(self):
- """Returns number of GSoCCodeSample entities uploaded for this project.
+ """Returns number of code_sample.GSoCCodeSample entities uploaded
+ for this project.
+
+ Returns:
+ number of code samples uploaded for this project
"""
- query = GSoCCodeSample.all(keys_only=True)
+ query = code_sample_model.GSoCCodeSample.all(keys_only=True)
query.ancestor(self)
return query.count()
diff --git a/app/soc/modules/gsoc/views/accept_withdraw_projects.py b/app/soc/modules/gsoc/views/accept_withdraw_projects.py
index 57c3648..56100bc 100644
--- a/app/soc/modules/gsoc/views/accept_withdraw_projects.py
+++ b/app/soc/modules/gsoc/views/accept_withdraw_projects.py
@@ -32,12 +32,15 @@
from soc.modules.gsoc.logic import project as project_logic
from soc.modules.gsoc.logic import proposal as proposal_logic
from soc.modules.gsoc.models.profile import GSoCStudentInfo
-from soc.modules.gsoc.models.project import GSoCProject
-from soc.modules.gsoc.models.proposal import GSoCProposal
+from soc.modules.gsoc.models import project as project_model
+from soc.modules.gsoc.models import proposal as proposal_model
from soc.modules.gsoc.views.base import GSoCRequestHandler
from soc.modules.gsoc.views.helper.url_patterns import url
+# key to map proposal with and its full key in list data
+_PROPOSAL_KEY = 'full_proposal_key'
+
class ProposalList(Template):
"""Template for listing the student proposals submitted to the program."""
@@ -57,7 +60,7 @@
def status(proposal):
"""Status to show on the list with color.
"""
- if proposal.status == 'accepted':
+ if proposal.status == proposal_model.STATUS_ACCEPTED:
return """<strong><font color="green">Accepted</font><strong>"""
elif proposal.status == 'withdrawn':
return """<strong><font color="red">Withdrawn</font></strong>"""
@@ -72,12 +75,12 @@
# hidden keys
list_config.addHtmlColumn(
- 'full_proposal_key', 'Full proposal key',
+ _PROPOSAL_KEY, 'Full proposal key',
(lambda ent, *args: str(ent.key())), hidden=True)
# action button
bounds = [1,'all']
- keys = ['full_proposal_key']
+ keys = [_PROPOSAL_KEY]
list_config.addPostButton('accept', "Accept", "", bounds, keys)
self._list_config = list_config
@@ -114,74 +117,30 @@
def postHandler(self, data):
for properties in data:
- if 'full_proposal_key' not in properties:
+ if _PROPOSAL_KEY not in properties:
logging.warning("Missing key in '%s'" % properties)
continue
- proposal_key = properties['full_proposal_key']
- proposal = db.get(db.Key(proposal_key))
-
- if not proposal:
- logging.warning("Proposal '%s' doesn't exist" % proposal_key)
- continue
-
- if proposal.status == 'accepted':
- logging.warning("Proposal '%s' already accepted" % proposal_key)
- continue
-
- # organization for the proposal
- org = proposal.org
- # key of the student profile for the project
- profile_key = proposal.parent_key()
-
- if not proposal.mentor:
- logging.warning(
- 'Proposal with key %s cannot be accepted because no mentor has '
- 'been assigned to it.' % (proposal_key))
- continue
-
- qp = GSoCProject.all()
- qp.ancestor(profile_key)
- qp.filter('org', org)
- qp.filter('status', 'withdrawn')
-
- if qp.count() > 0:
- logging.warning('Student with key %s already has an accepted '
- 'project' % profile_key)
- continue
-
- qorgp = GSoCProject.all()
- qorgp.filter('org', org)
- # TODO: The list save should actually fail, but no clue how to do this.
- if qorgp.count() >= org.slots:
- logging.warning('%d >= %d' % (qorgp.count(), org.slots))
- logging.warning(
- 'Organization %s has all the slots used up. No more '
- 'projects can be accepted into the organization.' % (
- org.name))
- continue
-
- fields = {
- 'org': proposal.org,
- 'program': proposal.program,
- 'title': proposal.title,
- 'abstract': proposal.abstract,
- 'mentors': [proposal.mentor.key()],
- }
- project = GSoCProject(parent=profile_key, **fields)
+ proposal_key = properties[_PROPOSAL_KEY]
def accept_proposal_txn():
- student_info = GSoCStudentInfo.all().ancestor(profile_key).get()
- orgs = student_info.project_for_orgs
+ """Accept the proposal within a transaction."""
+ proposal = db.get(proposal_key)
- orgs = list(set(orgs + [org.key()]))
-
- proposal.status = 'accepted'
-
- student_info.project_for_orgs = orgs
- student_info.number_of_projects = 1
-
- db.put([proposal, project, student_info])
+ if not proposal:
+ logging.warning("Proposal '%s' doesn't exist" % proposal_key)
+ elif proposal.status == proposal_model.STATUS_ACCEPTED:
+ logging.warning("Proposal '%s' already accepted" % proposal_key)
+ else:
+ # check if the proposal has been assigned a mentor
+ mentor_key = (proposal_model.GSoCProposal.mentor
+ .get_value_for_datastore(proposal))
+ if not mentor_key:
+ logging.warning(
+ 'Proposal with key %s cannot be accepted because no mentor has'
+ ' been assigned to it.' % proposal_key)
+ else:
+ proposal_logic.acceptProposal(proposal)
db.run_in_transaction(accept_proposal_txn)
@@ -198,7 +157,8 @@
list_query = proposal_logic.getProposalsQuery(program=self.data.program)
starter = lists.keyStarter
- prefetcher = lists.ModelPrefetcher(GSoCProposal, ['org'], parent=True)
+ prefetcher = lists.ModelPrefetcher(
+ proposal_model.GSoCProposal, ['org'], parent=True)
response_builder = lists.RawQueryContentResponseBuilder(
self.data.request, self._list_config, list_query,
@@ -208,7 +168,7 @@
return None
def templatePath(self):
- return "v2/modules/gsoc/accept_withdraw_projects/_project_list.html"
+ return 'v2/modules/gsoc/accept_withdraw_projects/_project_list.html'
class AcceptProposals(GSoCRequestHandler):
@@ -276,7 +236,7 @@
def status(project):
"""Status to show on the list with color.
"""
- if project.status == 'accepted':
+ if project.status == project_model.STATUS_ACCEPTED:
return """<strong><font color="green">Accepted</font><strong>"""
elif project.status == 'withdrawn':
return """<strong><font color="red">Withdrawn</font></strong>"""
@@ -353,12 +313,12 @@
logging.warning("Project '%s' already withdrawn" % project_key)
continue
- if not withdraw and project.status == 'accepted':
+ if not withdraw and project.status == project_model.STATUS_ACCEPTED:
logging.warning("Project '%s' already accepted" % project_key)
continue
# key of the organization for the project
- org_key = GSoCProject.org.get_value_for_datastore(project)
+ org_key = project_model.GSoCProject.org.get_value_for_datastore(project)
# key of the student profile for the project
profile_key = project.parent_key()
@@ -373,7 +333,7 @@
new_number = 0
orgs.remove(org_key)
else:
- new_status = 'accepted'
+ new_status = project_model.STATUS_ACCEPTED
new_number = 1
orgs = list(set(orgs + [org_key]))
@@ -399,7 +359,8 @@
list_query = project_logic.getProjectsQuery(program=self.data.program)
starter = lists.keyStarter
- prefetcher = lists.ModelPrefetcher(GSoCProject, ['org'], parent=True)
+ prefetcher = lists.ModelPrefetcher(
+ project_model.GSoCProject, ['org'], parent=True)
response_builder = lists.RawQueryContentResponseBuilder(
self.data.request, self._list_config, list_query,
diff --git a/app/soc/modules/gsoc/views/helper/access_checker.py b/app/soc/modules/gsoc/views/helper/access_checker.py
index f4483e5..c66ff1c 100644
--- a/app/soc/modules/gsoc/views/helper/access_checker.py
+++ b/app/soc/modules/gsoc/views/helper/access_checker.py
@@ -27,6 +27,7 @@
from soc.modules.gsoc.logic import project as project_logic
from soc.modules.gsoc.logic import slot_transfer as slot_transfer_logic
+from soc.modules.gsoc.models import proposal as proposal_model
from soc.modules.gsoc.models.connection import GSoCConnection, GSoCAnonymousConnection
from soc.modules.gsoc.models.grading_project_survey import GradingProjectSurvey
from soc.modules.gsoc.models.grading_project_survey_record import \
@@ -38,7 +39,6 @@
from soc.modules.gsoc.models.project_survey import ProjectSurvey
from soc.modules.gsoc.models.project_survey_record import \
GSoCProjectSurveyRecord
-from soc.modules.gsoc.models.proposal import GSoCProposal
from soc.modules.gsoc.models.organization import GSoCOrganization
@@ -149,13 +149,14 @@
if not proposal_id:
raise exception.NotFound(message='Proposal id must be a positive number')
- self.data.proposal = GSoCProposal.get_by_id(
+ self.data.proposal = proposal_model.GSoCProposal.get_by_id(
proposal_id, parent=self.data.url_profile)
if not self.data.proposal:
raise exception.NotFound(message='Requested proposal does not exist')
- org_key = GSoCProposal.org.get_value_for_datastore(self.data.proposal)
+ org_key = proposal_model.GSoCProposal.org.get_value_for_datastore(
+ self.data.proposal)
self.data.proposal_org = self.data.getOrganization(org_key)
@@ -346,9 +347,9 @@
# check how many proposals the student has already submitted
# TODO(daniel): replace this query with checking on number_of_proposals
- query = GSoCProposal.all()
+ query = proposal_model.GSoCProposal.all()
query.ancestor(self.data.profile)
- query.filter(GSoCProposal.status.name, 'pending')
+ query.filter(proposal_model.GSoCProposal.status.name, 'pending')
if query.count() >= self.data.program.apps_tasks_limit:
# too many proposals access denied
@@ -556,7 +557,7 @@
status = self.data.proposal.status
if status == 'ignored':
raise exception.Forbidden(message=DEF_PROPOSAL_IGNORED_MESSAGE)
- elif status in ['invalid', 'accepted', 'rejected']:
+ elif status in ['invalid', proposal_model.STATUS_ACCEPTED, 'rejected']:
raise exception.Forbidden(
message=access_checker.DEF_CANNOT_UPDATE_ENTITY % {
'name': 'proposal'
diff --git a/app/soc/modules/gsoc/views/org_profile.py b/app/soc/modules/gsoc/views/org_profile.py
index a958820..e9b1234 100644
--- a/app/soc/modules/gsoc/views/org_profile.py
+++ b/app/soc/modules/gsoc/views/org_profile.py
@@ -230,6 +230,7 @@
if not data.organization:
form.cleaned_data['scope'] = data.program
+ form.cleaned_data['program'] = data.program
form.cleaned_data['link_id'] = data.org_id
form.cleaned_data['new_org'] = data.org_app_record.new_org
key_name = '%s/%s' % (
diff --git a/app/soc/views/org_profile.py b/app/soc/views/org_profile.py
index fac65d8..bd83fc6 100644
--- a/app/soc/views/org_profile.py
+++ b/app/soc/views/org_profile.py
@@ -22,7 +22,7 @@
PROFILE_EXCLUDE = [
'status', 'scope', 'slots', 'note', 'new_org',
'slots_calculated', 'nr_applications', 'nr_mentors', 'link_id',
- 'proposal_extra',
+ 'proposal_extra', 'program'
]
HOMEPAGE_INFO_GROUP = translation.ugettext(
diff --git a/app/soc/views/profile.py b/app/soc/views/profile.py
index dc5363c..27e0424 100644
--- a/app/soc/views/profile.py
+++ b/app/soc/views/profile.py
@@ -42,7 +42,7 @@
PROFILE_EXCLUDE = [
# identification fields
'link_id', 'user', 'scope', 'status',
- 'agreed_to_tos_on', 'name_on_documents',
+ 'agreed_to_tos_on', 'name_on_documents', 'program',
# notification fields
'notify_new_requests', 'notify_new_invites',
'notify_invite_handled', 'notify_request_handled',
@@ -258,6 +258,7 @@
profile_form.cleaned_data['user'] = user
profile_form.cleaned_data['link_id'] = user.link_id
profile_form.cleaned_data['scope'] = data.program
+ profile_form.cleaned_data['program'] = data.program
if data.profile:
profile = profile_form.save(commit=False)
diff --git a/app/soc/views/site.py b/app/soc/views/site.py
index 5fe6395..b1f64c8 100644
--- a/app/soc/views/site.py
+++ b/app/soc/views/site.py
@@ -52,7 +52,7 @@
class Meta:
model = site.Site
- exclude = ['link_id', 'scope', 'xsrf_secret_key']
+ exclude = ['xsrf_secret_key']
# NOTE(nathaniel): There aren't really no choices, it's just that we
# can't know what the choices are at module-load-time. For the moment
# we have to set the available choices below in EditSitePage.context.
diff --git a/scripts/download_gci_student_forms.py b/scripts/download_gci_student_forms.py
index 079d253..c193c27 100755
--- a/scripts/download_gci_student_forms.py
+++ b/scripts/download_gci_student_forms.py
@@ -29,7 +29,7 @@
parser.add_option("-o", "--output", dest="outputdir", default="forms",
help="write files to target DIR", metavar="DIR")
parser.add_option("-p", "--program", dest="program_path", default="",
- help="scope path of the program", metavar="DIR")
+ help="full key name of the program", metavar="DIR")
def downloadStudentForms(options):
from google.appengine.ext import db
diff --git a/scripts/gci_statistic_seeder.py b/scripts/gci_statistic_seeder.py
deleted file mode 100755
index 54570ce..0000000
--- a/scripts/gci_statistic_seeder.py
+++ /dev/null
@@ -1,268 +0,0 @@
-#!/usr/bin/python2.5
-#
-# Copyright 2010 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.
-
-"""Starts an interactive shell which allows to create statistic entities.
-
-Usage is simple:
-
-In order to seed all available statistics, just type:
->>> seed_all()
-
-In order to seed one statistic:
->>> seed_one(link_id)
-where link_id is for the desired statistic
-
-In order to change program in scope:
->>> set_program(key_name)
-where key_name represents a new program
-
-In order to terminate the script:
->>> exit()
-"""
-
-
-import sys
-import interactive
-interactive.setup()
-
-from django.utils import simplejson
-
-from soc.logic import dicts
-
-from soc.modules.gci.logic.models.program import logic as program_logic
-
-from soc.modules.statistic.logic.models.statistic import logic as \
- statistic_logic
-
-from soc.modules.statistic.models.statistic import Statistic
-
-
-SUCCESS_MSG_FMT = 'Statistic %s has been sucessfully added.'
-FAILURE_MSG_FMT = 'An error occured while adding %s statistic.'
-DOES_NOT_EXISTS_MSG_FMT = 'Statistic %s does not exists.'
-
-VISUALIZATION_SETS = {
- "cumulative_standard": [
- "Table",
- "BarChart",
- "ColumnChart",
- "ImageChartBar",
- ],
- "cumulative_countries": [
- "Table"
- ],
- "single_standard": [
- "Table",
- "BarChart",
- "ColumnChart",
- "ImageChartBar",
- "ImageChartP",
- "ImageChartP3",
- "PieChart",
- "ScatterChart"
- ],
- "single_countries": [
- "Table",
- "GeoMap"
- ]
- }
-
-STATISTIC_PROPERTIES = {
- "mentors_per_continent": (
- "Mentors Per Continent",
- {
- "type": "per_field",
- "field": "continent",
- "model": "gci_mentor",
- "subsets": [("all", {}), ("referenced", {}), ("no-referenced", {})],
- "filter": "property_filter",
- "params": {
- "ref_logic": "gci_task",
- "ref_field": "mentors",
- "program_field": "program",
- "property_conditions": {
- "status": ["active", "inactive"]
- },
- }
- },
- {
- "description": [("continent", "string", "Continent"),
- ("all_mentors", "number", "Mentors"),
- ("pro_mentors", "number", "Mentors with tasks"),
- ("nop_mentors", "number", "Mentors without tasks")],
- "options": {
- 'Mentors Per Continent (cumulative)': {
- "visualizations": VISUALIZATION_SETS['cumulative_standard'],
- "columns": [0, 1, 2]
- },
- 'Mentors Per Continent (all)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [0]
- },
- 'Mentors Per Continent (with tasks)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [1]
- },
- 'Mentors Per Continent (without tasks)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [2]
- }
- }
- },
- "org_admin"),
- "students_per_age": (
- "Students Per Age",
- {
- "type": "per_field",
- "field": "age",
- "model": "gci_student",
- "transformer": "remove-out-of-range",
- "filter": "property_filter",
- "params": {
- "program_field": "scope",
- "property_conditions": {
- "status": ['active', 'inactive']
- },
- }
- },
- {
- "description": [("age", "number", "Age"),
- ("number", "number", "Number")],
- "options": {
- 'Organization Admins Per Age': {
- "visualizations": VISUALIZATION_SETS['single_standard']
- }
- }
- },
- "host"),
-}
-
-STATISTICS_LIST = [k for k in STATISTIC_PROPERTIES]
-NAMES_DICT = dict((k, v) for k, (v, _, _, _)
- in STATISTIC_PROPERTIES.iteritems())
-INSTRUCTIONS_DICT = dict((k, v) for k, (_, v, _, _)
- in STATISTIC_PROPERTIES.iteritems())
-CHARTS_DICT = dict((k, v) for k, (_, _, v, _)
- in STATISTIC_PROPERTIES.iteritems())
-ACCESS_DICT = dict((k, v) for k, (_, _, _, v)
- in STATISTIC_PROPERTIES.iteritems())
-
-
-def _getCommonProperties():
- """Returns properties that are common for all statistic entities.
- """
-
- program = program_logic.getFromKeyName(program_keyname)
-
- properties = {
- 'access_for_other_programs': 'invisible',
- 'scope': program,
- 'scope_path': program_keyname,
- }
-
- return properties
-
-
-def _getSpecificProperties(link_id):
- """Returns properties that are specific to a particular statistic.
- """
-
- properties = {
- 'link_id': link_id,
- 'name': NAMES_DICT[link_id],
- 'chart_json': simplejson.dumps(CHARTS_DICT[link_id]),
- 'instructions_json': simplejson.dumps(INSTRUCTIONS_DICT[link_id]),
- 'read_access': ACCESS_DICT[link_id]
- }
-
- return properties
-
-
-def _seedStatistic(properties):
- """Saves a new statistic entity, described by properties, in data store.
- """
-
- entity = statistic_logic.updateOrCreateFromFields(properties, silent=True)
-
- if entity:
- print SUCCESS_MSG_FMT % properties['link_id']
- else:
- print FALIURE_MSG_FMT % properties['link_id']
-
-
-def exit():
- """Terminates the script.
- """
-
- sys.exit(0)
-
-
-def seedOne(link_id):
- """Seeds a single statistic to the data store.
-
- Args:
- link_id: link_id of the statistic that should be added.
- """
-
- if link_id not in STATISTICS_LIST:
- print DOES_NOT_EXISTS_MSG_FMT % link_id
- else:
- properties = _getCommonProperties()
- new_properties = _getSpecificProperties(link_id)
- properties.update(new_properties)
- _seedStatistic(properties)
-
-
-def seedAll():
- """Seeds all available statistics to the data store.
- """
-
- properties = _getCommonProperties()
-
- for statistic in STATISTICS_LIST:
-
- new_properties = _getSpecificProperties(statistic)
- properties.update(new_properties)
- _seedStatistic(properties)
-
-
-def setProgram(keyname):
- """Sets program key name.
- """
-
- program_keyname = keyname
-
-
-def main(args):
-
- context = {
- 'exit': exit,
- 'seed_all': seedAll,
- 'seed_one': seedOne,
- 'statistics_list': STATISTICS_LIST,
- 'set_program': setProgram,
- }
-
- interactive.remote(args, context)
-
-program_keyname = 'melange/gcirunthrough'
-
-if __name__ == '__main__':
- if len(sys.argv) < 2:
- print "Usage: %s app_id [host]" % (sys.argv[0],)
- sys.exit(1)
-
- main(sys.argv[1:])
diff --git a/scripts/statistic_seeder.py b/scripts/statistic_seeder.py
deleted file mode 100755
index fe130a3..0000000
--- a/scripts/statistic_seeder.py
+++ /dev/null
@@ -1,1018 +0,0 @@
-#!/usr/bin/python2.5
-#
-# Copyright 2010 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.
-
-"""Starts an interactive shell which allows to create statistic entities.
-
-Usage is simple:
-
-In order to seed all available statistics, just type:
->>> seed_all()
-
-In order to seed one statistic:
->>> seed_one(link_id)
-where link_id is for the desired statistic
-
-In order to change program in scope:
->>> set_program(key_name)
-where key_name represents a new program
-
-In order to terminate the script:
->>> exit()
-"""
-
-
-import sys
-import interactive
-interactive.setup()
-
-from django.utils import simplejson
-
-from soc.logic import dicts
-
-from soc.modules.gsoc.logic.models.program import logic as program_logic
-
-from soc.modules.statistic.logic.models.statistic import logic as \
- statistic_logic
-
-from soc.modules.statistic.models.statistic import Statistic
-
-
-SUCCESS_MSG_FMT = 'Statistic %s has been sucessfully added.'
-FAILURE_MSG_FMT = 'An error occured while adding %s statistic.'
-DOES_NOT_EXISTS_MSG_FMT = 'Statistic %s does not exists.'
-
-VISUALIZATION_SETS = {
- "cumulative_standard": [
- "Table",
- "BarChart",
- "ColumnChart",
- "ImageChartBar",
- ],
- "cumulative_countries": [
- "Table"
- ],
- "single_standard": [
- "Table",
- "BarChart",
- "ColumnChart",
- "ImageChartBar",
- "ImageChartP",
- "ImageChartP3",
- "PieChart",
- "ScatterChart"
- ],
- "single_countries": [
- "Table",
- "GeoMap"
- ]
- }
-
-STATISTIC_PROPERTIES = {
- "applications_per_program": (
- "Applications Per Program",
- {
- "params": {"fields": ["program", "__key__"]},
- "type": "per_field",
- "model": "gsoc_student_proposal",
- "choice_instructions":
- {
- "model": "gsoc_program",
- },
- "transformer": "pretty_names",
- },
- {
- "description": [("program", "string", "Program"),
- ("number", "number", "Number")],
- "options": {
- 'Applications Per Program': {
- "visualizations": VISUALIZATION_SETS['single_standard']
- }
- }
- },
- "host"),
- "applications_per_student": (
- "Applications Per Student",
- {
- "type": "per_field",
- "model": "gsoc_student_proposal",
- "choice_instructions":
- {
- "program_field": "student",
- "model": "gsoc_student",
- "filter": "property_filter",
- "property_conditions": {
- "status": ['active', 'inactive']
- },
- },
- "transformer": "enumerate",
- "params": {
- "fields": ["scope", "__key__"],
- "program_field": "program",
- }
- },
- {
- "description": [("app_number", "string", "Number of Applications"),
- ("student_number", "number", "Number of Students")],
- "options": {
- 'Applications Per Student': {
- "visualizations": VISUALIZATION_SETS['single_standard']
- }
- }
- },
- "host"),
- "mentors_per_continent": (
- "Mentors Per Continent",
- {
- "type": "per_field",
- "field": "continent",
- "model": "gsoc_mentor",
- "subsets": {"all":{}, "referenced":{}, "no-referenced":{}},
- "filter": "property_filter",
- "params": {
- "ref_logic": "gsoc_student_project",
- "ref_field": "mentor",
- "program_field": "program",
- "property_conditions": {
- "status": ["active", "inactive"]
- },
- }
- },
- {
- "description": [("continent", "string", "Continent"),
- ("all_mentors", "number", "Mentors"),
- ("pro_mentors", "number", "Mentors with projects"),
- ("nop_mentors", "number",
- "Mentors without projects")],
- "options": {
- 'Mentors Per Continent (cumulative)': {
- "visualizations": VISUALIZATION_SETS['cumulative_standard'],
- "columns": [0, 1, 2]
- },
- 'Mentors Per Continent (all)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [0]
- },
- 'Mentors Per Continent (with projects)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [1]
- },
- 'Mentors Per Continent (without projects)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [2]
- }
- }
- },
- "org_admin"),
- "mentors_per_country": (
- "Mentors Per Country",
- {
- "type": "per_field",
- "field": "country",
- "model": "gsoc_mentor",
- "subsets": {"all":{}, "referenced":{}, "no-referenced":{}},
- "filter": "property_filter",
- "transformer": "get-vis-names",
- "params": {
- "fields": ["res_country"],
- "ref_logic": "gsoc_student_project",
- "ref_field": "mentor",
- "program_field": "program",
- "property_conditions": {
- "status": ["active", "inactive"]
- },
- }
- },
- {
- "description": [("country", "string", "Country"),
- ("all_mentors", "number", "Mentors"),
- ("pro_mentors", "number", "Mentors with projects"),
- ("nop_mentors", "number",
- "Mentors without projects")],
- "options": {
- 'Mentors Per Country (cumulative)': {
- "visualizations": VISUALIZATION_SETS['cumulative_countries'],
- "columns": [0, 1, 2]
- },
- 'Mentors Per Country (all)': {
- "visualizations": VISUALIZATION_SETS['single_countries'],
- "columns": [0]
- },
- 'Mentors Per Country (with projects)': {
- "visualizations": VISUALIZATION_SETS['single_countries'],
- "columns": [1]
- },
- 'Mentors Per Country (without projects)': {
- "visualizations": VISUALIZATION_SETS['single_countries'],
- "columns": [2]
- }
- }
- },
- "org_admin"),
- "mentors_per_organization": (
- "Mentors Per Organization",
- {
- "type": "per_field",
- "model": "gsoc_mentor",
- "choice_instructions": {
- "program_field": "scope",
- "model": "gsoc_organization",
- "filter": "property_filter",
- "property_conditions": {
- "status": ['new', 'active', 'inactive']
- },
- },
- "transformer": "pretty_names",
- "filter": "property_filter",
- "params": {
- "fields": ["scope", "__key__"],
- "program_field": "program",
- "property_conditions": {
- "status": ['active', 'inactive']
- },
- }
- },
- {
- "description": [("org_name", "string", "Organization"),
- ("student_number", "number", "Number of Mentors")],
- "options": {
- 'Mentors Per Organization': {
- "visualizations": ["Table"]
- }
- }
- },
- "host"),
- "organizations_per_program": (
- "Organizations Per Program",
- {
- "type": "per_field",
- "model": "gsoc_organization",
- "choice_instructions": {
- "model": "gsoc_program",
- },
- "transformer": "pretty_names",
- "filter": "property_filter",
- "params": {
- "fields": ["scope", "__key__"],
- "property_conditions": {
- "status": ['new', 'active', 'inactive']
- },
- }
- },
- {
- "description": [("program", "string", "Program"),
- ("number", "number", "Number")],
- "options": {
- 'Organizations Per Program': {
- "visualizations": VISUALIZATION_SETS['single_standard']
- }
- }
- },
- "host"),
- "organization_admins_per_age": (# strange visualizations
- "Organization Admins Per Age",
- {
- "type": "per_field",
- "field": "age",
- "model": "gsoc_org_admin",
- "transformer": "remove-out-of-range",
- "filter": "property_filter",
- "params": {
- "program_field": "program",
- "property_conditions": {
- "status": ['active', 'inactive']
- },
- }
- },
- {
- "description": [("age", "number", "Age"),
- ("number", "number", "Number")],
- "options": {
- 'Organization Admins Per Age': {
- "visualizations": VISUALIZATION_SETS['single_standard']
- }
- }
- },
- "host"),
- "student_projects_per_continent": (
- "Student Projects Per Continent",
- {
- "type": "per_field",
- "field": "continent",
- "model": "gsoc_student_project",
- "filter": "property_filter",
- "params": {
- "fields": ["student"],
- "property_conditions": {
- "status": ["accepted", "completed", "failed"]
- },
- "program_field": "program",
- }
- },
- {
- "description": [("continent", "string", "Continent"),
- ("number", "number", "Number")],
- "options": {
- 'Student Projects Per Continent': {
- "visualizations": VISUALIZATION_SETS['single_standard']
- }
- }
- },
- "host"),
- "student_projects_per_country": (
- "Student Projects Per Country",
- {
- "type": "per_field",
- "model": "gsoc_student_project",
- "field": "country",
- "transformer": "get-vis-names",
- "filter": "property_filter",
- "params": {
- "fields": ["student", "res_country"],
- "property_conditions": {
- "status": ["accepted", "completed", "failed"]
- },
- "program_field": "program",
- }
- },
- {
- "description": [("country", "string", "Country"),
- ("number", "number", "Number")],
- "options": {
- 'Student Projects Per Country': {
- "visualizations": VISUALIZATION_SETS['single_countries']
- }
- }
- },
- "host"),
- "student_projects_per_organization": (
- "Student Projects Per Organization",
- {
- "type": "per_field",
- "filter": "property_filter",
- "model": "gsoc_student_project",
- "transformer": "pretty_names",
- "subsets": [
- ('all', {}),
- ('within_range', {'constraints': [
- {'field': 'passed_evaluations',
- 'type': 'size',
- 'min_value': 1,
- 'max_value': 2}
- ]}),
- ('within_range', {'constraints': [
- {'field': 'passed_evaluations',
- 'type': 'size',
- 'min_value': 2,
- 'max_value': 2}
- ]})
- ],
- "choice_instructions": {
- "program_field": "scope",
- "model": "gsoc_organization",
- "filter": "property_filter",
- "property_conditions": {
- "status": ['new', 'active', 'inactive']
- },
- },
- "params": {
- "fields": ["scope", "__key__"],
- "program_field": "program",
- "property_conditions": {
- "status": ["accepted", "completed", "failed"]
- },
- }
- },
- {
- "description": [("organization", "string", "Organization"),
- ("accepted_projects", "number", "Accepted"),
- ("midterm_projects", "number", "Midterm Passed"),
- ("passed_projects", "number", "Final Passed")],
- "options": {
- 'Student Projects Per Organization (cumulative)': {
- "visualizations": ['Table'],
- "columns": [0, 1, 2]
- },
- 'Accepted Student Projects Per Organization': {
- "visualizations": ["Table", "ColumnChart"],
- "columns": [0]
- },
- 'Midterm-Passed Student Projects Per Organization': {
- "visualizations": ["Table", "ColumnChart"],
- "columns": [1]
- },
- 'Final-Passed Student Projects Per Organization': {
- "visualizations": ["Table", "ColumnChart"],
- "columns": [2]
- },
- }
- },
- "host"),
- "student_proposals_per_continent": (
- "Student Proposals Per Continent",
- {
- "type": "per_field",
- "field": "continent",
- "model": "gsoc_student_proposal",
- "params": {
- "fields": ["scope"],
- "program_field": "program",
- }
- },
- {
- "description": [("continent", "string", "Continent"),
- ("number", "number", "Number")],
- "options": {
- 'Student Proposals Per Continent': {
- "visualizations": VISUALIZATION_SETS['single_standard']
- }
- }
- },
- "host"),
- "student_proposals_per_country": (
- "Student Proposals Per Country",
- {
- "type": "per_field",
- "field": "country",
- "model": "gsoc_student_proposal",
- "transformer": "get-vis-names",
- "params": {
- "fields": ["scope", "res_country"],
- "program_field": "program",
- }
- },
- {
- "description": [("country", "string", "Country"),
- ("number", "number", "Number")],
- "options": {
- 'Student Proposals Per Country': {
- "visualizations": VISUALIZATION_SETS['single_countries']
- }
- }
- },
- "host"),
- "student_proposals_per_organization": (
- "Student Proposals Per Organization",
- {
- "type": "per_field",
- "model": "gsoc_student_proposal",
- "choice_instructions": {
- "program_field": "scope",
- "model": "gsoc_organization",
- "filter": "property_filter",
- "property_conditions": {
- "status": ['new', 'active', 'inactive']
- },
- },
- "transformer": "pretty_names",
- "params": {
- "fields": ["org", "__key__"],
- "program_field": "program",
- }
- },
- {
- "description": [("organization", "string", "Organization"),
- ("number", "number", "Number")],
- "options": {
- 'Student Proposals Per Organization': {
- "visualizations": ["Table", "ColumnChart"]
- }
- }
- },
- "host"),
- "students_per_age": (
- "Students Per Age",
- {
- "type": "per_field",
- "field": "age",
- "filter": "property_filter",
- "model": "gsoc_student",
- "subsets": {"all":{}, "referenced":{}, "no-referenced":{}},
- "transformer": "remove-out-of-range",
- "params": {
- "ref_logic": "gsoc_student_project",
- "ref_field": "student",
- "program_field": "scope",
- "property_conditions": {
- "status": ["active", "inactive"]
- },
- }
- },
- {
- "description": [("age", "string", "Age"),
- ("all_students", "number", "Students"),
- ("pro_students", "number",
- "Students with projects"),
- ("nop_students", "number",
- "Students without projects")],
- "options": {
- 'Students Per Age (cumulative)': {
- "visualizations": VISUALIZATION_SETS['cumulative_standard'],
- "columns": [0, 1, 2]
- },
- 'Students Per Age (all)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [0]
- },
- 'Students Per Age (with projects)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [1]
- },
- 'Students Per Age (without projects)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [2]
- }
- }
- },
- "host"),
- "students_per_continent": (
- "Students Per Continent",
- {
- "type": "per_field",
- "field": "continent",
- "filter": "property_filter",
- "model": "gsoc_student",
- "subsets": {"all":{}, "referenced":{}, "no-referenced":{}},
- "params": {
- "ref_logic": "gsoc_student_project",
- "ref_field": "student",
- "program_field": "scope",
- "property_conditions": {
- "status": ["active", "inactive"]
- },
- }
- },
- {
- "description": [("age", "string", "Continent"),
- ("all_students", "number", "Students"),
- ("pro_students", "number",
- "Students with projects"),
- ("nop_students", "number",
- "Students without projects")],
- "options": {
- 'Students Per Continent (cumulative)': {
- "visualizations": [
- "Table",
- "BarChart",
- "ColumnChart",
- "ImageChartBar",
- ],
- "columns": [0, 1, 2]
- },
- 'Students Per Continent (all)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [0]
- },
- 'Students Per Continent (with projects)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [1]
- },
- 'Students Per Continent (without projects)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [2]
- }
- },
- },
- "host"),
- "students_per_country": (
- "Students Per Country",
- {
- "type": "per_field",
- "field": "country",
- "filter": "property_filter",
- "model": "gsoc_student",
- "subsets": {"all":{}, "referenced":{}, "no-referenced":{}},
- "transformer": "get-vis-names",
- "params": {
- "fields": ["res_country"],
- "ref_logic": "gsoc_student_project",
- "ref_field": "student",
- "program_field": "scope",
- "property_conditions": {
- "status": ["active", "inactive"]
- },
- }
- },
- {
- "description": [("country", "string", "Country"),
- ("all_students", "number", "Students"),
- ("pro_students", "number",
- "Students with projects"),
- ("nop_students", "number",
- "Students without projects")],
- "options": {
- 'Students Per Country (cumulative)': {
- "visualizations": VISUALIZATION_SETS['cumulative_countries'],
- "columns": [0, 1, 2]
- },
- 'Students Per Country (all)': {
- "visualizations": VISUALIZATION_SETS['single_countries'],
- "columns": [0]
- },
- 'Students Per Country (with projects)': {
- "visualizations": VISUALIZATION_SETS['single_countries'],
- "columns": [1]
- },
- 'Students Per Country (without projects)': {
- "visualizations": VISUALIZATION_SETS['single_countries'],
- "columns": [2]
- }
- },
- },
- "host"),
- "students_per_degree": (
- "Students Per Degree",
- {
- "type": "per_field",
- "field": "degree",
- "filter": "property_filter",
- "model": "gsoc_student",
- "subsets": [("all", {}), ("referenced", {}), ("no-referenced", {})],
- "params": {
- "fields": ["degree"],
- "ref_logic": "gsoc_student_project",
- "ref_field": "student",
- "program_field": "scope",
- "property_conditions": {
- "status": ["active", "inactive"]
- },
- }
- },
- {
- "description": [("degree", "string", "Degree"),
- ("all_students", "number", "Students"),
- ("pro_students", "number",
- "Students with projects"),
- ("nop_students", "number",
- "Students without projects")],
- "options": {
- 'Students Per Degree (cumulative)': {
- "visualizations": VISUALIZATION_SETS['cumulative_standard'],
- "columns": [0, 1, 2]
- },
- 'Students Per Degree (all)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [0]
- },
- 'Students Per Degree (with projects)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [1]
- },
- 'Students Per Degree (without projects)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [2]
- }
- }
- },
- "host"),
- "students_per_graduation_year": (
- "Students Per Graduation Year",
- {
- "type": "per_field",
- "field": "expected_graduation",
- "filter": "property_filter",
- "model": "gsoc_student",
- "subsets": [("all", {}), ("referenced", {}), ("no-referenced", {})],
- "transformer": "remove-out-of-range",
- "params": {
- "fields": ["expected_graduation"],
- "ref_logic": "gsoc_student_project",
- "ref_field": "student",
- "program_field": "scope",
- "property_conditions": {
- "status": ["active", "inactive"]
- },
- }
- },
- {
- "description": [("graduation_year", "string", "Graduation Year"),
- ("all_students", "number", "Students"),
- ("pro_students", "number",
- "Students with projects"),
- ("nop_students", "number",
- "Students without projects")],
- "options": {
- 'Students Per Graduation Year (cumulative)': {
- "visualizations": VISUALIZATION_SETS['cumulative_standard'],
- "columns": [0, 1, 2]
- },
- 'Students Per Graduation Year (all)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [0]
- },
- 'Students Per Graduation Year (with projects)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [1]
- },
- 'Students Per Graduation Year (without projects)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [2]
- }
- }
- },
- "host"),
- "students_per_tshirt_style": (
- "Students Per T-Shirt Style",
- {
- "type": "per_field",
- "field": "tshirt_style",
- "filter": "property_filter",
- "model": "gsoc_student",
- "subsets": [
- ("all", {}),
- ("referenced", {}),
- ("no-referenced", {})],
- "params": {
- "fields": ["tshirt_style"],
- "ref_logic": "gsoc_student_project",
- "ref_field": "student",
- "program_field": "scope",
- "property_conditions": {
- "status": ["active", "inactive"]
- },
- }
- },
- {
- "description": [("style", "string", "Style"),
- ("all_students", "number", "Students"),
- ("pro_students", "number",
- "Students with projects"),
- ("nop_students", "number",
- "Students without projects")],
- "options": {
- 'Students Per T-Shirt Style (cumulative)': {
- "visualizations": [
- "Table",
- "BarChart",
- "ColumnChart",
- "ImageChartBar",
- ],
- "columns": [0, 1, 2]
- },
- 'Students Per T-Shirt Style (all)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [0]
- },
- 'Students Per T-Shirt Style (with projects)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [1]
- },
- 'Students Per T-Shirt Style (without projects)': {
- "visualizations": VISUALIZATION_SETS['single_standard'],
- "columns": [2]
- }
- },
- },
- "host"),
- "gsoc2010_overall": (
- "GSoC2010 Overall",
- {
- "type": "overall",
- "items": (
- {
- "name": "Number of Students",
- "type": "number",
- "model": "gsoc_student",
- "program_field": "scope",
- "filter": "property_filter",
- "params": {
- "property_conditions": {
- "status": ["active", "inactive"]
- },
- }
- },
- {
- "name": "Number of Mentors",
- "type": "number",
- "model": "gsoc_mentor",
- "program_field": "program",
- "filter": "property_filter",
- "params": {
- "property_conditions": {
- "status": ["active", "inactive"]
- },
- }
- },
- {
- "name": "Number of Student Proposals",
- "type": "number",
- "model": "gsoc_student_proposal",
- "program_field": "program",
- },
- {
- "name": "Number of Student Projects",
- "type": "number",
- "model": "gsoc_student_project",
- "program_field": "program",
- "filter": "property_filter",
- "params": {
- "property_conditions": {
- "status": ["accepted", "completed", "failed"]
- },
- }
- },
- {
- "name": "Number of Organization Admins",
- "type": "number",
- "model": "gsoc_org_admin",
- "program_field": "program",
- "filter": "property_filter",
- "params": {
- "property_conditions": {
- "status": ["active", "inactive"]
- },
- }
- },
- {
- "name": "Number of Mentors With Projects",
- "type": "number",
- "model": "gsoc_student_project",
- "program_field": "program",
- "fields": ["mentor"],
- "filter": "property_filter",
- "params": {
- "property_conditions": {
- "status": ["accepted", "completed", "failed"]
- },
- }
- },
- {
- "name": "Number of Students With Projects",
- "type": "number",
- "model": "gsoc_student_project",
- "program_field": "program",
- "fields": ["student"],
- "filter": "property_filter",
- "params": {
- "property_conditions": {
- "status": ["accepted", "completed", "failed"]
- },
- }
- },
- {
- "name": "Number of Students With Proposals",
- "type": "number",
- "model": "gsoc_student_proposal",
- "program_field": "program",
- "fields": ["scope"]
- },
- {
- "name": "Average Number of Projects Per Mentor",
- "type": "average",
- "model": "gsoc_mentor",
- "program_field": "program",
- "ref_logic": "gsoc_student_project",
- "ref_field": "mentor"
- },
- {
- "name": "Average Number of Proposals Per Student",
- "type": "average",
- "model": "gsoc_student",
- "program_field": "scope",
- "ref_logic": "gsoc_student_proposal",
- "ref_field": "scope"
- },
- )
- },
- {
- "description": [("stat_name", "string", "Statistic Name"),
- ("value", "number", "Value")],
- "options": {
- 'Google Summer of Code 2009 (overall)': {
- 'visualizations': [
- 'Table'
- ]
- }
- }
- },
- "host"),
- }
-
-STATISTICS_LIST = [k for k in STATISTIC_PROPERTIES]
-NAMES_DICT = dict((k, v) for k, (v, _, _, _)
- in STATISTIC_PROPERTIES.iteritems())
-INSTRUCTIONS_DICT = dict((k, v) for k, (_, v, _, _)
- in STATISTIC_PROPERTIES.iteritems())
-CHARTS_DICT = dict((k, v) for k, (_, _, v, _)
- in STATISTIC_PROPERTIES.iteritems())
-ACCESS_DICT = dict((k, v) for k, (_, _, _, v)
- in STATISTIC_PROPERTIES.iteritems())
-
-program_keyname = 'google/gsoc2010'
-
-def _getCommonProperties():
- """Returns properties that are common for all statistic entities.
- """
-
- program = program_logic.getFromKeyName(program_keyname)
-
- properties = {
- 'access_for_other_programs': 'invisible',
- 'scope': program,
- 'scope_path': program_keyname,
- }
-
- return properties
-
-
-def _getSpecificProperties(link_id):
- """Returns properties that are specific to a particular statistic.
- """
-
- properties = {
- 'link_id': link_id,
- 'name': NAMES_DICT[link_id],
- 'chart_json': simplejson.dumps(CHARTS_DICT[link_id]),
- 'instructions_json': simplejson.dumps(INSTRUCTIONS_DICT[link_id]),
- 'read_access': ACCESS_DICT[link_id]
- }
-
- return properties
-
-
-def _seedStatistic(properties):
- """Saves a new statistic entity, described by properties, in data store.
- """
-
- entity = statistic_logic.updateOrCreateFromFields(properties, silent=True)
-
- if entity:
- print SUCCESS_MSG_FMT % properties['link_id']
- else:
- print FALIURE_MSG_FMT % properties['link_id']
-
-
-def exit():
- """Terminates the script.
- """
-
- sys.exit(0)
-
-
-def seedOne(link_id):
- """Seeds a single statistic to the data store.
-
- Args:
- link_id: link_id of the statistic that should be added.
- """
-
- if link_id not in STATISTICS_LIST:
- print DOES_NOT_EXISTS_MSG_FMT % link_id
- else:
- properties = _getCommonProperties()
- new_properties = _getSpecificProperties(link_id)
- properties.update(new_properties)
- _seedStatistic(properties)
-
-
-def seedAll():
- """Seeds all available statistics to the data store.
- """
-
- properties = _getCommonProperties()
-
- for statistic in STATISTICS_LIST:
-
- new_properties = _getSpecificProperties(statistic)
- properties.update(new_properties)
- _seedStatistic(properties)
-
-
-def setProgram(keyname):
- """Sets program key name.
- """
-
- program_keyname = keyname
-
-
-def main(args):
-
- context = {
- 'exit': exit,
- 'seed_all': seedAll,
- 'seed_one': seedOne,
- 'statistics_list': STATISTICS_LIST,
- 'set_program': setProgram,
- }
-
- interactive.remote(args, context)
-
-
-if __name__ == '__main__':
- if len(sys.argv) < 2:
- print "Usage: %s app_id [host]" % (sys.argv[0],)
- sys.exit(1)
-
- main(sys.argv[1:])
diff --git a/tests/app/soc/modules/gci/logic/test_profile.py b/tests/app/soc/modules/gci/logic/test_profile.py
index 50d7100..a9f899b 100644
--- a/tests/app/soc/modules/gci/logic/test_profile.py
+++ b/tests/app/soc/modules/gci/logic/test_profile.py
@@ -21,6 +21,7 @@
from soc.modules.gci.logic import profile as profile_logic
from soc.modules.gci.models.organization import GCIOrganization
from soc.modules.gci.models.profile import GCIProfile
+from soc.modules.gci.models import program as program_model
from soc.modules.seeder.logic.seeder import logic as seeder_logic
from soc.modules.gci.models import task as task_model
@@ -29,12 +30,16 @@
class ProfileTest(unittest.TestCase):
- """Tests the logic for GCI profiles.
- """
+ """Tests the logic for GCI profiles."""
def setUp(self):
- self.foo_org = seeder_logic.seed(GCIOrganization)
- self.bar_org = seeder_logic.seed(GCIOrganization)
+ program = seeder_logic.seed(program_model.GCIProgram)
+
+ org_properties = {
+ 'program': program
+ }
+ self.foo_org = seeder_logic.seed(GCIOrganization, org_properties)
+ self.bar_org = seeder_logic.seed(GCIOrganization, org_properties)
def testQueryAllMentorKeysForOrg(self):
"""Tests if a list of keys of all the mentors for an organization is
diff --git a/tests/app/soc/modules/gci/views/test_accepted_orgs.py b/tests/app/soc/modules/gci/views/test_accepted_orgs.py
index b9f696f..74de9dc 100644
--- a/tests/app/soc/modules/gci/views/test_accepted_orgs.py
+++ b/tests/app/soc/modules/gci/views/test_accepted_orgs.py
@@ -55,7 +55,7 @@
self.data.createUser()
org_properties = {
'scope': self.gci, 'status': 'active',
- 'home': None, 'backup_winner': None,
+ 'home': None, 'backup_winner': None, 'program': self.gci,
}
self.seed(GCIOrganization, org_properties)
self.seed(GCIOrganization, org_properties)
diff --git a/tests/app/soc/modules/gci/views/test_org_profile.py b/tests/app/soc/modules/gci/views/test_org_profile.py
index a7ebcdb..28702db 100644
--- a/tests/app/soc/modules/gci/views/test_org_profile.py
+++ b/tests/app/soc/modules/gci/views/test_org_profile.py
@@ -13,9 +13,7 @@
# limitations under the License.
-"""Tests for GCI Organization profile related views.
-"""
-
+"""Tests for GCI Organization profile related views."""
from google.appengine.ext import db
@@ -81,7 +79,7 @@
self.assertOrgProfilePageTemplatesUsed(response)
postdata = {
- 'home': self.createDocument().key(),
+ 'home': self.createDocument().key(), 'program': self.gci,
'scope': self.gci, 'irc_channel': 'irc://example.com',
'pub_mailing_list': 'http://example.com', 'backup_winner': None,
}
@@ -90,3 +88,5 @@
self.assertResponseRedirect(response, url + '/new_org?validated')
profile = db.get(self.data.profile.key())
self.assertEqual(1, len(profile.org_admin_for))
+ self.assertSameEntity(self.gci, profile.program)
+
diff --git a/tests/app/soc/modules/gci/views/test_org_score.py b/tests/app/soc/modules/gci/views/test_org_score.py
index 9df097f..f0e148c 100644
--- a/tests/app/soc/modules/gci/views/test_org_score.py
+++ b/tests/app/soc/modules/gci/views/test_org_score.py
@@ -69,7 +69,7 @@
self.data.createHost()
org_properties = {
- 'scope': self.gci, 'status': 'active',
+ 'scope': self.gci, 'status': 'active', 'program': self.gci,
'home': None, 'backup_winner': None,
}
self.seed(GCIOrganization, org_properties)
@@ -86,7 +86,7 @@
self.data.createHost()
org_properties = {
- 'scope': self.gci, 'status': 'invalid',
+ 'scope': self.gci, 'status': 'invalid', 'program': self.gci,
'home': None, 'backup_winner': None
}
self.seed(GCIOrganization, org_properties)
diff --git a/tests/app/soc/modules/gci/views/test_profile.py b/tests/app/soc/modules/gci/views/test_profile.py
index 024ff3b..4a3436d 100644
--- a/tests/app/soc/modules/gci/views/test_profile.py
+++ b/tests/app/soc/modules/gci/views/test_profile.py
@@ -222,6 +222,7 @@
student = GCIProfile.all().get()
self.assertEqual(self.birth_date, str(student.birth_date))
+ self.assertSameEntity(self.gci, student.program)
def testCreateUserNoLinkId(self):
self.timeline.studentSignup()
diff --git a/tests/app/soc/modules/gsoc/logic/test_project.py b/tests/app/soc/modules/gsoc/logic/test_project.py
index 9cc3940..14a08ce 100644
--- a/tests/app/soc/modules/gsoc/logic/test_project.py
+++ b/tests/app/soc/modules/gsoc/logic/test_project.py
@@ -35,9 +35,9 @@
# seed a couple of organizations
self.organization_one = seeder_logic.seed(org_model.GSoCOrganization,
- {'scope': self.program})
+ {'scope': self.program, 'program': self.program})
self.organization_two = seeder_logic.seed(org_model.GSoCOrganization,
- {'scope': self.program})
+ {'scope': self.program, 'program': self.program})
# seed a couple of projects for the organizations
self.project_one = seeder_logic.seed(project_model.GSoCProject,
diff --git a/tests/app/soc/modules/gsoc/logic/test_proposal.py b/tests/app/soc/modules/gsoc/logic/test_proposal.py
index 6741443..b290fcd 100644
--- a/tests/app/soc/modules/gsoc/logic/test_proposal.py
+++ b/tests/app/soc/modules/gsoc/logic/test_proposal.py
@@ -38,11 +38,19 @@
def setUp(self):
self.program = seeder_logic.seed(GSoCProgram)
#An organization which has all its slots allocated.
- org_properties = {'scope':self.program, 'slots': 2}
+ org_properties = {
+ 'scope': self.program,
+ 'slots': 2,
+ 'program': self.program
+ }
self.foo_organization = seeder_logic.seed(GSoCOrganization, org_properties)
- proposal_properties = {'program': self.program, 'org': self.foo_organization,
- 'mentor': None, 'status': 'accepted'}
+ proposal_properties = {
+ 'program': self.program,
+ 'org': self.foo_organization,
+ 'mentor': None,
+ 'status': proposal_model.STATUS_ACCEPTED,
+ }
self.foo_proposals = seeder_logic.seedn(
proposal_model.GSoCProposal, 2, proposal_properties)
@@ -51,8 +59,12 @@
org_properties = {'scope':self.program, 'slots': 5}
self.bar_organization = seeder_logic.seed(GSoCOrganization, org_properties)
#Create some already accepted proposals for bar_organization.
- proposal_properties = {'program': self.program, 'org': self.bar_organization,
- 'mentor': None, 'status': 'accepted'}
+ proposal_properties = {
+ 'program': self.program,
+ 'org': self.bar_organization,
+ 'mentor': None,
+ 'status': proposal_model.STATUS_ACCEPTED,
+ }
self.bar_accepted_proposals = seeder_logic.seedn(
proposal_model.GSoCProposal, 2, proposal_properties)
#proposals which are yet to be accepted.
@@ -114,7 +126,11 @@
self.assertEqual(actual, expected)
#Create an organization which has empty slots but no accepted projects.
- properties = {'scope': self.program, 'slots': 5}
+ properties = {
+ 'scope': self.program,
+ 'slots': 5,
+ 'program': self.program
+ }
organization = seeder_logic.seed(GSoCOrganization, properties)
expected = []
actual = proposal_logic.getProposalsToBeAcceptedForOrg(organization)
diff --git a/tests/app/soc/modules/gsoc/logic/test_slot_transfer.py b/tests/app/soc/modules/gsoc/logic/test_slot_transfer.py
index 52cd08a..4abc226 100644
--- a/tests/app/soc/modules/gsoc/logic/test_slot_transfer.py
+++ b/tests/app/soc/modules/gsoc/logic/test_slot_transfer.py
@@ -34,11 +34,14 @@
def setUp(self):
self.gsoc_program = seeder_logic.seed(GSoCProgram)
self.gsoc_organization = seeder_logic.seed(GSoCOrganization,
- {'scope': self.gsoc_program})
+ {'scope': self.gsoc_program, 'program': self.gsoc_program})
slot_transfer_properties = {'program': self.gsoc_program,
'status': 'accepted'}
- organization_properties = {'scope': self.gsoc_program}
+ organization_properties = {
+ 'scope': self.gsoc_program,
+ 'program': self.gsoc_program
+ }
self.org_entities = seeder_logic.seedn(GSoCOrganization, 10,
organization_properties)
@@ -79,6 +82,6 @@
#An organization has no slot transer entity
expected = []
organization = seeder_logic.seed(GSoCOrganization,
- {'scope': self.gsoc_program})
+ {'scope': self.gsoc_program, 'program': self.gsoc_program})
actual = slot_transfer_logic.getSlotTransferEntitiesForOrg(organization)
self.assertEqual(expected, actual)
diff --git a/tests/app/soc/modules/gsoc/tasks/test_accept_proposals.py b/tests/app/soc/modules/gsoc/tasks/test_accept_proposals.py
index dc40b42..ffa273b 100644
--- a/tests/app/soc/modules/gsoc/tasks/test_accept_proposals.py
+++ b/tests/app/soc/modules/gsoc/tasks/test_accept_proposals.py
@@ -169,7 +169,7 @@
projects = project_model.GSoCProject.all().ancestor(self.student1.profile)
self.assertEqual(projects.count(), 1)
project = projects.get()
- self.assertEqual(project.status, 'accepted')
+ self.assertEqual(project.status, project_model.STATUS_ACCEPTED)
# assert reject task is queued
self.assertTasksInQueue(n=1, url=REJECT_URL)
diff --git a/tests/app/soc/modules/gsoc/views/test_accept_withdraw_projects.py b/tests/app/soc/modules/gsoc/views/test_accept_withdraw_projects.py
new file mode 100644
index 0000000..abf9379
--- /dev/null
+++ b/tests/app/soc/modules/gsoc/views/test_accept_withdraw_projects.py
@@ -0,0 +1,145 @@
+# 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.
+
+"""Unit tests for manage projects related views."""
+
+import json
+
+from soc.modules.gsoc.models import profile as profile_model
+from soc.modules.gsoc.models import project as project_model
+from soc.modules.gsoc.models import proposal as proposal_model
+from soc.modules.gsoc.views import accept_withdraw_projects
+
+from tests import profile_utils
+from tests import test_utils
+
+
+class AcceptProposalsTest(test_utils.GSoCDjangoTestCase):
+ """Unit tests for AcceptProposals view."""
+
+ def setUp(self):
+ self.init()
+ self.url = '/gsoc/admin/proposals/accept/%s' % self.gsoc.key().name()
+
+ def _assertTemplatesUsed(self, response):
+ self.assertGSoCTemplatesUsed(response)
+ self.assertTemplateUsed(response,
+ 'v2/modules/gsoc/accept_withdraw_projects/_project_list.html')
+ self.assertTemplateUsed(response,
+ 'v2/modules/gsoc/accept_withdraw_projects/base.html')
+ self.assertTemplateUsed(response, 'soc/_program_select.html')
+ self.assertTemplateUsed(response, 'soc/list/lists.html')
+ self.assertTemplateUsed(response, 'soc/list/list.html')
+
+ def testLoneUserAccessForbidded(self):
+ """Tests that a lone user cannot access the page."""
+ self.data.createUser()
+ response = self.get(self.url)
+ self.assertResponseForbidden(response)
+ self.assertErrorTemplatesUsed(response)
+
+ def testStudentAccessForbidded(self):
+ """Tests that a student cannot access the page."""
+ self.data.createStudent()
+ response = self.get(self.url)
+ self.assertResponseForbidden(response)
+ self.assertErrorTemplatesUsed(response)
+
+ def testMentorAccessForbidded(self):
+ """Tests that a mentor cannot access the page."""
+ self.data.createMentor(self.org)
+ response = self.get(self.url)
+ self.assertResponseForbidden(response)
+ self.assertErrorTemplatesUsed(response)
+
+ def testOrgAdminAccessForbidded(self):
+ """Tests that an organization admin cannot access the page."""
+ self.data.createOrgAdmin(self.org)
+ response = self.get(self.url)
+ self.assertResponseForbidden(response)
+ self.assertErrorTemplatesUsed(response)
+
+ def testHostAccessGranted(self):
+ """Tests that a program admin can access the page."""
+ self.data.createHost()
+ response = self.get(self.url)
+ self.assertResponseOK(response)
+ self._assertTemplatesUsed(response)
+
+ def testAcceptProposal(self):
+ """Tests that a proposal is correctly accepted."""
+ self.data.createHost()
+
+ mentor = profile_utils.GSoCProfileHelper(
+ self.gsoc, False).createMentor(self.org)
+ student = profile_utils.GSoCProfileHelper(
+ self.gsoc, False).createStudentWithProposal(self.org, mentor)
+ proposal = proposal_model.GSoCProposal.all().ancestor(student).get()
+
+ list_data = [{
+ accept_withdraw_projects._PROPOSAL_KEY: str(proposal.key())
+ }]
+ post_data = {
+ 'button_id': 'accept',
+ 'data': json.dumps(list_data),
+ 'idx': 0,
+ }
+ self.post(self.url, post_data)
+
+ # check if proposal is accepted correctly
+ proposal = proposal_model.GSoCProposal.get(proposal.key())
+ self.assertEqual(proposal.status, proposal_model.STATUS_ACCEPTED)
+
+ # check if a project is created
+ project = project_model.GSoCProject.all().ancestor(student).get()
+ self.assertEqual(project.status, project_model.STATUS_ACCEPTED)
+ self.assertEqual(project.proposal.key(), proposal.key())
+
+ # check if number of projects is updated
+ student_info = profile_model.GSoCStudentInfo.all().ancestor(student).get()
+ self.assertEqual(student_info.number_of_projects, 1)
+
+ def testAcceptTwoProposals(self):
+ """Tests that two proposals can be accepted in the same request."""
+
+ self.data.createHost()
+
+ mentor = profile_utils.GSoCProfileHelper(
+ self.gsoc, False).createMentor(self.org)
+ student1 = profile_utils.GSoCProfileHelper(
+ self.gsoc, False).createStudentWithProposal(self.org, mentor)
+ student2 = profile_utils.GSoCProfileHelper(
+ self.gsoc, False).createStudentWithProposal(self.org, mentor)
+
+ proposal1 = proposal_model.GSoCProposal.all().ancestor(student1).get()
+ proposal2 = proposal_model.GSoCProposal.all().ancestor(student2).get()
+
+ list_data = [
+ {accept_withdraw_projects._PROPOSAL_KEY: str(proposal1.key())},
+ {accept_withdraw_projects._PROPOSAL_KEY: str(proposal2.key())}
+ ]
+
+ post_data = {
+ 'button_id': 'accept',
+ 'data': json.dumps(list_data),
+ 'idx': 0,
+ }
+ self.post(self.url, post_data)
+
+ # check if both proposals are accepted correctly
+ proposal1 = proposal_model.GSoCProposal.get(proposal1.key())
+ self.assertEqual(proposal1.status, proposal_model.STATUS_ACCEPTED)
+
+ proposal2 = proposal_model.GSoCProposal.get(proposal2.key())
+ self.assertEqual(proposal2.status, proposal_model.STATUS_ACCEPTED)
diff --git a/tests/app/soc/modules/gsoc/views/test_accepted_orgs.py b/tests/app/soc/modules/gsoc/views/test_accepted_orgs.py
index d1dc6ea..00854bf 100644
--- a/tests/app/soc/modules/gsoc/views/test_accepted_orgs.py
+++ b/tests/app/soc/modules/gsoc/views/test_accepted_orgs.py
@@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-
"""Tests the view for GSoC accepted organizations."""
from soc.modules.gsoc.models import organization as org_model
diff --git a/tests/app/soc/modules/gsoc/views/test_org_profile.py b/tests/app/soc/modules/gsoc/views/test_org_profile.py
index 1afc6a6..171a941 100644
--- a/tests/app/soc/modules/gsoc/views/test_org_profile.py
+++ b/tests/app/soc/modules/gsoc/views/test_org_profile.py
@@ -145,6 +145,7 @@
org = organization.GSoCOrganization.get_by_key_name(org_key_name)
self.assertEqual(org.link_id, record.org_id)
+ self.assertSameEntity(self.gsoc, org.program)
# Make sure that the new org/veteran value is copied from the organization
# application
diff --git a/tests/app/soc/modules/gsoc/views/test_profile.py b/tests/app/soc/modules/gsoc/views/test_profile.py
index dab090c..785ade4 100644
--- a/tests/app/soc/modules/gsoc/views/test_profile.py
+++ b/tests/app/soc/modules/gsoc/views/test_profile.py
@@ -175,6 +175,7 @@
self.assertEqual(postdata.get('given_name'), profile.given_name)
self.assertEqual(postdata.get('surname'), profile.surname)
+ self.assertSameEntity(self.gsoc, profile.program)
# Make sure student info entity is created with right values.
self.assertEqual(postdata.get('school_name'),
diff --git a/tests/app/soc/modules/gsoc/views/test_project_details.py b/tests/app/soc/modules/gsoc/views/test_project_details.py
index f318321..78fc578 100644
--- a/tests/app/soc/modules/gsoc/views/test_project_details.py
+++ b/tests/app/soc/modules/gsoc/views/test_project_details.py
@@ -46,7 +46,7 @@
project = project_model.GSoCProject.all().get()
project.is_featured = False
- project.status = 'accepted'
+ project.status = project_model.STATUS_ACCEPTED
project.put()
return project
@@ -75,7 +75,7 @@
student_helper.createStudentWithProject(org, mentor)
project = project_model.GSoCProject.all().get()
project.is_featured = False
- project.status = 'accepted'
+ project.status = project_model.STATUS_ACCEPTED
project.put()
return project
@@ -105,7 +105,7 @@
project = project_model.GSoCProject.all().get()
project.is_featured = False
- project.status = 'accepted'
+ project.status = project_model.STATUS_ACCEPTED
project.put()
return project
diff --git a/tests/app/soc/modules/gsoc/views/test_slot_transfer_admin.py b/tests/app/soc/modules/gsoc/views/test_slot_transfer_admin.py
index b3a929d..2e0226c 100644
--- a/tests/app/soc/modules/gsoc/views/test_slot_transfer_admin.py
+++ b/tests/app/soc/modules/gsoc/views/test_slot_transfer_admin.py
@@ -73,7 +73,8 @@
org_properties = {
'status': 'active',
- 'scope': self.gsoc
+ 'scope': self.gsoc,
+ 'program': self.gsoc,
}
other_org = seeder_logic.seed(org_model.GSoCOrganization, org_properties)
diff --git a/tests/app/soc/modules/gsoc/views/test_student_evaluation.py b/tests/app/soc/modules/gsoc/views/test_student_evaluation.py
index 7da88ca..6a32280 100644
--- a/tests/app/soc/modules/gsoc/views/test_student_evaluation.py
+++ b/tests/app/soc/modules/gsoc/views/test_student_evaluation.py
@@ -103,10 +103,12 @@
def createProject(self, override_properties={}):
properties = {
- 'is_featured': False, 'mentors': [],
- 'status': 'accepted', 'program': self.gsoc, 'org': self.org,
-
- }
+ 'is_featured': False,
+ 'mentors': [],
+ 'status': project_model.STATUS_ACCEPTED,
+ 'program': self.gsoc,
+ 'org': self.org,
+ }
properties.update(override_properties)
return self.seed(GSoCProject, properties)
diff --git a/tests/profile_utils.py b/tests/profile_utils.py
index 4d8d954..aa63763 100644
--- a/tests/profile_utils.py
+++ b/tests/profile_utils.py
@@ -238,7 +238,7 @@
properties = {
'link_id': user.link_id, 'student_info': None, 'user': user,
'parent': user, 'scope': self.program, 'status': 'active',
- 'email': self.user.account.email(),
+ 'email': self.user.account.email(), 'program': self.program,
'mentor_for': [], 'org_admin_for': [],
'is_org_admin': False, 'is_mentor': False, 'is_student': False
}
@@ -313,8 +313,8 @@
projects for the current program.
"""
student = self.createStudentWithProposal(org, mentor)
- from soc.modules.gsoc.models.proposal import GSoCProposal
- proposal = GSoCProposal.all().ancestor(student).get()
+ from soc.modules.gsoc.models import proposal as proposal_model
+ proposal = proposal_model.GSoCProposal.all().ancestor(student).get()
student.student_info.number_of_projects = n
@@ -323,10 +323,16 @@
# this to make project removal easy.
student.student_info.project_for_orgs += [org.key()] * n
student.student_info.put()
- from soc.modules.gsoc.models.project import GSoCProject
- properties = {'program': self.program, 'org': org, 'status': 'accepted',
- 'parent': self.profile, 'mentors': [mentor.key()], 'proposal': proposal}
- self.seedn(GSoCProject, properties, n)
+ from soc.modules.gsoc.models import project as project_model
+ properties = {
+ 'program': self.program,
+ 'org': org,
+ 'status': project_model.STATUS_ACCEPTED,
+ 'parent': self.profile,
+ 'mentors': [mentor.key()],
+ 'proposal': proposal
+ }
+ self.seedn(project_model.GSoCProject, properties, n)
return self.profile
def createMentorWithProject(self, org, student):
@@ -388,7 +394,7 @@
properties = {
'link_id': user.link_id, 'student_info': None, 'user': user,
'parent': user, 'scope': self.program, 'status': 'active',
- 'email': self.user.account.email(),
+ 'email': self.user.account.email(), 'program': self.program,
'mentor_for': [], 'org_admin_for': [],
'is_org_admin': False, 'is_mentor': False, 'is_student': False
}
diff --git a/tests/program_utils.py b/tests/program_utils.py
index e8af1cd..6853269 100644
--- a/tests/program_utils.py
+++ b/tests/program_utils.py
@@ -226,8 +226,14 @@
This new organization will not be stored in self.org but returned.
"""
super(GSoCProgramHelper, self).createNewOrg(override)
- properties = {'scope': self.program, 'status': 'active',
- 'scoring_disabled': False, 'max_score': 5, 'home': None}
+ properties = {
+ 'scope': self.program,
+ 'status': 'active',
+ 'scoring_disabled': False,
+ 'max_score': 5,
+ 'home': None,
+ 'program': self.program,
+ }
properties.update(override)
return self.seed(GSoCOrganization, properties)
@@ -315,7 +321,8 @@
'status': 'active',
'home': None,
'task_quota_limit': 100,
- 'backup_winner': None
+ 'backup_winner': None,
+ 'program': self.program,
}
properties.update(override)
return self.seed(GCIOrganization, properties)
diff --git a/tests/test_utils.py b/tests/test_utils.py
index a805e3b..ac42493 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -702,7 +702,7 @@
properties = {'scope': self.gsoc, 'status': 'active',
'scoring_disabled': False, 'max_score': 5,
- 'home': None,}
+ 'home': None, 'program': self.gsoc}
properties.update(override)
return self.seed(GSoCOrganization, properties)