source: asadb/groups/models.py @ 8439bf6

space-accessstablestagetest-hooks
Last change on this file since 8439bf6 was 3275ab1, checked in by Alex Dehnert <adehnert@…>, 14 years ago

Add a manager for active groups

  • Property mode set to 100644
File size: 11.9 KB
Line 
1from django.db import models
2from django.contrib.auth.models import User
3
4import datetime
5
6import settings
7
8# Create your models here.
9
10class ActiveGroupManager(models.Manager):
11    def get_query_set(self, ):
12        return super(ActiveGroupManager, self).get_query_set().filter(
13            group_status__slug='active',
14        )
15
16class Group(models.Model):
17    name = models.CharField(max_length=100)
18    abbreviation = models.CharField(max_length=10, blank=True)
19    description = models.TextField()
20    activity_category = models.ForeignKey('ActivityCategory', null=True, blank=True, )
21    group_class = models.ForeignKey('GroupClass')
22    group_status = models.ForeignKey('GroupStatus')
23    group_funding = models.ForeignKey('GroupFunding', null=True, blank=True, )
24    website_url = models.URLField()
25    constitution_url = models.CharField(max_length=200, blank=True)
26    meeting_times = models.TextField(blank=True)
27    advisor_name = models.CharField(max_length=100, blank=True)
28    num_undergrads = models.IntegerField(null=True, blank=True, )
29    num_grads = models.IntegerField(null=True, blank=True, )
30    num_community = models.IntegerField(null=True, blank=True, )
31    num_other = models.IntegerField(null=True, blank=True, )
32    group_email = models.EmailField(blank=True, )
33    officer_email = models.EmailField()
34    main_account_id = models.IntegerField(null=True, blank=True, )
35    funding_account_id = models.IntegerField(null=True, blank=True, )
36    athena_locker = models.CharField(max_length=20, blank=True)
37    recognition_date = models.DateField()
38    update_date = models.DateTimeField(editable=False, )
39    updater = models.CharField(max_length=30, editable=False, null=True, ) # match Django username field
40    _updater_set = False
41
42    objects = models.Manager()
43    active_groups = ActiveGroupManager()
44
45    def update_string(self, ):
46        updater = self.updater or "unknown"
47        return "%s by %s" % (self.update_date.strftime(settings.DATETIME_FORMAT_PYTHON), updater, )
48
49    def set_updater(self, who):
50        if hasattr(who, 'username'):
51            self.updater = who.username
52        else:
53            self.updater = who
54        self._updater_set = True
55
56    def save(self, ):
57        if not self._updater_set:
58            self.updater = None
59        self.update_date = datetime.datetime.now()
60        super(Group, self).save()
61
62    def viewable_notes(self, user):
63        return GroupNote.viewable_notes(self, user)
64
65    def officers(self, role=None, person=None, as_of="now",):
66        """Get the set of people holding some office.
67
68        If None is passed for role, person, or as_of, that field will not
69        be constrained. If as_of is "now" (default) the status will be
70        required to be current. If any of the three parameters are set
71        to another value, the corresponding filter will be applied.
72        """
73        office_holders = OfficeHolder.objects.filter(group=self,)
74        if role:
75            if isinstance(role, str):
76                office_holders = office_holders.filter(role__slug=role)
77            else:
78                office_holders = office_holders.filter(role=role)
79        if person:
80            office_holders = office_holders.filter(person=person)
81        if as_of:
82            if as_of == "now": as_of = datetime.datetime.now()
83            office_holders = office_holders.filter(start_time__lte=as_of, end_time__gte=as_of)
84        return office_holders
85
86    def __str__(self, ):
87        return self.name
88
89    class Meta:
90        ordering = ('name', )
91        permissions = (
92            ('view_group_private_info', 'View private group information'),
93            # ability to update normal group info or people
94            # this is weaker than change_group, which is the built-in
95            # permission that controls the admin interface
96            ('admin_group', 'Administer basic group information'),
97            ('view_signatories', 'View signatory information for all groups'),
98        )
99
100
101class GroupNote(models.Model):
102    author = models.CharField(max_length=30, ) # match Django username field
103    timestamp = models.DateTimeField(default=datetime.datetime.now, editable=False, )
104    body = models.TextField()
105    acl_read_group = models.BooleanField(default=True, help_text='Can the group read this note')
106    acl_read_offices = models.BooleanField(default=True, help_text='Can "offices" that interact with groups (SAO, CAC, and funding boards) read this note')
107    group = models.ForeignKey(Group)
108
109    def __str__(self, ):
110        return "Note by %s on %s" % (self.author, self.timestamp, )
111
112    @classmethod
113    def viewable_notes(cls, group, user):
114        notes = cls.objects.filter(group=group)
115        if not user.has_perm('groups.view_note_all'):
116            q = models.Q(pk=0)
117            if user.has_perm('groups.view_note_group', group):
118                q |= models.Q(acl_read_group=True)
119            if user.has_perm('groups.view_note_office'):
120                q |= models.Q(acl_read_offices=True)
121            notes = notes.filter(q)
122        return notes
123
124    class Meta:
125        permissions = (
126            ('view_note_group',     'View notes intended for the group to see', ),
127            ('view_note_office',    'View notes intended for "offices" to see', ),
128            ('view_note_all',       'View all notes', ),
129        )
130
131
132class OfficerRole(models.Model):
133    UNLIMITED = 10000
134
135    display_name = models.CharField(max_length=50)
136    slug = models.SlugField()
137    description = models.TextField()
138    max_count = models.IntegerField(default=UNLIMITED, help_text='Maximum number of holders of this role. Use %d for no limit.' % UNLIMITED)
139    require_student = models.BooleanField(default=False)
140    grant_user = models.ForeignKey(User, null=True, blank=True,
141        limit_choices_to={ 'username__endswith': '@SYSTEM'})
142    publicly_visible = models.BooleanField(default=True, help_text='Can everyone see the holders of this office.')
143
144    def max_count_str(self, ):
145        if self.max_count == self.UNLIMITED:
146            return "unlimited"
147        else:
148            return str(self.max_count)
149
150    def __str__(self, ):
151        return self.display_name
152
153    @classmethod
154    def getGrantUsers(cls, roles):
155        ret = set([role.grant_user for role in roles])
156        if None in ret: ret.remove(None)
157        return ret
158
159    @classmethod
160    def retrieve(cls, slug, ):
161        return cls.objects.get(slug=slug)
162
163
164class OfficeHolder_CurrentManager(models.Manager):
165    def get_query_set(self, ):
166        return super(OfficeHolder_CurrentManager, self).get_query_set().filter(
167            start_time__lte=datetime.datetime.now,
168            end_time__gte=datetime.datetime.now,
169        )
170
171class OfficeHolder(models.Model):
172    EXPIRE_OFFSET   = datetime.timedelta(seconds=1)
173    END_NEVER       = datetime.datetime.max
174
175    person = models.CharField(max_length=30)
176    role = models.ForeignKey('OfficerRole')
177    group = models.ForeignKey('Group')
178    start_time = models.DateTimeField(default=datetime.datetime.now)
179    end_time = models.DateTimeField(default=datetime.datetime.max)
180
181    objects = models.Manager()
182    current_holders = OfficeHolder_CurrentManager()
183
184    def expire(self, ):
185        self.end_time = datetime.datetime.now()-self.EXPIRE_OFFSET
186        self.save()
187
188    def __str__(self, ):
189        return "<OfficeHolder: person=%s, role=%s, group=%s, start_time=%s, end_time=%s>" % (
190            self.person, self.role, self.group, self.start_time, self.end_time, )
191
192    def __repr__(self, ):
193        return str(self)
194
195
196class PerGroupAuthz:
197    supports_anonymous_user = True
198    supports_inactive_user = True
199    supports_object_permissions = True
200
201    def authenticate(self, username=None, password=None, ):
202        return None # we don't do authn
203    def get_user(user_id, ):
204        return None # we don't do authn
205
206    def has_perm(self, user_obj, perm, obj=None, ):
207        print "Checking user %s for perm %s on obj %s" % (user_obj, perm, obj)
208        if not user_obj.is_active:
209            return False
210        if not user_obj.is_authenticated():
211            return False
212        if obj is None:
213            return False
214        # Great, we're active, authenticated, and not in a recursive call
215        # Check that we've got a reasonable object
216        if getattr(user_obj, 'is_system', False):
217            return False
218        if isinstance(obj, Group):
219            # Now we can do the real work
220            holders = obj.officers(person=user_obj.username).select_related('role__grant_user')
221            sys_users = OfficerRole.getGrantUsers([holder.role for holder in holders])
222            for sys_user in sys_users:
223                sys_user.is_system = True
224                if sys_user.has_perm(perm):
225                    print "While checking user %s for perm %s on obj %s: implicit user %s has perms" % (user_obj, perm, obj, sys_user, )
226                    return True
227        print "While checking user %s for perm %s on obj %s: no perms found (implicit: %s)" % (user_obj, perm, obj, sys_users)
228        return False
229
230
231
232class ActivityCategory(models.Model):
233    name = models.CharField(max_length=50)
234
235    def __str__(self, ):
236        return self.name
237
238    class Meta:
239        verbose_name_plural = "activity categories"
240
241
242class GroupClass(models.Model):
243    name = models.CharField(max_length=50)
244    slug = models.SlugField(unique=True, )
245    description = models.TextField()
246    gets_publicity = models.BooleanField(help_text="Gets publicity resources such as FYSM or Activities Midway")
247
248    def __str__(self, ):
249        return self.name
250
251    class Meta:
252        verbose_name_plural = "group classes"
253
254
255class GroupStatus(models.Model):
256    name = models.CharField(max_length=50)
257    slug = models.SlugField(unique=True, )
258    description = models.TextField()
259    is_active = models.BooleanField(default=True, help_text="This status represents an active group")
260
261    def __str__(self, ):
262        active = ""
263        if not self.is_active:
264            active = " (inactive)"
265        return "%s%s" % (self.name, active, )
266
267    class Meta:
268        verbose_name_plural= "group statuses"
269
270
271class GroupFunding(models.Model):
272    name = models.CharField(max_length=50)
273    slug = models.SlugField(unique=True, )
274    contact_email = models.EmailField()
275    funding_list = models.CharField(max_length=32, blank=True, help_text="List that groups receiving funding emails should be on. The database will attempt to make sure that ONLY those groups are on it.")
276
277    def __str__(self, ):
278        return "%s (%s)" % (self.name, self.contact_email, )
279
280
281class AthenaMoiraAccount_ActiveManager(models.Manager):
282    def get_query_set(self, ):
283        return super(AthenaMoiraAccount_ActiveManager, self).get_query_set().filter(del_date=None)
284
285class AthenaMoiraAccount(models.Model):
286    username = models.CharField(max_length=8)
287    mit_id = models.CharField(max_length=15)
288    first_name      = models.CharField(max_length=45)
289    last_name       = models.CharField(max_length=45)
290    account_class   = models.CharField(max_length=10)
291    mutable         = models.BooleanField(default=True)
292    add_date        = models.DateField(help_text="Date when this person was added to the dump.", )
293    del_date        = models.DateField(help_text="Date when this person was removed from the dump.", blank=True, null=True, )
294    mod_date        = models.DateField(help_text="Date when this person's record was last changed.", blank=True, null=True, )
295
296    objects = models.Manager()
297    active_accounts = AthenaMoiraAccount_ActiveManager()
298
299    def is_student(self, ):
300        # XXX: Is this... right?
301        return self.account_class == 'G' or self.account_class.isdigit()
302
303    def __str__(self, ):
304        if self.mutable:
305            mutable_str = ""
306        else:
307            mutable_str = " (immutable)"
308        return "<AthenaMoiraAccount: username=%s name='%s, %s' account_class=%s%s>" % (
309            self.username, self.last_name, self.first_name,
310            self.account_class, mutable_str,
311        )
312
313    def __repr__(self, ):
314        return str(self)
315
316    class Meta:
317        verbose_name = "Athena (Moira) account"
Note: See TracBrowser for help on using the repository browser.