1 from django
.db
import models
2 from django
.contrib
.admin
.models
import User
3 from django
import newforms
as forms
# This will change to forms in 0.98 or 1.0
4 from django
.core
.mail
import send_mass_mail
5 from django
.db
.models
import signals
, permalink
6 from django
.dispatch
import dispatcher
8 from django
.template
.loader
import render_to_string
10 from datetime
import datetime
13 class Category(models
.Model
):
14 name
= models
.CharField(max_length
=20)
16 def __unicode__(self
):
23 verbose_name_plural
= 'categories'
26 class Architecture(models
.Model
):
27 name
= models
.CharField(max_length
=10)
29 def __unicode__(self
):
36 class Repository(models
.Model
):
37 name
= models
.CharField(max_length
=20)
39 def __unicode__(self
):
46 verbose_name_plural
= 'repositories'
49 class License(models
.Model
):
50 name
= models
.CharField(max_length
=24)
52 def __unicode__(self
):
59 class Group(models
.Model
):
60 name
= models
.CharField(max_length
=10)
62 def __unicode__(self
):
69 class Package(models
.Model
):
70 name
= models
.CharField(primary_key
=True, max_length
=30)
71 version
= models
.CharField(max_length
=20)
72 release
= models
.SmallIntegerField()
73 description
= models
.CharField(max_length
=180)
74 url
= models
.CharField(max_length
=200, null
=True, blank
=True)
75 maintainers
= models
.ManyToManyField(User
)
76 repository
= models
.ForeignKey(Repository
)
77 category
= models
.ForeignKey(Category
)
78 tarball
= models
.FileField(upload_to
='packages')
79 licenses
= models
.ManyToManyField(License
, null
=True, blank
=True)
80 architectures
= models
.ManyToManyField(Architecture
)
81 depends
= models
.ManyToManyField('self', null
=True, blank
=True,
82 related_name
="reverse_depends", symmetrical
=False)
83 make_depends
= models
.ManyToManyField('self', null
=True, blank
=True,
84 related_name
="reverse_make_depends", symmetrical
=False)
85 provides
= models
.ManyToManyField('self', null
=True, blank
=True,
86 related_name
="reverse_provides", symmetrical
=False)
87 replaces
= models
.ManyToManyField('self', null
=True, blank
=True,
88 related_name
="reverse_replaces", symmetrical
=False)
89 conflicts
= models
.ManyToManyField('self', null
=True, blank
=True,
90 related_name
="reverse_conflicts", symmetrical
=False)
91 deleted
= models
.BooleanField(default
=False)
92 outdated
= models
.BooleanField(default
=False)
93 added
= models
.DateTimeField(editable
=False, default
=datetime
.now())
94 updated
= models
.DateTimeField()
95 groups
= models
.ManyToManyField(Group
, null
=True, blank
=True)
97 def __unicode__(self
):
98 return "%s %s" % (self
.name
, self
.version
)
101 return ', '.join(map(str, self
.architectures
.all()))
102 get_arch
.short_description
= 'architectures'
104 def get_tarball_basename(self
):
105 """Return the basename of the absolute path to the tarball"""
106 return os
.path
.basename(self
.get_tarball_filename())
108 def get_absolute_url(self
):
109 return ('aur-package_detail', [self
.name
,])
110 get_absolute_url
= permalink(get_absolute_url
)
113 self
.updated
= datetime
.now()
114 super(Package
, self
).save()
116 def _save_FIELD_file(self
, field
, filename
, raw_contents
, save
=True):
117 old_upload_to
=field
.upload_to
118 dirname
, filename
= filename
.rsplit(os
.path
.sep
, 1)
119 field
.upload_to
= os
.path
.join(field
.upload_to
, dirname
)
120 super(Package
, self
)._save
_FIELD
_file
(field
, filename
,
122 field
.upload_to
= old_upload_to
125 list_display
= ('name', 'category', 'get_arch', 'updated')
128 ordering
= ('-updated',)
129 get_latest_by
= 'updated'
132 class PackageFile(models
.Model
):
133 package
= models
.ForeignKey(Package
)
134 # filename for local sources and url for external
135 filename
= models
.FileField(upload_to
='packages', null
=True, blank
=True)
136 url
= models
.URLField(null
=True, blank
=True)
138 def get_absolute_url(self
):
140 return self
.get_filename_url()
144 def get_filename(self
):
146 return os
.path
.basename(self
.get_filename_filename())
150 def _save_FIELD_file(self
, field
, filename
, raw_contents
, save
=True):
151 old_upload_to
=field
.upload_to
152 dirname
, filename
= filename
.rsplit(os
.path
.sep
, 1)
153 field
.upload_to
= os
.path
.join(field
.upload_to
, dirname
)
154 super(PackageFile
, self
)._save
_FIELD
_file
(field
, filename
,
156 field
.upload_to
= old_upload_to
158 def __unicode__(self
):
164 class PackageHash(models
.Model
):
165 # sha512 hashes are 128 characters
166 hash = models
.CharField(max_length
=128, primary_key
=True)
167 type = models
.CharField(max_length
=12)
168 file = models
.ForeignKey(PackageFile
)
170 def __unicode__(self
):
174 verbose_name_plural
= 'package hashes'
180 class Comment(models
.Model
):
181 package
= models
.ForeignKey(Package
)
182 parent
= models
.ForeignKey('self', null
=True, blank
=True)
183 user
= models
.ForeignKey(User
)
184 message
= models
.TextField()
185 added
= models
.DateTimeField(editable
=False, default
=datetime
.now())
186 ip
= models
.IPAddressField()
187 hidden
= models
.BooleanField(default
=False)
188 commit
= models
.BooleanField(default
=False)
190 def __unicode__(self
):
197 class PackageNotification(models
.Model
):
198 user
= models
.ForeignKey(User
)
199 package
= models
.ForeignKey(Package
)
201 def __unicode__(self
):
202 return "%s subscription to %s updates" % (self
.user
.username
, self
.package
.name
)
207 class PackageSearchForm(forms
.Form
):
208 # Borrowed from AUR2-BR
209 def __init__(self
, *args
, **kwargs
):
210 super(PackageSearchForm
, self
).__init
__(*args
, **kwargs
)
211 category_choices
= [('all', 'All')]
212 category_choices
+= [(category
.name
.lower(), category
.name
) for category
in Category
.objects
.all()]
213 repository_choices
= [('all', 'All')]
214 repository_choices
+= [(repository
.name
.lower(), repository
.name
) for repository
in Repository
.objects
.all()]
215 self
.fields
['category'].choices
= category_choices
216 self
.fields
['repository'].choices
= repository_choices
218 repository
= forms
.ChoiceField(initial
='all', choices
=())
219 category
= forms
.ChoiceField(initial
='all', choices
=())
220 query
= forms
.CharField(max_length
=30, label
="Keywords", required
=False)
221 searchby
= forms
.ChoiceField(initial
='name', label
="Search By",choices
=(
222 ('name', 'Package Name'),
223 ('maintainer', 'Maintainer'),
225 lastupdate
= forms
.DateTimeField(label
="Last Update", required
=False)
226 sortby
= forms
.ChoiceField(initial
='name', label
="Sort By", choices
=(
227 ('name', 'Package Name'),
228 ('category', 'Category'),
229 ('repository', 'Repository'),
230 ('updated', 'Last Updated'),
232 order
= forms
.ChoiceField(initial
='asc', choices
=(
233 ('asc', 'Ascending'),
234 ('desc', 'Descending'),
236 limit
= forms
.ChoiceField(initial
='25', choices
=(
244 def get_or_default(self
, key
):
245 if not self
.is_bound
:
246 return self
.fields
[key
].initial
247 return self
.cleaned_data
.get(key
, self
.fields
[key
].initial
)
250 if self
.is_bound
and not self
.is_valid():
252 repository
= self
.get_or_default('repository')
253 lastupdate
= self
.get_or_default('lastupdate')
254 category
= self
.get_or_default('category')
255 sortby
= self
.get_or_default('sortby')
256 order
= self
.get_or_default('order')
258 # Find the packages by searching description and package name or maintainer
259 if self
.get_or_default('query'):
260 if self
.get_or_default('searchby') == 'maintainer':
261 results
= Package
.objects
.filter(maintainers__username__icontains
=self
.cleaned_data
["query"])
263 res1
= Package
.objects
.filter(name__icontains
=self
.cleaned_data
["query"])
264 res2
= Package
.objects
.filter(description__icontains
=self
.cleaned_data
["query"])
265 results
= res1 | res2
267 results
= Package
.objects
.all()
269 if repository
!= 'all':
270 results
= results
.filter(repository__name__iexact
=repository
)
271 if category
!= 'all':
272 results
= results
.filter(category__name__exact
=category
)
274 results
= results
.filter(updated__gte
=lastupdate
)
275 # Change the sort order if necessary
277 results
= results
.order_by('-' + sortby
, 'repository', 'category', 'name')
279 results
= results
.order_by(sortby
, 'repository', 'category', 'name')
282 class PackageSubmitForm(forms
.Form
):
283 # Borrowed from AUR2-BR
284 def __init__(self
, *args
, **kwargs
):
285 super(PackageSubmitForm
, self
).__init
__(*args
, **kwargs
)
286 category_choices
= [(category
.name
.lower(), category
.name
) for category
in Category
.objects
.all()]
287 self
.fields
['category'].choices
= category_choices
289 category
= forms
.ChoiceField(choices
=())
290 file = forms
.FileField(label
="PKGBUILD")
291 comment
= forms
.CharField(widget
=forms
.Textarea
, label
="Commit Message")
293 # Should this be here?
294 def email_package_updates(sender
, instance
, signal
, *args
, **kwargs
):
295 """Send notification to users of modification to a Package"""
296 subject
= "Archlinux AUR: %s updated" % instance
.name
299 notifications
= PackageNotification
.objects
.filter(package
=instance
)
300 for notification
in notifications
:
301 message
= render_to_string('aur/email_notification.txt', {
303 'user': notification
.user
,
305 mail_list
.append((subject
, message
, sender
,
306 (notification
.user
.email
,)))
307 return send_mass_mail(mail_list
)
309 # Send notifications of updates to users on saves and deltion of packages
310 dispatcher
.connect(email_package_updates
, signal
=signals
.post_save
,
312 dispatcher
.connect(email_package_updates
, signal
=signals
.post_delete
,