class Screenshot(models.Model): DIFF_EPSILON = 1e-4 STATE_PENDING = 0 STATE_MATCHING = 1 STATE_DIFFERENT = 2 STATE_NEW = 3 STATE_DELETED = 4 STATE_CHOICES = ( (STATE_PENDING, 'Pending'), (STATE_MATCHING, 'Matching'), (STATE_DIFFERENT, 'Different'), (STATE_NEW, 'New'), (STATE_DELETED, 'Deleted'), ) build = models.ForeignKey(Build, models.CASCADE, related_name='screenshots') state = models.IntegerField(choices=STATE_CHOICES, default=STATE_PENDING) parent = models.ForeignKey('self', models.SET_NULL, null=True, related_name='children') date_created = models.DateTimeField(auto_now_add=True) name = models.CharField(max_length=255) metadata_json = models.TextField(blank=True, null=True) image = models.ImageField(upload_to='screenshots/') image_diff = models.ImageField(upload_to='screenshot_diffs/', null=True) image_diff_amount = models.FloatField(null=True) archived = models.BooleanField(default=False, db_index=True) @cached_property def metadata(self): if self.metadata_json: return json.loads(self.metadata_json) return None @cached_property def metadata_pretty(self): if self.metadata: return json.dumps(self.metadata, indent=2, ensure_ascii=False) return None def find_parent(self): parent_build = self.build.parent if parent_build: return parent_build.screenshots.filter(name=self.name).first() else: return None def process(self): self.parent = self.find_parent() if self.parent is None: self.state = Screenshot.STATE_NEW else: wand_current = Image(filename=self.image.path) wand_parent = Image(filename=self.parent.image.path) wand_diff, difference = wand_current.compare( wand_parent, metric='root_mean_square') self.image_diff_amount = difference if difference > self.DIFF_EPSILON: self.image_diff = ContentFile( wand_diff.make_blob('png'), name='{}_{}_diff.png'.format(self.parent.id, self.id), ) self.state = Screenshot.STATE_DIFFERENT else: self.state = Screenshot.STATE_MATCHING def archive(self): if self.image: self.image.delete(save=False) if self.image_diff: self.image_diff.delete(save=False) self.archived = True class Meta: unique_together = (('build', 'name'), )