get app ready for heroku
This commit is contained in:
0
items/__init__.py
Normal file
0
items/__init__.py
Normal file
5
items/admin.py
Normal file
5
items/admin.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.contrib import admin
|
||||
from models import Item
|
||||
|
||||
# Register your models here.
|
||||
admin.site.register(Item)
|
||||
7
items/apps.py
Normal file
7
items/apps.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ItemsConfig(AppConfig):
|
||||
name = 'items'
|
||||
179
items/models.py
Normal file
179
items/models.py
Normal file
@@ -0,0 +1,179 @@
|
||||
from __future__ import unicode_literals
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.mail import send_mail
|
||||
from storages.backends.s3boto3 import S3Boto3Storage
|
||||
from stdimage.models import StdImageField
|
||||
from datetime import datetime, date
|
||||
|
||||
from committee.models import Committee
|
||||
|
||||
from django.db import models
|
||||
|
||||
class Item(models.Model):
|
||||
S3 = S3Boto3Storage()
|
||||
|
||||
NEW = 'N'
|
||||
PREAPPROVED = 'C'
|
||||
PROCESSED = 'P'
|
||||
REJECTED = 'R'
|
||||
|
||||
STATUS = (
|
||||
(NEW, 'New'),
|
||||
(PREAPPROVED, 'Committee Approved'),
|
||||
(PROCESSED, 'Processed'),
|
||||
(REJECTED, 'Rejected'),
|
||||
)
|
||||
|
||||
desc = models.CharField(max_length=200)
|
||||
event = models.CharField(max_length=200)
|
||||
committee = models.ForeignKey(Committee)
|
||||
details = models.TextField()
|
||||
cost = models.DecimalField(max_digits=7, decimal_places=2)
|
||||
date_purchased = models.DateField('date purchased')
|
||||
image = StdImageField(upload_to='images/%Y/%m/%d',
|
||||
variations={'thumbnail': (600, 600)}
|
||||
)
|
||||
|
||||
created_by = models.ForeignKey(User, related_name='created_by')
|
||||
approved_by = models.ManyToManyField(User, blank=True, related_name='approved_by')
|
||||
date_filed = models.DateTimeField('date filed')
|
||||
status = models.CharField(max_length=2, choices=STATUS)
|
||||
|
||||
def approved(self):
|
||||
return self.status == Item.PREAPPROVED
|
||||
|
||||
def processed(self):
|
||||
return self.status == Item.PROCESSED
|
||||
|
||||
def rejected(self):
|
||||
return self.status == Item.REJECTED
|
||||
|
||||
def new(self):
|
||||
return self.status == Item.NEW
|
||||
|
||||
def statusText(self):
|
||||
return dict(Item.STATUS)[self.status]
|
||||
|
||||
def comName(self):
|
||||
return self.committee.name
|
||||
|
||||
def __str__(self):
|
||||
return self.committee.name + ": " + self.event + " " + self.desc
|
||||
|
||||
def mail_com_chair(self):
|
||||
send_mail(
|
||||
'New Reimbursement - ' + self.event + ': ' + self.desc,
|
||||
'Hey ' + self.committee.chair.first_name + ',\n\n' + \
|
||||
'Please take a moment to review http://fincom.delt.space/items/' + \
|
||||
str(self.pk) + ' and approve or reject the request from ' + \
|
||||
self.created_by.first_name + ' ' + self.created_by.last_name + \
|
||||
'. If you have any questions, contact the treasurer.\n\n' + \
|
||||
'Have a fiscally responsible day,\n Fincom Bot',
|
||||
'fincom.bot@gmail.com',
|
||||
[self.committee.chair.email],
|
||||
html_message='Hey ' + self.committee.chair.first_name+',<br><br>' +\
|
||||
'Please take a moment to review ' + \
|
||||
'<a href="http://fincom.delt.space/items/' + \
|
||||
str(self.pk) + '">http://fincom.delt.space/items/' + str(self.pk) +\
|
||||
'</a> and approve or reject the request from ' + \
|
||||
self.created_by.first_name + ' ' + self.created_by.last_name + \
|
||||
'. If you have any questions, contact the treasurer.<br><br>' + \
|
||||
'Have a fiscally responsible day,<br><br><em>Fincom Bot</em>'
|
||||
)
|
||||
|
||||
def mail_fincom(self):
|
||||
emails = [s.email for s in User.objects.filter(groups__name='Fincom')]
|
||||
|
||||
send_mail(
|
||||
'Preapproved Reimbursement - ' + self.event + ': ' + self.desc,
|
||||
'Hey Fincom,\n\n' + \
|
||||
'Please take a moment to review http://fincom.delt.space/items/' + \
|
||||
str(self.pk) + ' and approve or reject the request from ' + \
|
||||
self.created_by.first_name + ' ' + self.created_by.last_name + \
|
||||
'.\n\nHave a fiscally responsible day,\n Fincom Bot',
|
||||
'fincom.bot@gmail.com',
|
||||
emails,
|
||||
html_message='Hey Fincom,<br><br>' + \
|
||||
'Please take a moment to review ' + \
|
||||
'<a href="http://fincom.delt.space/items/' + \
|
||||
str(self.pk) + '">http://fincom.delt.space/items/' + str(self.pk) +\
|
||||
'</a> and approve or reject the request from ' + \
|
||||
self.created_by.first_name + ' ' + self.created_by.last_name + \
|
||||
'.<br><br>' + \
|
||||
'Have a fiscally responsible day,<br><br> <em>Fincom Bot</em>'
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def parseDate(date_str):
|
||||
try:
|
||||
return datetime.strptime(date_str, "%m/%d/%y").date().isoformat()
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(date_str, "%m/%d/%Y").date().isoformat()
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(date_str, "%m-%d-%y").date().isoformat()
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(date_str, "%m-%d-%Y").date().isoformat()
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(date_str, "%m %d %y").date().isoformat()
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(date_str, "%m %d %Y").date().isoformat()
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(date_str, "%y %m %d").date().isoformat()
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(date_str, "%Y %m %d").date().isoformat()
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(date_str, "%y-%m-%d").date().isoformat()
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(date_str, "%Y-%m-%d").date().isoformat()
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(date_str, "%y/%m/%d").date().isoformat()
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(date_str, "%Y/%m/%d").date().isoformat()
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(date_str, "%d %m %y").date().isoformat()
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(date_str, "%d %m %Y").date().isoformat()
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(date_str, "%d/%m/%y").date().isoformat()
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(date_str, "%d/%m/%Y").date().isoformat()
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(date_str, "%d-%m-%y").date().isoformat()
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(date_str, "%d-%m-%Y").date().isoformat()
|
||||
except ValueError:
|
||||
return date.today().isoformat()
|
||||
3
items/tests.py
Normal file
3
items/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
12
items/urls.py
Normal file
12
items/urls.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from django.conf.urls import url
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.list, name='list'),
|
||||
url(r'^(?P<item_id>\d+)/$', views.details, name='details'),
|
||||
url(r'^(?P<item_id>\d+)/approve$', views.approve, name='approve'),
|
||||
url(r'^(?P<item_id>\d+)/reject$', views.reject, name='reject'),
|
||||
url(r'^(?P<item_id>\d+)/edit$', views.edit, name='edit'),
|
||||
url(r'^(?P<item_id>\d+)/delete$', views.delete, name='delete'),
|
||||
url(r'^new$', views.new_form, name='new_form'),
|
||||
]
|
||||
181
items/views.py
Normal file
181
items/views.py
Normal file
@@ -0,0 +1,181 @@
|
||||
from django.shortcuts import HttpResponse, HttpResponseRedirect
|
||||
from django.template import loader
|
||||
from django.utils import timezone
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.db.models import Q
|
||||
from models import Item
|
||||
from committee.models import Committee
|
||||
|
||||
def isAuthorised(request, item):
|
||||
return (request.user == item.committee.chair
|
||||
or request.user.groups.filter(name='Fincom').exists())
|
||||
|
||||
def authError():
|
||||
return HttpResponseRedirect('/items')
|
||||
|
||||
def myItems(user):
|
||||
if (user.groups.filter(name='Fincom').exists()):
|
||||
return Item.objects.order_by('-date_filed', 'desc')
|
||||
|
||||
comms = []
|
||||
for c in Committee.objects.all():
|
||||
if (c.chair == user):
|
||||
comms.append(c)
|
||||
|
||||
return Item.objects.filter(Q(created_by=user) | Q(committee__in=comms)) \
|
||||
.order_by('-date_filed', 'desc')
|
||||
|
||||
def pageItems(request, items, status):
|
||||
if status != 'M':
|
||||
p = Paginator(items.filter(status=status), 8)
|
||||
else:
|
||||
p = Paginator(items, 8)
|
||||
page = request.GET.get(status)
|
||||
|
||||
try:
|
||||
return p.page(page)
|
||||
except PageNotAnInteger:
|
||||
return p.page(1)
|
||||
except EmptyPage:
|
||||
return p.page(p.num_pages)
|
||||
|
||||
@login_required
|
||||
def list(request):
|
||||
template = loader.get_template('items/list.html')
|
||||
items = myItems(request.user)
|
||||
mine = Item.objects.filter(created_by=request.user).order_by('-date_filed')
|
||||
|
||||
|
||||
context = {
|
||||
'preapproved': pageItems(request, items, Item.PREAPPROVED),
|
||||
'processed': pageItems(request, items, Item.PROCESSED),
|
||||
'newitems': pageItems(request, items, Item.NEW),
|
||||
'rejected': pageItems(request, items, Item.REJECTED),
|
||||
'mine': pageItems(request, mine, 'M'),
|
||||
}
|
||||
|
||||
return HttpResponse(template.render(context, request))
|
||||
|
||||
@login_required
|
||||
def details(request, item_id):
|
||||
I = Item.objects.get(pk=item_id)
|
||||
if (not isAuthorised(request, I)
|
||||
and request.user != I.created_by):
|
||||
return authError()
|
||||
|
||||
template = loader.get_template('items/details.html')
|
||||
|
||||
context = {
|
||||
'I': I,
|
||||
'approve': isAuthorised(request, I),
|
||||
}
|
||||
return HttpResponse(template.render(context, request))
|
||||
|
||||
def approve(request, item_id):
|
||||
I = Item.objects.get(pk=item_id)
|
||||
|
||||
if (not isAuthorised(request, I)):
|
||||
return authError()
|
||||
|
||||
I.approved_by.add(request.user)
|
||||
if (I.committee.chair == request.user and
|
||||
I.status == Item.NEW):
|
||||
I.status = Item.PREAPPROVED
|
||||
I.mail_fincom()
|
||||
elif (request.user.groups.filter(name='Fincom').exists()):
|
||||
I.status = Item.PROCESSED
|
||||
|
||||
I.save()
|
||||
|
||||
return HttpResponseRedirect('/items')
|
||||
|
||||
def reject(request, item_id):
|
||||
I = Item.objects.get(pk=item_id)
|
||||
|
||||
if (not isAuthorised(request, I)):
|
||||
return authError()
|
||||
|
||||
I.status = Item.REJECTED
|
||||
I.save()
|
||||
|
||||
return HttpResponseRedirect('/items')
|
||||
|
||||
def delete(request, item_id):
|
||||
I = Item.objects.get(pk=item_id)
|
||||
|
||||
if (not isAuthorised(request, I)):
|
||||
return authError()
|
||||
|
||||
I.delete()
|
||||
|
||||
return HttpResponseRedirect('/items')
|
||||
|
||||
def edit(request, item_id):
|
||||
I = Item.objects.get(pk=item_id)
|
||||
|
||||
if (not isAuthorised(request, I)
|
||||
and request.user != I.created_by):
|
||||
return authError()
|
||||
|
||||
if request.method == 'POST':
|
||||
if (request.POST.get('desc', None)):
|
||||
I.desc = request.POST['desc']
|
||||
if (request.POST.get('event', None)):
|
||||
I.event = request.POST['event']
|
||||
if (request.POST.get('committee', None)):
|
||||
I.committee = Committee.objects.get(name=request.POST['committee'])
|
||||
if (request.POST.get('cost', None)):
|
||||
I.cost = request.POST['cost']
|
||||
if (request.POST.get('date', None)):
|
||||
I.date_purchased = Item.parseDate(request.POST['date']),
|
||||
if (request.POST.get('details', None)):
|
||||
I.details = request.POST['details']
|
||||
|
||||
I.save()
|
||||
|
||||
return HttpResponseRedirect('/items/' + str(item_id))
|
||||
else:
|
||||
template = loader.get_template('items/edit.html')
|
||||
|
||||
context = {
|
||||
'I': I,
|
||||
'committees': Committee.objects.order_by('name'),
|
||||
}
|
||||
return HttpResponse(template.render(context, request))
|
||||
|
||||
def new_form(request):
|
||||
if request.method == 'POST':
|
||||
if request.FILES['image'].size > 10 * (1 << 20):
|
||||
template = loader.get_template('items/new.html')
|
||||
context = {
|
||||
'committees': Committee.objects.order_by('name'),
|
||||
'error': 'Your image file is too large. Maximum size is 20MB',
|
||||
}
|
||||
return HttpResponse(template.render(context, request))
|
||||
item = Item(
|
||||
desc = request.POST['desc'],
|
||||
event = request.POST['event'],
|
||||
committee = Committee.objects.get(name=request.POST['committee']),
|
||||
cost = request.POST['cost'],
|
||||
date_purchased = Item.parseDate(request.POST['date']),
|
||||
details = request.POST['details'],
|
||||
date_filed = timezone.now(),
|
||||
created_by = request.user,
|
||||
status = Item.NEW,
|
||||
image = request.FILES['image'],
|
||||
)
|
||||
|
||||
item.save()
|
||||
|
||||
item.mail_com_chair()
|
||||
|
||||
return HttpResponseRedirect('/items')
|
||||
|
||||
else:
|
||||
template = loader.get_template('items/new.html')
|
||||
context = {
|
||||
'committees': Committee.objects.order_by('name'),
|
||||
}
|
||||
|
||||
return HttpResponse(template.render(context, request))
|
||||
Reference in New Issue
Block a user