models.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. from base64 import b64decode
  2. from bencode import bdecode, bencode
  3. from datetime import datetime
  4. import hashlib
  5. from pytz import utc
  6. from django.conf import settings
  7. from django.core.urlresolvers import reverse
  8. from django.db import models
  9. from django.db.models.signals import pre_save
  10. from django.utils.safestring import mark_safe
  11. from main.fields import PositiveBigIntegerField
  12. from main.utils import set_created_field, parse_markdown
  13. class IsoOption(models.Model):
  14. name = models.CharField(max_length=200)
  15. def __unicode__(self):
  16. return self.name
  17. class Meta:
  18. abstract = True
  19. class RollbackOption(IsoOption):
  20. class Meta:
  21. abstract = True
  22. class Iso(models.Model):
  23. name = models.CharField(max_length=255)
  24. created = models.DateTimeField(editable=False)
  25. removed = models.DateTimeField(null=True, blank=True, default=None)
  26. active = models.BooleanField(default=True)
  27. def get_absolute_url(self):
  28. return reverse('releng-results-iso', args=[self.pk])
  29. def __unicode__(self):
  30. return self.name
  31. class Meta:
  32. verbose_name = 'ISO'
  33. class Architecture(IsoOption):
  34. pass
  35. class IsoType(IsoOption):
  36. class Meta:
  37. verbose_name = 'ISO type'
  38. class BootType(IsoOption):
  39. pass
  40. class HardwareType(IsoOption):
  41. pass
  42. class InstallType(IsoOption):
  43. pass
  44. class Source(IsoOption):
  45. pass
  46. class ClockChoice(IsoOption):
  47. pass
  48. class Filesystem(RollbackOption):
  49. pass
  50. class Module(RollbackOption):
  51. pass
  52. class Bootloader(IsoOption):
  53. pass
  54. class Test(models.Model):
  55. user_name = models.CharField(max_length=500)
  56. user_email = models.EmailField('email address')
  57. ip_address = models.GenericIPAddressField('IP address', unpack_ipv4=True)
  58. created = models.DateTimeField(editable=False)
  59. iso = models.ForeignKey(Iso)
  60. architecture = models.ForeignKey(Architecture)
  61. iso_type = models.ForeignKey(IsoType)
  62. boot_type = models.ForeignKey(BootType)
  63. hardware_type = models.ForeignKey(HardwareType)
  64. install_type = models.ForeignKey(InstallType)
  65. source = models.ForeignKey(Source)
  66. clock_choice = models.ForeignKey(ClockChoice)
  67. filesystem = models.ForeignKey(Filesystem)
  68. modules = models.ManyToManyField(Module, blank=True)
  69. bootloader = models.ForeignKey(Bootloader)
  70. rollback_filesystem = models.ForeignKey(Filesystem,
  71. related_name="rollback_test_set", null=True, blank=True)
  72. rollback_modules = models.ManyToManyField(Module,
  73. related_name="rollback_test_set", blank=True)
  74. success = models.BooleanField(default=True)
  75. comments = models.TextField(null=True, blank=True)
  76. class Release(models.Model):
  77. release_date = models.DateField(db_index=True)
  78. version = models.CharField(max_length=50, unique=True)
  79. kernel_version = models.CharField(max_length=50, blank=True)
  80. md5_sum = models.CharField('MD5 digest', max_length=32, blank=True)
  81. sha1_sum = models.CharField('SHA1 digest', max_length=40, blank=True)
  82. created = models.DateTimeField(editable=False)
  83. last_modified = models.DateTimeField(editable=False)
  84. available = models.BooleanField(default=True)
  85. info = models.TextField('Public information', blank=True)
  86. torrent_data = models.TextField(blank=True,
  87. help_text="base64-encoded torrent file")
  88. class Meta:
  89. get_latest_by = 'release_date'
  90. ordering = ('-release_date', '-version')
  91. def __unicode__(self):
  92. return self.version
  93. def get_absolute_url(self):
  94. return reverse('releng-release-detail', args=[self.version])
  95. def dir_path(self):
  96. return "iso/%s/" % self.version
  97. def iso_url(self):
  98. return "iso/%s/%s-%s-x86_64.iso" % (self.version, settings.BRANDING_SLUG, self.version)
  99. def magnet_uri(self):
  100. query = [
  101. ('dn', "%s-%s-x86_64.iso" % (settings.BRANDING_SLUG, self.version)),
  102. ]
  103. if settings.TORRENT_TRACKERS:
  104. query.extend(('tr', uri) for uri in settings.TORRENT_TRACKERS)
  105. metadata = self.torrent()
  106. if metadata and 'info_hash' in metadata:
  107. query.insert(0, ('xt', "urn:btih:%s" % metadata['info_hash']))
  108. return "magnet:?%s" % '&'.join(['%s=%s' % (k, v) for k, v in query])
  109. def info_html(self):
  110. return mark_safe(parse_markdown(self.info))
  111. def torrent(self):
  112. try:
  113. data = b64decode(self.torrent_data.encode('utf-8'))
  114. except TypeError:
  115. return None
  116. if not data:
  117. return None
  118. data = bdecode(data)
  119. # transform the data into a template-friendly dict
  120. info = data.get('info', {})
  121. metadata = {
  122. 'comment': data.get('comment', None),
  123. 'created_by': data.get('created by', None),
  124. 'creation_date': None,
  125. 'announce': data.get('announce', None),
  126. 'file_name': info.get('name', None),
  127. 'file_length': info.get('length', None),
  128. 'piece_count': len(info.get('pieces', '')) / 20,
  129. 'piece_length': info.get('piece length', None),
  130. 'url_list': data.get('url-list', []),
  131. 'info_hash': None,
  132. }
  133. if 'creation date' in data:
  134. created= datetime.utcfromtimestamp(data['creation date'])
  135. metadata['creation_date'] = created.replace(tzinfo=utc)
  136. if info:
  137. metadata['info_hash'] = hashlib.sha1(bencode(info)).hexdigest()
  138. return metadata
  139. for model in (Iso, Test, Release):
  140. pre_save.connect(set_created_field, sender=model,
  141. dispatch_uid="releng.models")
  142. # vim: set ts=4 sw=4 et: