Commits

Tobias McNulty committed 259a710 Merge

merging latest default into stable

  • Participants
  • Parent commits 2fa44a2, 6d12e70
  • Branches stable

Comments (0)

Files changed (1187)

 syntax: glob
 *.pyc
+*.egg*

File timepiece/admin.py

 
 class ActivityAdmin(admin.ModelAdmin):
     model = timepiece.Activity
-    list_display = ('code', 'name')
+    list_display = ('code', 'name', 'billable')
+    list_filter = ('billable',)
 admin.site.register(timepiece.Activity, ActivityAdmin)
 
 
+class RelationshipTypeAdmin(admin.ModelAdmin):
+    pass
+admin.site.register(timepiece.RelationshipType, RelationshipTypeAdmin)
+
+
+class BusinessAdmin(admin.ModelAdmin):
+    pass
+admin.site.register(timepiece.Business, BusinessAdmin)
+
+
 class EntryAdmin(admin.ModelAdmin):
     model = timepiece.Entry
     list_display = ('user',
                     'project',
                     'location',
+                    'project_type',
                     'activity',
                     'start_time',
                     'end_time',
                     'hours',
                     'is_closed',
                     'is_paused',
-                    'billable')
-    list_filter = ['user', 'project']
+                    )
+    list_filter = ['activity', 'project__type', 'user', 'project']
     search_fields = ['user', 'project', 'activity', 'comments']
     date_hierarchy = 'start_time'
     ordering = ('-start_time',)
+    
+    def project_type(self, entry):
+        return entry.project.type
 admin.site.register(timepiece.Entry, EntryAdmin)
 
 
 class AttributeAdmin(admin.ModelAdmin):
     search_fields = ('label', 'type')
-    list_display = ('label', 'type')
-    list_filter = ('type',)
+    list_display = ('label', 'type', 'enable_timetracking', 'billable')
+    list_filter = ('type', 'enable_timetracking', 'billable')
     ordering = ('type', 'sort_order',) # Django honors only first field
 admin.site.register(timepiece.Attribute, AttributeAdmin)
 
 
 class ProjectAdmin(admin.ModelAdmin):
     model = timepiece.Project
-    raw_id_fields = ('interactions', 'business')
+    raw_id_fields = ('business',)
     list_display = ('name', 'business', 'point_person', 'status', 'type',)
     list_filter = ('type', 'status')
     inlines = (ProjectContractInline,)
 
 
 class ContractAssignmentAdmin(admin.ModelAdmin):
