PageRenderTime 59ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

/apps/package/models.py

https://github.com/jrothenbuhler/djangopackages
Python | 258 lines | 233 code | 20 blank | 5 comment | 4 complexity | 5ddbdef34a534ebd9ddb377ae0e8158e MD5 | raw file
  1. # TODO - cleanup regex to do proper string subs
  2. # TODO - add is_other field to repo
  3. # TODO - add repo.user_url
  4. from django.conf import settings
  5. from django.contrib.auth.models import User
  6. from django.core.urlresolvers import reverse
  7. from django.db import models
  8. from django.db.models import Q
  9. from django.utils.translation import ugettext_lazy as _
  10. from github2.client import Github
  11. from package.fields import CreationDateTimeField, ModificationDateTimeField
  12. from package.handlers import github
  13. from package.pypi import fetch_releases
  14. from package.utils import uniquer
  15. from distutils.version import LooseVersion as versioner
  16. from urllib import urlopen
  17. import logging
  18. import os
  19. import re
  20. import sys
  21. class NoPyPiVersionFound(Exception):
  22. pass
  23. class BaseModel(models.Model):
  24. """ Base abstract base class to give creation and modified times """
  25. created = CreationDateTimeField(_('created'))
  26. modified = ModificationDateTimeField(_('modified'))
  27. class Meta:
  28. abstract = True
  29. class Category(BaseModel):
  30. title = models.CharField(_("Title"), max_length="50")
  31. slug = models.SlugField(_("slug"))
  32. description = models.TextField(_("description"), blank=True)
  33. title_plural = models.CharField(_("Title Plural"), max_length="50", blank=True)
  34. show_pypi = models.BooleanField(_("Show pypi stats & version"), default=True)
  35. class Meta:
  36. ordering = ['title']
  37. verbose_name_plural = 'Categories'
  38. def __unicode__(self):
  39. return self.title
  40. REPO_CHOICES = (
  41. ("package.handlers.unsupported", "Unsupported"),
  42. ("package.handlers.bitbucket", "Bitbucket"),
  43. ("package.handlers.github", "Github"),
  44. ("package.handlers.launchpad", "Launchpad")
  45. )
  46. class Repo(BaseModel):
  47. is_supported = models.BooleanField(_("Supported?"), help_text="Does Django Packages support this repo site?", default=False)
  48. title = models.CharField(_("Title"), max_length="50")
  49. description = models.TextField(_("description"), blank=True)
  50. url = models.URLField(_("base URL of repo"))
  51. is_other = models.BooleanField(_("Is Other?"), default=False, help_text="Only one can be set this way")
  52. user_regex = models.CharField(_("User Regex"), help_text="Regex to calculate user's name or id",max_length="100", blank=True)
  53. repo_regex = models.CharField(_("Repo Regex"), help_text="Regex to get repo's name", max_length="100", blank=True)
  54. slug_regex = models.CharField(_("Slug Regex"), help_text="Regex to get repo's slug", max_length="100", blank=True)
  55. handler = models.CharField(_("Handler"),
  56. help_text="Warning: Don't change this unless you know what you are doing!",
  57. choices=REPO_CHOICES,
  58. max_length="200",
  59. default="package.handlers.unsupported")
  60. def packages_for_profile(self, profile):
  61. """Return a list of all packages contributed to by a profile."""
  62. repo_url = profile.url_for_repo(self)
  63. if repo_url:
  64. regex = r'^{0},|,{0},|{0}$'.format(repo_url)
  65. query = Q(participants__regex=regex) & Q(repo=self)
  66. return list(Package.objects.filter(query))
  67. else:
  68. return []
  69. class Meta:
  70. ordering = ['-is_supported', 'title']
  71. def __unicode__(self):
  72. if not self.is_supported:
  73. return '%s (unsupported)' % self.title
  74. return self.title
  75. downloads_re = re.compile(r'<td style="text-align: right;">[0-9]{1,}</td>')
  76. doap_re = re.compile(r"/pypi\?\:action=doap\&amp;name=[a-zA-Z0-9\.\-\_]+\&amp;version=[a-zA-Z0-9\.\-\_]+")
  77. version_re = re.compile(r'<revision>[a-zA-Z0-9\.\-\_]+</revision>')
  78. repo_url_help_text = "Enter your project repo hosting URL here.<br />Example: https://bitbucket.com/ubernostrum/django-registration"
  79. pypi_url_help_text = "<strong>Leave this blank if this package does not have a PyPI release.</strong><br />What PyPI uses to index your package. <br />Example: django-registration"
  80. category_help_text = """
  81. <ul>
  82. <li><strong>Apps</strong> is anything that is installed by placing in settings.INSTALLED_APPS.</li>
  83. <li><strong>Frameworks</strong> are large efforts that combine many python modules or apps to build things like Pinax.</li>
  84. <li><strong>Other</strong> are not installed by settings.INSTALLED_APPS, are not frameworks or sites but still help Django in some way.</li>
  85. <li><strong>Projects</strong> are individual projects such as Django Packages, DjangoProject.com, and others.</li>
  86. </ul>
  87. """
  88. class Package(BaseModel):
  89. title = models.CharField(_("Title"), max_length="100")
  90. slug = models.SlugField(_("Slug"), help_text="Slugs will be lowercased", unique=True)
  91. category = models.ForeignKey(Category, verbose_name="Installation", help_text=category_help_text)
  92. repo = models.ForeignKey(Repo, null=True)
  93. repo_description= models.TextField(_("Repo Description"), blank=True)
  94. repo_url = models.URLField(_("repo URL"), help_text=repo_url_help_text, blank=True,unique=True)
  95. repo_watchers = models.IntegerField(_("repo watchers"), default=0)
  96. repo_forks = models.IntegerField(_("repo forks"), default=0)
  97. repo_commits = models.IntegerField(_("repo commits"), default=0)
  98. pypi_url = models.URLField(_("PyPI slug"), help_text=pypi_url_help_text, blank=True, default='')
  99. #pypi_version = models.CharField(_("Current Pypi version"), max_length="20", blank=True)
  100. pypi_downloads = models.IntegerField(_("Pypi downloads"), default=0)
  101. related_packages = models.ManyToManyField("self", blank=True)
  102. participants = models.TextField(_("Participants"),
  103. help_text="List of collaborats/participants on the project", blank=True)
  104. usage = models.ManyToManyField(User, blank=True)
  105. created_by = models.ForeignKey(User, blank=True, null=True, related_name="creator")
  106. last_modified_by = models.ForeignKey(User, blank=True, null=True, related_name="modifier")
  107. @property
  108. def pypi_version(self):
  109. string_ver_list = self.version_set.values_list('number', flat=True)
  110. if string_ver_list:
  111. vers_list = [versioner(v) for v in string_ver_list]
  112. latest = sorted(vers_list)[-1]
  113. return str(latest)
  114. return ''
  115. @property
  116. def pypi_name(self):
  117. """ return the pypi name of a package"""
  118. if not self.pypi_url.strip():
  119. return ""
  120. name = self.pypi_url.replace("http://pypi.python.org/pypi/","")
  121. if "/" in name:
  122. return name[:name.index("/")]
  123. return name
  124. @property
  125. def last_updated(self):
  126. last_commit = self.commit_set.latest('commit_date')
  127. if last_commit:
  128. return last_commit.commit_date
  129. return None
  130. def active_examples(self):
  131. return self.packageexample_set.filter(active=True)
  132. def grids(self):
  133. return (x.grid for x in self.gridpackage_set.all())
  134. def repo_name(self):
  135. return self.repo_url.replace(self.repo.url + '/','')
  136. def participant_list(self):
  137. return self.participants.split(',')
  138. def commits_over_52(self):
  139. from package.templatetags.package_tags import commits_over_52
  140. return commits_over_52(self)
  141. def fetch_metadata(self, *args, **kwargs):
  142. # Get the downloads from pypi
  143. if self.pypi_url.strip() and self.pypi_url != "http://pypi.python.org/pypi/":
  144. total_downloads = 0
  145. for release in fetch_releases(self.pypi_name):
  146. version, created = Version.objects.get_or_create(
  147. package = self,
  148. number = release.version
  149. )
  150. # add to total downloads
  151. total_downloads += release.downloads
  152. # add to versions
  153. version.downloads = release.downloads
  154. version.license = release.license
  155. version.hidden = release._pypi_hidden
  156. version.save()
  157. self.pypi_downloads = total_downloads
  158. # Get the repo watchers number
  159. base_handler = __import__(self.repo.handler)
  160. handler = sys.modules[self.repo.handler]
  161. self = handler.pull(self)
  162. self.save()
  163. class Meta:
  164. ordering = ['title']
  165. def __unicode__(self):
  166. return self.title
  167. @models.permalink
  168. def get_absolute_url(self):
  169. return ("package", [self.slug])
  170. class PackageExample(BaseModel):
  171. package = models.ForeignKey(Package)
  172. title = models.CharField(_("Title"), max_length="100")
  173. url = models.URLField(_("URL"))
  174. active = models.BooleanField(_("Active"), default=True, help_text="Moderators have to approve links before they are provided")
  175. class Meta:
  176. ordering = ['title']
  177. def __unicode__(self):
  178. return self.title
  179. class Commit(BaseModel):
  180. package = models.ForeignKey(Package)
  181. commit_date = models.DateTimeField(_("Commit Date"))
  182. class Meta:
  183. ordering = ['-commit_date']
  184. def __unicode__(self):
  185. return "Commit for '%s' on %s" % (self.package.title, unicode(self.commit_date))
  186. class Version(BaseModel):
  187. package = models.ForeignKey(Package, blank=True, null=True)
  188. number = models.CharField(_("Version"), max_length="100", default="", blank="")
  189. downloads = models.IntegerField(_("downloads"), default=0)
  190. license = models.CharField(_("license"), max_length="100")
  191. hidden = models.BooleanField(_("hidden"), default=False)
  192. class Meta:
  193. get_latest_by = 'created'
  194. ordering = ['-created']
  195. def __unicode__(self):
  196. return "%s: %s" % (self.package.title, self.number)