17
17
from django .conf import settings
18
18
from django .core .urlresolvers import reverse
19
19
from django .db import models
20
+ from django .utils import timezone
20
21
from django .utils .encoding import python_2_unicode_compatible
21
22
from django .utils .translation import ugettext
22
23
from django .utils .translation import ugettext_lazy as _
23
24
from guardian .shortcuts import assign
25
+ from jsonfield import JSONField
24
26
from taggit .managers import TaggableManager
25
27
26
28
from readthedocs .core .utils import broadcast
@@ -128,6 +130,21 @@ def __str__(self):
128
130
pk = self .pk ,
129
131
))
130
132
133
+ @property
134
+ def config (self ):
135
+ """
136
+ Proxy to the configuration of the build.
137
+
138
+ :returns: The configuration used in the last successful build.
139
+ :rtype: dict
140
+ """
141
+ last_build = (
142
+ self .builds .filter (state = 'finished' , success = True )
143
+ .order_by ('-date' )
144
+ .first ()
145
+ )
146
+ return last_build .config
147
+
131
148
@property
132
149
def commit_name (self ):
133
150
"""
@@ -450,6 +467,7 @@ class Build(models.Model):
450
467
exit_code = models .IntegerField (_ ('Exit code' ), null = True , blank = True )
451
468
commit = models .CharField (
452
469
_ ('Commit' ), max_length = 255 , null = True , blank = True )
470
+ _config = JSONField (_ ('Configuration used in the build' ), default = dict )
453
471
454
472
length = models .IntegerField (_ ('Build Length' ), null = True , blank = True )
455
473
@@ -463,11 +481,81 @@ class Build(models.Model):
463
481
464
482
objects = BuildQuerySet .as_manager ()
465
483
484
+ CONFIG_KEY = '__config'
485
+
466
486
class Meta (object ):
467
487
ordering = ['-date' ]
468
488
get_latest_by = 'date'
469
489
index_together = [['version' , 'state' , 'type' ]]
470
490
491
+ def __init__ (self , * args , ** kwargs ):
492
+ super (Build , self ).__init__ (* args , ** kwargs )
493
+ self ._config_changed = False
494
+
495
+ @property
496
+ def previous (self ):
497
+ """
498
+ Returns the previous build to the current one.
499
+
500
+ Matching the project and version.
501
+ """
502
+ date = self .date or timezone .now ()
503
+ if self .project is not None and self .version is not None :
504
+ return (
505
+ Build .objects
506
+ .filter (
507
+ project = self .project ,
508
+ version = self .version ,
509
+ date__lt = date ,
510
+ )
511
+ .order_by ('-date' )
512
+ .first ()
513
+ )
514
+ return None
515
+
516
+ @property
517
+ def config (self ):
518
+ """
519
+ Get the config used for this build.
520
+
521
+ Since we are saving the config into the JSON field only when it differs
522
+ from the previous one, this helper returns the correct JSON used in
523
+ this Build object (it could be stored in this object or one of the
524
+ previous ones).
525
+ """
526
+ if self .CONFIG_KEY in self ._config :
527
+ return Build .objects .get (pk = self ._config [self .CONFIG_KEY ])._config
528
+ return self ._config
529
+
530
+ @config .setter
531
+ def config (self , value ):
532
+ """
533
+ Set `_config` to value.
534
+
535
+ `_config` should never be set directly from outside the class.
536
+ """
537
+ self ._config = value
538
+ self ._config_changed = True
539
+
540
+ def save (self , * args , ** kwargs ): # noqa
541
+ """
542
+ Save object.
543
+
544
+ To save space on the db we only save the config if it's different
545
+ from the previous one.
546
+
547
+ If the config is the same, we save the pk of the object
548
+ that has the **real** config under the `CONFIG_KEY` key.
549
+ """
550
+ if self .pk is None or self ._config_changed :
551
+ previous = self .previous
552
+ if (previous is not None and
553
+ self ._config and self ._config == previous .config ):
554
+ previous_pk = previous ._config .get (self .CONFIG_KEY , previous .pk )
555
+ self ._config = {self .CONFIG_KEY : previous_pk }
556
+ super (Build , self ).save (* args , ** kwargs )
557
+ self ._config_changed = False
558
+
471
559
def __str__ (self ):
472
560
return ugettext (
473
561
'Build {project} for {usernames} ({pk})' .format (
0 commit comments