1 |
commit: e6a1805fa32a963d1728c1e95797a13874662200 |
2 |
Author: Slava Bacherikov <slava <AT> bacher09 <DOT> org> |
3 |
AuthorDate: Thu Jul 5 17:43:48 2012 +0000 |
4 |
Commit: Slava Bacherikov <slava <AT> bacherikov <DOT> org <DOT> ua> |
5 |
CommitDate: Thu Jul 5 17:43:48 2012 +0000 |
6 |
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/gentoo-packages.git;a=commit;h=e6a1805f |
7 |
|
8 |
Add stats calculating |
9 |
|
10 |
--- |
11 |
gpackages/apps/packages/models.py | 92 ++++++++++++++++++-- |
12 |
gpackages/apps/packages/stats.py | 31 +++++++ |
13 |
.../libs/package_info/package_backends/pkgcore.py | 6 ++ |
14 |
.../libs/package_info/package_backends/portage.py | 6 ++ |
15 |
4 files changed, 127 insertions(+), 8 deletions(-) |
16 |
|
17 |
diff --git a/gpackages/apps/packages/models.py b/gpackages/apps/packages/models.py |
18 |
index 51b839e..efb4638 100644 |
19 |
--- a/gpackages/apps/packages/models.py |
20 |
+++ b/gpackages/apps/packages/models.py |
21 |
@@ -6,6 +6,7 @@ from package_info.generic import get_from_kwargs_and_del |
22 |
from package_info.repo_info import REPOS_TYPE |
23 |
# relative |
24 |
from .keywords import KeywordRepr |
25 |
+from .stats import StatsMixin |
26 |
#from utils import get_link |
27 |
from package_info.validators import validate_url, validate_email, \ |
28 |
validate_name |
29 |
@@ -90,12 +91,17 @@ class ArchesModel(models.Model): |
30 |
def __unicode__(self): |
31 |
return self.name |
32 |
|
33 |
-class RepositoryModel(AbstractDateTimeModel): |
34 |
+class RepositoryModel(StatsMixin, AbstractDateTimeModel): |
35 |
QUALITY_CHOICES = ( (0, 'stable'), |
36 |
(1, 'testing'), |
37 |
(2, 'experimental'), |
38 |
) |
39 |
|
40 |
+ stats_params = ( |
41 |
+ ('packages_count', 'packagemodel'), |
42 |
+ ('ebuilds_count', 'packagemodel__ebuildmodel'), |
43 |
+ ) |
44 |
+ |
45 |
def __init__(self, *args, **kwargs): |
46 |
repo = get_from_kwargs_and_del('repo', kwargs) |
47 |
super(RepositoryModel, self).__init__(*args, **kwargs) |
48 |
@@ -113,6 +119,12 @@ class RepositoryModel(AbstractDateTimeModel): |
49 |
official = models.BooleanField(default = False) |
50 |
quality = models.PositiveSmallIntegerField(choices = QUALITY_CHOICES) |
51 |
|
52 |
+ # Maybe in future auto generate this field by metaclass ? |
53 |
+ # For fast stats |
54 |
+ packages_count = models.PositiveIntegerField(default = 0) |
55 |
+ ebuilds_count = models.PositiveIntegerField(default = 0) |
56 |
+ |
57 |
+ |
58 |
objects = managers.RepositoryManager() |
59 |
|
60 |
def init_by_repo(self, repo): |
61 |
@@ -194,7 +206,7 @@ class RepositorySourceModel(models.Model): |
62 |
def __unicode__(self): |
63 |
return self.url |
64 |
|
65 |
-class CategoryModel(models.Model): |
66 |
+class CategoryModel(StatsMixin, models.Model): |
67 |
def __init__(self, *args, **kwargs): |
68 |
super(CategoryModel, self).__init__(*args, **kwargs) |
69 |
|
70 |
@@ -202,6 +214,13 @@ class CategoryModel(models.Model): |
71 |
if isinstance(category, AbstractCategory): |
72 |
self.update_by_category(category) |
73 |
|
74 |
+ stats_params = ( |
75 |
+ ('virtual_packages_count', 'virtualpackagemodel'), |
76 |
+ ('packages_count', 'virtualpackagemodel__packagemodel'), |
77 |
+ ('repositories_count', 'virtualpackagemodel__packagemodel__repository'), |
78 |
+ ('ebuilds_count', 'virtualpackagemodel__packagemodel__ebuildmodel'), |
79 |
+ ) |
80 |
+ |
81 |
def update_by_category(self, category): |
82 |
self.description = category.metadata.default_descr |
83 |
self.metadata_hash = category.metadata_sha1 |
84 |
@@ -212,21 +231,47 @@ class CategoryModel(models.Model): |
85 |
category = models.CharField(unique = True, max_length = 70, db_index = True) |
86 |
description = models.TextField(blank = True, null = True) |
87 |
metadata_hash = models.CharField(max_length = 128, null = True) |
88 |
+ |
89 |
+ # Maybe in future auto generate this field by metaclass ? |
90 |
+ # For fast stats |
91 |
+ virtual_packages_count = models.PositiveIntegerField(default = 0) |
92 |
+ packages_count = models.PositiveIntegerField(default = 0) |
93 |
+ repositories_count = models.PositiveIntegerField(default = 0) |
94 |
+ ebuilds_count = models.PositiveIntegerField(default = 0) |
95 |
|
96 |
def __unicode__(self): |
97 |
return unicode(self.category) |
98 |
|
99 |
-class MaintainerModel(AbstractDateTimeModel): |
100 |
+class MaintainerModel(StatsMixin, AbstractDateTimeModel): |
101 |
|
102 |
def __init__(self, *args, **kwargs): |
103 |
maintainer = get_from_kwargs_and_del('maintainer', kwargs) |
104 |
super(MaintainerModel, self).__init__(*args, **kwargs) |
105 |
if maintainer is not None: |
106 |
self.init_by_maintainer(maintainer) |
107 |
+ |
108 |
+ stats_params = ( |
109 |
+ ('packages_count', 'packagemodel'), |
110 |
+ ('herds_count', 'herdsmodel'), |
111 |
+ ('ebuilds_count', 'packagemodel__ebuildmodel'), |
112 |
+ ('repositories_count', 'packagemodel__ebuildmodel'), |
113 |
+ ('news_author_count', 'author_news_set'), |
114 |
+ ('news_translator_count', 'translator_news_set') |
115 |
+ ) |
116 |
|
117 |
name = models.CharField(max_length = 255, blank = True, null = True) |
118 |
email = models.EmailField(unique = True, validators = [validate_email], db_index = True) |
119 |
|
120 |
+ # For fast stats |
121 |
+ # Maybe use django-composition ? |
122 |
+ # Maybe in future auto generate this field by metaclass ? |
123 |
+ herds_count = models.PositiveIntegerField(default = 0) |
124 |
+ packages_count = models.PositiveIntegerField(default = 0) |
125 |
+ ebuilds_count = models.PositiveIntegerField(default = 0) |
126 |
+ repositories_count = models.PositiveIntegerField(default = 0) |
127 |
+ news_author_count = models.PositiveIntegerField(default = 0) |
128 |
+ news_translator_count = models.PositiveIntegerField(default = 0) |
129 |
+ |
130 |
objects = managers.MaintainerManager() |
131 |
|
132 |
def init_by_maintainer(self, maintainer): |
133 |
@@ -246,7 +291,7 @@ class MaintainerModel(AbstractDateTimeModel): |
134 |
class Meta: |
135 |
ordering = ('name',) |
136 |
|
137 |
-class HerdsModel(AbstractDateTimeModel): |
138 |
+class HerdsModel(StatsMixin, AbstractDateTimeModel): |
139 |
|
140 |
def __init__(self, *args, **kwargs): |
141 |
herd = get_from_kwargs_and_del('herd', kwargs) |
142 |
@@ -254,11 +299,26 @@ class HerdsModel(AbstractDateTimeModel): |
143 |
if herd is not None: |
144 |
self.init_by_herd(herd) |
145 |
|
146 |
+ stats_params = ( |
147 |
+ ('packages_count', 'packagemodel'), |
148 |
+ ('maintainers_count', 'maintainers'), |
149 |
+ ('ebuilds_count', 'packagemodel__ebuildmodel'), |
150 |
+ ('repositories_count', 'packagemodel__repository'), |
151 |
+ ) |
152 |
+ |
153 |
name = models.CharField(unique = True, max_length = 150, db_index = True) |
154 |
email = models.EmailField(validators = [validate_email]) |
155 |
description = models.TextField(blank = True, null = True) |
156 |
maintainers = models.ManyToManyField(MaintainerModel, blank = True) |
157 |
|
158 |
+ # For fast stats |
159 |
+ # Maybe use django-composition ? |
160 |
+ # Maybe in future auto generate this field by metaclass ? |
161 |
+ maintainers_count = models.PositiveIntegerField(default = 0) |
162 |
+ packages_count = models.PositiveIntegerField(default = 0) |
163 |
+ ebuilds_count = models.PositiveIntegerField(default = 0) |
164 |
+ repositories_count = models.PositiveIntegerField(default = 0) |
165 |
+ |
166 |
objects = managers.HerdsManager() |
167 |
|
168 |
def init_by_herd(self, herd): |
169 |
@@ -304,7 +364,8 @@ class VirtualPackageModel(models.Model): |
170 |
class Meta: |
171 |
unique_together = ('name', 'category') |
172 |
|
173 |
-class PackageModel(AbstractDateTimeModel): |
174 |
+class PackageModel(StatsMixin, AbstractDateTimeModel): |
175 |
+ |
176 |
def __init__(self, *args, **kwargs): |
177 |
package_object, category = \ |
178 |
get_from_kwargs_and_del(('package','category' ), kwargs) |
179 |
@@ -313,6 +374,9 @@ class PackageModel(AbstractDateTimeModel): |
180 |
if isinstance(package_object, AbstarctPackage): |
181 |
self.init_by_package(package_object, category = category) |
182 |
|
183 |
+ stats_params = ( |
184 |
+ ('ebuilds_count', 'ebuildmodel'), |
185 |
+ ) |
186 |
|
187 |
virtual_package = models.ForeignKey(VirtualPackageModel, db_index = True) |
188 |
changelog = models.TextField(blank = True, null = True) |
189 |
@@ -329,6 +393,8 @@ class PackageModel(AbstractDateTimeModel): |
190 |
description = models.TextField(blank = True, null = True) |
191 |
repository = models.ForeignKey(RepositoryModel, db_index = True) |
192 |
# Different versions can have different licenses, or homepages. |
193 |
+ |
194 |
+ ebuilds_count = models.PositiveIntegerField(default = 0) |
195 |
|
196 |
objects = managers.PackageManager() |
197 |
|
198 |
@@ -407,10 +473,16 @@ class PackageModel(AbstractDateTimeModel): |
199 |
unique_together = ('virtual_package', 'repository') |
200 |
ordering = ('-updated_datetime',) |
201 |
|
202 |
-class UseFlagModel(models.Model): |
203 |
+class UseFlagModel(StatsMixin, models.Model): |
204 |
+ stats_params = ( |
205 |
+ ('ebuilds_count', 'ebuildmodel'), |
206 |
+ ) |
207 |
+ |
208 |
name = models.CharField(unique = True, max_length = 60, db_index = True) |
209 |
description = models.TextField(blank = True) |
210 |
|
211 |
+ ebuilds_count = models.PositiveIntegerField(default = 0) |
212 |
+ |
213 |
def __unicode__(self): |
214 |
return self.name |
215 |
|
216 |
@@ -431,9 +503,14 @@ class UseFlagDescriptionModel(models.Model): |
217 |
class Meta: |
218 |
unique_together = ('use_flag', 'package') |
219 |
|
220 |
-class LicenseModel(models.Model): |
221 |
+class LicenseModel(StatsMixin, models.Model): |
222 |
+ stats_params = ( |
223 |
+ ('ebuilds_count', 'ebuildmodel'), |
224 |
+ ) |
225 |
+ |
226 |
name = models.CharField(unique = True, max_length = 60, db_index = True) |
227 |
#description = TextField() |
228 |
+ ebuilds_count = models.PositiveIntegerField(default = 0) |
229 |
|
230 |
def __unicode__(self): |
231 |
return self.name |
232 |
@@ -648,4 +725,3 @@ class Keyword(models.Model): |
233 |
class Meta: |
234 |
unique_together = ('ebuild', 'arch') |
235 |
|
236 |
- |
237 |
|
238 |
diff --git a/gpackages/apps/packages/stats.py b/gpackages/apps/packages/stats.py |
239 |
new file mode 100644 |
240 |
index 0000000..9bf3efa |
241 |
--- /dev/null |
242 |
+++ b/gpackages/apps/packages/stats.py |
243 |
@@ -0,0 +1,31 @@ |
244 |
+from django.db.models import Count |
245 |
+from itertools import starmap |
246 |
+ |
247 |
+gen_prefix = lambda x: x + '_prefix' |
248 |
+ |
249 |
+def gen_query_dict(params): |
250 |
+ query_dict = {} |
251 |
+ for key, value in starmap(lambda x,y: (gen_prefix(x), y), params): |
252 |
+ query_dict[key] = Count(value, distinct = True) |
253 |
+ |
254 |
+ return query_dict |
255 |
+ |
256 |
+def make_query_for_stats(model, params): |
257 |
+ query_dict = gen_query_dict(params) |
258 |
+ return model.objects.annotate(**query_dict) |
259 |
+ |
260 |
+def update_stats(model, params): |
261 |
+ query = make_query_for_stats(model, params) |
262 |
+ for obj in query: |
263 |
+ for key, mykey in starmap(lambda x,y: (x, gen_prefix(x)), params): |
264 |
+ setattr(obj, key, getattr(obj, mykey)) |
265 |
+ |
266 |
+ obj.save(force_update = True) |
267 |
+ |
268 |
+class StatsMixin(object): |
269 |
+ |
270 |
+ stats_params = ((),) |
271 |
+ |
272 |
+ @classmethod |
273 |
+ def calc_stats(cls): |
274 |
+ update_stats(cls, cls.stats_params) |
275 |
|
276 |
diff --git a/gpackages/libs/package_info/package_backends/pkgcore.py b/gpackages/libs/package_info/package_backends/pkgcore.py |
277 |
index 9a17208..2ad6b69 100644 |
278 |
--- a/gpackages/libs/package_info/package_backends/pkgcore.py |
279 |
+++ b/gpackages/libs/package_info/package_backends/pkgcore.py |
280 |
@@ -57,6 +57,9 @@ class Portage(PortageMixin): |
281 |
|
282 |
def __unicode__(self): |
283 |
return u'pkgcore' |
284 |
+ |
285 |
+ def __len__(self): |
286 |
+ return len(self.repo_list) |
287 |
|
288 |
class PortTree(PortTreeMixin): |
289 |
|
290 |
@@ -86,6 +89,9 @@ class PortTree(PortTreeMixin): |
291 |
def _versions(self): |
292 |
return self._repo_obj.versions |
293 |
|
294 |
+ def __len__(self): |
295 |
+ return len(self.categories) |
296 |
+ |
297 |
class Category(CategoryMixin): |
298 |
|
299 |
__slots__ = ('_repo_obj', 'name') |
300 |
|
301 |
diff --git a/gpackages/libs/package_info/package_backends/portage.py b/gpackages/libs/package_info/package_backends/portage.py |
302 |
index 729e6e9..e3707d4 100644 |
303 |
--- a/gpackages/libs/package_info/package_backends/portage.py |
304 |
+++ b/gpackages/libs/package_info/package_backends/portage.py |
305 |
@@ -51,6 +51,9 @@ class Portage(PortageMixin): |
306 |
def __unicode__(self): |
307 |
return u'portage' |
308 |
|
309 |
+ def __len__(self): |
310 |
+ return len(self.tree_order) |
311 |
+ |
312 |
class PortTree(PortTreeMixin): |
313 |
"Represent portage tree as object" |
314 |
|
315 |
@@ -72,6 +75,9 @@ class PortTree(PortTreeMixin): |
316 |
"Full path to portage tree" |
317 |
return self.porttree |
318 |
|
319 |
+ def __len__(self): |
320 |
+ return len(PORTDB.settings.categories) |
321 |
+ |
322 |
class Category(CategoryMixin): |
323 |
"Represent category of portage tree as object" |