Set new Melange version number to 0-5-20090410 in app.yaml.template.
[Melange.git] / scripts / stats.py
blob8fbc103e3baa9ef09d3d31d530d39984694436d9
1 #!/usr/bin/python2.5
3 # Copyright 2009 the Melange authors.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """Starts an interactive shell with statistic helpers.
18 """
20 __authors__ = [
21 '"Sverre Rabbelier" <sverre@rabbelier.nl>',
25 import cPickle
26 import datetime
27 import operator
28 import sys
30 import interactive
33 def dateFetch(queryGen, last=None, batchSize=100):
34 """Iterator that yields an entity in batches.
36 Args:
37 queryGen: should return a Query object
38 last: used to .filter() for last_modified_on
39 batchSize: how many entities to retrieve in one datastore call
41 Retrieved from http://tinyurl.com/d887ll (AppEngine cookbook).
42 """
44 from google.appengine.ext import db
46 # AppEngine will not fetch more than 1000 results
47 batchSize = min(batchSize,1000)
49 query = None
50 done = False
51 count = 0
53 while not done:
54 print count
55 query = queryGen()
56 query.order('last_modified_on')
57 if last:
58 query.filter("last_modified_on > ", last)
59 results = query.fetch(batchSize)
60 for result in results:
61 count += 1
62 yield result
63 if batchSize > len(results):
64 done = True
65 else:
66 last = results[-1].last_modified_on
69 def addKey(target, fieldname):
70 """Adds the key of the specified field.
71 """
73 result = target.copy()
74 result['%s_key' % fieldname] = target[fieldname].key().name()
75 return result
78 def getEntities(model):
79 """Returns all users as dictionary.
80 """
82 def wrapped():
83 gen = lambda: model.all()
84 it = interactive.deepFetch(gen)
86 entities = [(i.key().name(), i) for i in it]
87 return dict(entities)
89 return wrapped
92 def getProps(last=None):
93 """Returns all proposals as a list of dictionaries.
94 """
96 key_order = [
97 'link_id', 'scope_path', 'title', 'abstract', 'content',
98 'additional_info', '_mentor', 'possible_mentors', 'score',
99 'status', '_org', 'created_on', 'last_modified_on']
101 from soc.models.student_proposal import StudentProposal
103 gen = lambda: StudentProposal.all()
105 it = dateFetch(gen, last)
107 proposals = [(i.key().name(), i.toDict(key_order)) for i in it]
108 if proposals:
109 last = i.last_modified_on # last modified entity
110 else:
111 last = datetime.datetime.now()
113 return dict(proposals), last
116 def orgStats(target, orgs):
117 """Retrieves org stats.
120 from soc.logic import dicts
122 orgs = [(v.key(), v) for k, v in orgs.iteritems()]
123 orgs = dict(orgs)
125 grouped = dicts.groupby(target.values(), '_org')
127 grouped = [(orgs[k], v) for k, v in grouped.iteritems()]
128 popularity = [(k.link_id, len(v)) for k, v in grouped]
130 return dict(grouped), dict(popularity)
133 def countStudentsWithProposals():
134 """Retrieves number of Students who have submitted at least one Student Proposal.
137 proposals = getStudentProposals()
138 students = {}
140 for proposal_key in proposals.keys():
141 students[proposals[proposal_key].scope_path] = True
143 return len(students)
146 def printPopularity(popularity):
147 """Prints the popularity for the specified proposals.
150 g = operator.itemgetter(1)
152 for item in sorted(popularity.iteritems(), key=g, reverse=True):
153 print "%s: %d" % item
156 def savePopularity(popularities):
157 """Saves the specified popularities.
160 import logging
161 from google.appengine.ext import db
163 from soc.models.organization import Organization
165 def txn(key_name, popularity):
166 org = Organization.get_by_key_name(key_name)
167 org.nr_applications = popularity
168 org.put()
170 for key, value in sorted(popularities.iteritems()):
171 print key
172 db.run_in_transaction_custom_retries(10, txn, key, value)
174 print "done"
177 def addFollower(follower, proposals, add_public=True, add_private=True):
178 """Adds a user as follower to the specified proposals.
180 Args:
181 follower: the User to add as follower
182 proposals: a list with the StudnetProposals that should be subscribed to
183 add_public: whether the user is subscribed to public updates
184 add_private: whether the user should be subscribed to private updates
187 from soc.models.review_follower import ReviewFollower
189 result = []
191 for i in proposals:
192 properties = {
193 'user': follower,
194 'link_id': follower.link_id,
195 'scope': i,
196 'scope_path': i.key().name(),
197 'key_name': '%s/%s' % (i.key().name(), follower.link_id),
198 'subscribed_public': add_public,
199 'subscribed_private': add_private,
202 entity = ReviewFollower(**properties)
203 result.append(entity)
205 return result
208 def loadPickle(name):
209 """Loads a pickle.
212 f = open(name + '.dat')
213 return cPickle.load(f)
216 def dumpPickle(target, name):
217 """Dumps a pickle.
220 f = open("%s.dat" % name, 'w')
221 cPickle.dump(target, f)
224 def main(args):
225 """Main routine.
228 interactive.setup()
230 from soc.models.organization import Organization
231 from soc.models.user import User
232 from soc.models.student import Student
233 from soc.models.mentor import Mentor
234 from soc.models.org_admin import OrgAdmin
236 context = {
237 'load': loadPickle,
238 'dump': dumpPickle,
239 'orgStats': orgStats,
240 'printPopularity': printPopularity,
241 'savePopularity': savePopularity,
242 'getOrgs': getEntities(Organization),
243 'getUsers': getEntities(User),
244 'getStudents': getEntities(Student),
245 'getMentors': getEntities(Mentor),
246 'getOrgAdmins': getEntities(OrgAdmin),
247 'getProps': getProps,
248 'countStudentsWithProposals': countStudentsWithProposals,
249 'addFollower': addFollower,
250 'Organization': Organization,
251 'User': User,
252 'Student': Student,
253 'Mentor': Mentor,
254 'OrgAdmin': OrgAdmin,
257 interactive.remote(args, context)
259 if __name__ == '__main__':
260 if len(sys.argv) < 2:
261 print "Usage: %s app_id [host]" % (sys.argv[0],)
262 sys.exit(1)
264 main(sys.argv[1:])