-    list_display = ('id', 'contract', 'contact', 'start_date',
+    list_display = ('id', 'contract', 'user', 'start_date',
                     'end_date', 'min_hours_per_week', 'num_hours', 'worked',
                     'remaining')
     list_filter = ('contract',)
 
 
 class PersonScheduleAdmin(admin.ModelAdmin):
-    list_display = ('contact', 'hours_per_week', 'end_date', 'total_available',
+    list_display = ('user', 'hours_per_week', 'end_date', 'total_available',
                     'scheduled', 'unscheduled')
 
     def total_available(self, obj):
     list_display = ('id', 'name')
 admin.site.register(timepiece.Location, LocationAdmin)
 
+
+class AllocationAdmin(admin.ModelAdmin):
+    list_display = ('date','hours', 'hours_worked', 'hours_left',)
+admin.site.register(timepiece.AssignmentAllocation, AllocationAdmin)

File timepiece/context_processors.py

+from django.conf import settings
+
+from timepiece.forms import QuickSearchForm
+
+def timepiece_settings(request):
+    default_famfamfam_url = settings.STATIC_URL + 'images/icons/'
+    famfamfam_url = getattr(settings, 'FAMFAMFAM_URL', default_famfamfam_url)
+    context = {
+        'FAMFAMFAM_URL': famfamfam_url,
+    }
+    return context
+    
+def quick_search(request):
+    return {
+        'quick_search_form': QuickSearchForm(),
+    }

File timepiece/forms.py

 from django.db.models import Q
 from django.conf import settings
 
+from django.contrib.auth import models as auth_models
+from django.contrib.auth import forms as auth_forms
+from django.core.urlresolvers import reverse
+from django.utils.translation import ugettext_lazy as _
+
 from timepiece.models import Project, Entry
 from timepiece.fields import PendulumDateTimeField
 from timepiece.widgets import PendulumDateTimeWidget, SecondsToHoursWidget
 from datetime import datetime, timedelta
 
 from timepiece import models as timepiece
-from crm import models as crm
+
+from ajax_select.fields import AutoCompleteSelectMultipleField, \
+                               AutoCompleteSelectField, \
+                               AutoCompleteSelectWidget
+                               
+class CreatePersonForm(auth_forms.UserCreationForm):
+    class Meta:
+        model = auth_models.User
+        fields = (
+            "username", "first_name", "last_name", 
+            "email", "is_active", "is_staff")
+
+
+class EditPersonForm(auth_forms.UserChangeForm):
+    password_one = forms.CharField(required=False, max_length=36, label=_(u'Password'),
+                                widget=forms.PasswordInput(render_value=False))
+    password_two = forms.CharField(required=False, max_length=36, label=_(u'Repeat Password'),
+                                widget=forms.PasswordInput(render_value=False))
+    class Meta:
+        model = auth_models.User
+        fields = (
+            "username", "first_name", "last_name", 
+            "email", "is_active", "is_staff"
+        )
+                        
+    def clean(self):
+        super(EditPersonForm, self).clean()
+        password_one = self.cleaned_data.get('password_one', None)
+        password_two = self.cleaned_data.get('password_two', None)
+        if password_one and password_one != password_two:
+            raise forms.ValidationError(_('Passwords Must Match.'))
+        return self.cleaned_data
+    
+    def save(self, *args, **kwargs):
+        commit = kwargs.get('commit', True)
+        kwargs['commit'] = False
+        instance = super(EditPersonForm, self).save(*args, **kwargs)
+        password_one = self.cleaned_data.get('password_one', None)
+        if password_one:
+            instance.set_password(password_one)
+        if commit:
+            instance.save()
+        return instance
+        
+class CharAutoCompleteSelectWidget(AutoCompleteSelectWidget):
+    def value_from_datadict(self, data, files, name):
+        return data.get(name, None)
+
+
+class QuickSearchForm(forms.Form):
+    quick_search = AutoCompleteSelectField(
+        'quick_search',
+        widget=CharAutoCompleteSelectWidget('quick_search'),
+    )
+    
+    def clean_quick_search(self):
+        item = self.cleaned_data['quick_search']
+        if isinstance(item, timepiece.Project):
+            return reverse('view_project', kwargs={
+                'project_id': item.id,
+            })
+        elif isinstance(item, timepiece.Business,):
+            return reverse('view_business', kwargs={
+                'business': item.id,
+            })
+        elif isinstance(item, auth_models.User,):
+            return reverse('view_person', kwargs={
+                'person_id': item.id,
+            })
+        raise forms.ValidationError('Must be a User or Project')
+    
+    def save(self):
+        return self.cleaned_data['quick_search']
+
+
+class SearchForm(forms.Form):
+    search = forms.CharField(required=False)
+
+
+class AddUserToProjectForm(forms.Form):
+    user = AutoCompleteSelectField('user')
+    
+    def save(self):
+        return self.cleaned_data['user']
 
 
 class ClockInForm(forms.ModelForm):
     class Meta:
         model = timepiece.Entry
-        fields = ('location', 'project', 'start_time', 'billable')
+        fields = ('location', 'project', 'activity', 'start_time',)
 
     def __init__(self, *args, **kwargs):
         self.user = kwargs.pop('user')
             date_format='%m/%d/%Y',
         )
         self.fields['project'].queryset = timepiece.Project.objects.filter(
-            contacts__user=self.user,
+            users=self.user,
         ).filter(
             Q(status__enable_timetracking=True) |
             Q(type__enable_timetracking=True)
     
     class Meta:
         model = Entry
-        exclude = ('user', 'pause_time', 'site', 'hours', 'activity')
+        exclude = ('user', 'pause_time', 'site', 'hours', 'status',)
 
     def __init__(self, *args, **kwargs):
         self.user = kwargs.pop('user')
         super(AddUpdateEntryForm, self).__init__(*args, **kwargs)
         self.fields['project'].queryset = timepiece.Project.objects.filter(
-            contacts__user=self.user,
+            users=self.user,
         )
         if not self.instance.end_time:
             del self.fields['end_time']
         instance.save()
         return instance
         
-
+STATUS_CHOICES = [('','---------'),]
+STATUS_CHOICES.extend(timepiece.ENTRY_STATUS)
 
 class DateForm(forms.Form):
     from_date = forms.DateField(label="From", required=False)
     to_date = forms.DateField(label="To", required=False)
-    
+    status = forms.ChoiceField(choices=STATUS_CHOICES, widget=forms.HiddenInput(), required=False)
+    activity = forms.ModelChoiceField(
+        queryset=timepiece.Activity.objects.all(), 
+        widget=forms.HiddenInput(), required=False,
+    )
+    project = forms.ModelChoiceField(
+        queryset=timepiece.Project.objects.all(), 
+        widget=forms.HiddenInput(), required=False,
+    )
     def save(self):
         from_date = self.cleaned_data.get('from_date', '')
         to_date = self.cleaned_data.get('to_date', '')
 
 
 class ProjectionForm(DateForm):
-    contact = forms.ModelChoiceField(queryset=None)
+    user = forms.ModelChoiceField(queryset=None)
     
     def __init__(self, *args, **kwargs):
-        contacts = kwargs.pop('contacts')
+        users = kwargs.pop('users')
         super(ProjectionForm, self).__init__(*args, **kwargs)
-        self.fields['contact'].queryset = contacts
-    
+        self.fields['user'].queryset = users
 
 
+class BusinessForm(forms.ModelForm):
+    class Meta:
+        model = timepiece.Business
+        fields = ('name', 'email', 'description', 'notes',)
+
 class ProjectForm(forms.ModelForm):
     class Meta:
         model = timepiece.Project
         )
 
     def __init__(self, *args, **kwargs):
-        self.business = kwargs.pop('business')
         super(ProjectForm, self).__init__(*args, **kwargs)
 
-        if self.business:
-            self.fields.pop('business')
-        else:
-            self.fields['business'].queryset = crm.Contact.objects.filter(
-                type='business',
-                business_types__name='client',
-            )
-
     def save(self):
         instance = super(ProjectForm, self).save(commit=False)
-        if self.business:
-            instance.business = self.business
         instance.save()
         return instance
 
 class PersonTimeSheet(forms.ModelForm):
     class Meta:
         model = timepiece.PersonRepeatPeriod
-        fields = ('contact',)
+        fields = ('user',)
     
     def __init__(self, *args, **kwargs):
         super(PersonTimeSheet, self).__init__(*args, **kwargs)
-        self.fields['contact'].queryset = crm.Contact.objects.filter(
-            type='individual',
-            user__isnull=False,
-        ).order_by('sort_name')
+        self.fields['user'].queryset = auth_models.User.objects.all().order_by('last_name')

File timepiece/lookups.py

+from django.db.models import Q
+from django.core.urlresolvers import reverse
+from django.contrib.auth import models as auth_models
+
+from timepiece import models as timepiece
+
+
+class UserLookup(object):
+
+    def get_query(self,q,request):
+        """ return a query set.  you also have access to request.user if needed """
+        return auth_models.User.objects.filter(
+            Q(first_name__icontains=q) | 
+            Q(last_name__icontains=q) |
+            Q(email__icontains=q)
+        ).select_related().order_by('last_name')[:10]
+        
+    def format_item(self,user):
+        """ simple display of an object when it is displayed in the list of selected objects """
+        return unicode(user)
+
+    def format_result(self,user):
+        """ a more verbose display, used in the search results display.  may contain html and multi-lines """
+        return u"<span class='individual'>%s %s</span>" % (user.first_name, user.last_name)
+
+    def get_objects(self,ids):
+        """ given a list of ids, return the objects ordered as you would like them on the admin page.
+            this is for displaying the currently selected items (in the case of a ManyToMany field)
+        """
+        return auth_models.User.objects.filter(pk__in=ids)
+
+
+class SearchResult(object):
+    """
+    Fake search result for concatenating search queries.
+    """
+    def __init__(self, pk, type, name):
+        self.pk = "%s-%d" % (type, pk)
+        self.type = type
+        self.name = name
+        
+
+class QuickLookup(object):
+
+    def get_query(self,q,request):
+        """ 
+        return a query set (or a fake one).  you also have access to request.user if needed 
+        """
+        results = []
+        users = auth_models.User.objects.filter(
+            Q(first_name__icontains=q) | 
+            Q(last_name__icontains=q) |
+            Q(email__icontains=q)
+        ).select_related().order_by('last_name')[:10]
+        for user in users:
+            name = '%s %s' % (user.first_name, user.last_name)
+            results.append(
+                SearchResult(user.pk, 'individual', name)
+            )
+        for project in timepiece.Project.objects.filter(
+                name__icontains=q,
+            ).select_related():
+            results.append(
+                SearchResult(project.pk, 'project', project.name)
+            )
+        for business in timepiece.Business.objects.filter(
+                name__icontains=q,
+            ).select_related():
+            results.append(
+                SearchResult(business.pk, 'business', business.name)
+            )
+        results.sort(lambda a,b: cmp(a.name, b.name))
+        return results
+        
+    def format_item(self, item):
+        """ simple display of an object when it is displayed in the list of selected objects """
+        return item.name
+
+    def format_result(self, item):
+        """ a more verbose display, used in the search results display.  may contain html and multi-lines """
+        return u"<span class='%s'>%s</span>" % (item.type, item.name)
+