Commit c543969d authored by Daniel W Bond's avatar Daniel W Bond

Merge branch 'master' into 86-CrispyRedux

parents a18a9a3b b2685444
# Archival Warning
WARNING: This project is abandoned. As such, we cannot garauntee functionality or the security of the project. Please contact srct@gmu.edu to either take up this project or for questions regarding it.
# SRCT Bookshare
SRCT Bookshare is a platform for GMU students to buy and sell their textbooks.
......
# standard library imports
from __future__ import absolute_import, print_function, unicode_literals
# third party imports
from rest_framework import serializers
# imports from your apps
from trades.models import Listing
from trades.models import Bid, Listing
class BidSerializer(serializers.ModelSerializer):
num_bid_flags = serializers.SerializerMethodField()
def get_num_bid_flags(self, bid):
return bid.bidflag_set.all().count()
class Meta:
model = Bid
fields = ('price', 'text', 'num_bid_flags')
class ListingSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name = 'detail_listing',
lookup_field = 'slug'
)
active = serializers.SerializerMethodField()
final_price = serializers.SerializerMethodField()
old = serializers.SerializerMethodField()
bids = BidSerializer(many=True, read_only=True)
num_bids = serializers.SerializerMethodField()
num_flags = serializers.SerializerMethodField()
def get_active(self, listing):
return listing.active()
def get_final_price(self, listing):
return listing.final_price()
def get_old(self, listing):
return listing.old()
def get_num_bids(self, listing):
return listing.bid_set.all().count()
def get_num_flags(self, listing):
return listing.flag_set.all().count()
class Meta:
model = Listing
fields = ('url', 'slug',
'title', 'author', 'isbn', 'year', 'edition', 'condition',
'access_code', 'course_abbr', 'description', 'price', 'photo',
'num_bids', 'bids', 'num_flags', 'active', 'old',
'exchanged', 'cancelled', 'date_closed', 'final_price')
# standard libary imports
from __future__ import absolute_import, print_function, unicode_literals
# core django imports
from django.conf.urls import patterns, include, url
from django.conf.urls import include, url
# third party imports
from rest_framework.routers import DefaultRouter
# imports from your apps
from .views import ListingViewSet
# API v1
urlpatterns = patterns('',
)
router = DefaultRouter()
router.register(r'listings', ListingViewSet, 'listing_list')
urlpatterns = router.urls
from django.shortcuts import render
# standard library imports
from __future__ import absolute_import, print_function
# third party imports
from rest_framework.pagination import PageNumberPagination
from rest_framework.viewsets import ReadOnlyModelViewSet
# imports from your apps
from trades.models import Listing
from .serializers import ListingSerializer
# Create your views here.
# trades apis
class ListingPagination(PageNumberPagination):
page_size = 100 # number of results per page
page_size_query_param = 'page_size'
max_page_size = 1000 # so you can retrieve a maximum of 100,000 listings
class ListingViewSet(ReadOnlyModelViewSet):
queryset = Listing.objects.all().order_by('-created')
serializer_class = ListingSerializer
pagination_class = ListingPagination
lookup_field = 'slug'
......@@ -4,9 +4,9 @@
{% else %}
<tr>
{% endif %}
<td class="text-center col-md-1"><h5>{{ bid.created|date:"m/d/y" }}</h5></td>
<td class="text-center col-md-8"><h5><a href="{{ bid.listing.get_absolute_url }}">{{ bid.listing.isbn|isbn_name|title }}<a/></h5></td>
<td class="text-center col-md-1"><h5>
<td class="text-center col-md-2"><h4>{{ bid.created|date:"m/d/y" }}</h4></td>
<td class="text-center col-md-7"><h4><a href="{{ bid.listing.get_absolute_url }}">{{ bid.listing.isbn|isbn_name|title }}<a/></h4></td>
<td class="text-center col-md-1"><h4>
{% if bid.listing.exchanged %}
<span class="label label-primary"><strong>Exchanged</strong></span>
{% elif bid.listing.cancelled %}
......@@ -16,13 +16,13 @@
{% else %}
<span class="label label-info"><strong>Active</strong></span>
{% endif %}
</h5></td>
<td class="text-center col-md-1"><h5>{{ bid.listing.course_abbr }}</h5></td>
<td class="text-center col-md-1"><h5>
</h4></td>
<td class="text-center col-md-1"><h4>{{ bid.listing.course_abbr }}</h4></td>
<td class="text-center col-md-1"><h4>
${{ bid.price }}
{% if bid == bid.listing.winning_bid %}
<br /><small><span class="label label-success"><strong>Winning Bid</strong></span></small>
{% else %}
{% endif %}
</h5></td>
</h4></td>
</tr>
......@@ -4,17 +4,17 @@
{% else %}
<tr>
{% endif %}
<td class="text-center col-md-2"><h5>{{ listing.isbn }}</h5></td>
<td class="text-center col-md-7"><h5><a href="{{ listing.get_absolute_url }}">{{ listing.title|title }}</h5></a></td>
<td class="text-center col-md-1"><h5>
<td class="text-center col-md-2"><h4>{{ listing.isbn }}</h4></td>
<td class="text-center col-md-7"><h4><a href="{{ listing.get_absolute_url }}">{{ listing.title|title }}</h4></a></td>
<td class="text-center col-md-1"><h4>
{% if listing.exchanged %}
${{ listing.final_price }}
{% else %}
${{ listing.price }}
{% endif %}
</h5></td>
<td class="text-center col-md-1"><h5>{{ listing.bids|length }}</h5></td>
<td class="text-center col-md-1"><h5>
</h4></td>
<td class="text-center col-md-1"><h4>{{ listing.bids|length }}</h4></td>
<td class="text-center col-md-1"><h4>
{% if listing.exchanged %}
<span class="label label-primary"><strong>Exchanged</strong></span>
{% if student.user == request.user %}
......@@ -33,5 +33,5 @@
<small>(<a href="{% url 'cancel_listing' listing.slug %}">Cancel</a>)</small>
{% endif %}
{% endif %}
</h5></td>
</h4></td>
</tr>
......@@ -19,7 +19,7 @@
<div class="col-md-6">
<h2><strong>{{ student.get_full_name_or_uname }}</strong>
{% if student.user == request.user %}
<small><a href="{% url 'name_change' %}">(Edit)</a></small>
<small><a href="{% url 'name_change' %}"><i class="fa fa-pencil"></i></a></small>
{% endif %}
</h2>
</div>
......@@ -139,7 +139,7 @@
<table class="table table-bordered table-hover table-condensed text-center">
<thead>
<!-- the br's is a really shitty way of doing this -->
<td><h4><strong>Date<br />Bid</strong></h4></td>
<td><h4><strong><br />Date Bid</strong></h4></td>
<td><h4><strong><br />Title</strong></h4></td>
<td><h4><strong>Listing Status</strong></h4></td>
<td><h4><strong><br />Course</strong></h4></td>
......@@ -213,9 +213,9 @@
<tbody>
{% for lookout in lookouts %}
<tr>
<td class="text-center"><h5>{{ lookout.isbn }}</h5></td>
<td class="text-center"><h5><a href="{{ lookout.get_absolute_url }}">{{ lookout.isbn|isbn_name|title }}<a/></h5></td>
<td class="text-center"><h5><a href="{% url 'delete_lookout' lookout.slug %}"><span class="label label-danger">Delete</span></a></h5></td>
<td class="text-center"><h4>{{ lookout.isbn }}</h4></td>
<td class="text-center"><h4><a href="{{ lookout.get_absolute_url }}">{{ lookout.isbn|isbn_name|title }}<a/></h4></td>
<td class="text-center"><h4><a href="{% url 'delete_lookout' lookout.slug %}"><span class="label label-danger">Delete</span></a></h4></td>
{% endfor %}
</tbody>
</table>
......
......@@ -11,6 +11,7 @@ SRCT Bookshare &bull; {{ student.get_full_name_or_uname }}
{% load trades_extras %}
<div class="page-header" id="banner">
<legend>
<div class="row">
<div class="col-md-1">
<img class="img-circle img-responsive" src="{% gravatar_url request.user.email 75 %}">
......@@ -28,6 +29,7 @@ SRCT Bookshare &bull; {{ student.get_full_name_or_uname }}
</h3>
</div>
</div>
</legend>
</div>
<div class="row" style="padding-top: 15px;">
......
......@@ -5,20 +5,34 @@ SRCT Bookshare &bull; Edit Name
{% endblock title %}
{% block content %}
{% load gravatar %}
<div class="page-header" id="banner">
<div class="row">
<div class="col-lg-12 text-center">
<h1><strong>SRCT</strong>&#8203;BOOKSHARE</h1>
<p class="lead text-center"><strong>Edit Your Name</strong></p>
</div>
<div class="row">
<div class="col-lg-12 text-center">
<h1><strong>SRCT</strong>&#8203;BOOKSHARE</h1>
<p class="lead text-center"><strong>Edit Your Name</strong></p>
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-offset-2">
<img class="img-circle img-responsive" style="margin-left:auto;margin-right:auto;padding:10px"; src="{% gravatar_url request.user.email 150 %}">
</div>
<div class="col-md-4">
<hr />
<p>
If you have a <a href="https://en.gravatar.com/">Gravatar profile</a> associated
with your <a href="https://masonlive.gmu.edu/">Masonlive email</a> address, we will
display that profile picture.
</p>
<hr />
</div>
</div>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<div class="row">
......
......@@ -10,12 +10,10 @@ SRCT Bookshare &bull; Create Lookout
{% include 'messages.html' %}
<div class="page-header" id="banner">
<div class="row">
<div class="col-lg-12 text-center">
<h1><strong>SRCT</strong>&#8203;BOOKSHARE</h1>
<p class="lead text-center"><strong>Create Lookout</strong></p>
</div>
<div class="row">
<div class="col-lg-12 text-center">
<h1><strong>SRCT</strong>&#8203;BOOKSHARE</h1>
<p class="lead text-center"><strong>Create Lookout</strong></p>
</div>
</div>
......
......@@ -8,15 +8,14 @@ SRCT Bookshare &bull; Lookouts &bull; {{ lookout.isbn|isbn_name|title }}
{% block content %}
<div class="page-header" id="banner">
<div class="row">
<div class="col-sm-12 text-center">
<h1><strong>SRCT</strong>&#8203;BOOKSHARE</h1>
<p class="lead"><strong>Your lookout for <em>{{ lookout.isbn|isbn_name|title }}</em>.</strong></p>
<a href="{% url 'delete_lookout' lookout.slug %}"><button type="button" class="btn btn-danger btn-xs">Delete this Lookout</button></a>
</div>
<div class="row">
<div class="col-sm-12 text-center">
<h1><strong>SRCT</strong>&#8203;BOOKSHARE</h1>
<p class="lead"><strong>Your lookout for <em>{{ lookout.isbn|isbn_name|title }}</em>.</strong></p>
<a href="{% url 'delete_lookout' lookout.slug %}"><button type="button" class="btn btn-danger btn-xs">Delete this Lookout</button></a>
</div>
</div>
<hr />
<div class="row">
{% for listing in lookout.get_listings %}
......
......@@ -73,11 +73,21 @@ INSTALLED_APPS = (
'haystack',
'piwik',
'randomslugfield',
'rest_framework',
'storages',
)
CRISPY_TEMPLATE_PACK = 'bootstrap3'
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
}
MESSAGE_TAGS = {messages.ERROR: 'danger', }
MIDDLEWARE_CLASSES = (
......@@ -128,11 +138,13 @@ AUTHENTICATION_BACKENDS = (
'cas.backends.CASBackend',
)
CAS_SERVER_URL = 'https://login.gmu.edu'
CAS_SERVER_URL = 'https://login.gmu.edu/'
CAS_LOGOUT_COMPLETELY = True
CAS_PROVIDE_URL_TO_LOGOUT = True
#LOGIN_URL = '/login/'
#LOGOUT_URL = '/logout/'
# These fields are commented out because the views are
# specified directly in settings.urls
#LOGIN_URL = '/login'
#LOGOUT_URL = '/logout'
CAS_RESPONSE_CALLBACKS = (
'core.cas_callbacks.create_user',
......@@ -259,3 +271,31 @@ if not DEBUG:
# or CRITICAL level to the AdminEmailHandler via mail_admins
},
}
else:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse'
},
},
'handlers': {
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler'
},
'console':{
'level': 'DEBUG',
'class': 'logging.StreamHandler'
},
},
'loggers': {
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': True,
},
},
}
......@@ -6,6 +6,7 @@ from django.conf import settings
from django.contrib import admin
from django.views.decorators.cache import cache_page
from django.contrib.auth.decorators import login_required
from django.views.generic import RedirectView
# third party imports
from haystack.views import SearchView
from cas.views import login, logout
......@@ -53,5 +54,11 @@ urlpatterns = [
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)),
# api
url(r'^api/v1/', include('api.urls')),
# establishing versioning already so we easily can move to another API release
# without specific version redirects to latest version
url(r'^api/$', RedirectView.as_view(url="v1/")),
# location of user-uploaded media files from settings.py (for local)
] #+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
......@@ -17,7 +17,7 @@ SRCT Bookshare &bull; Charts
<div class="page-header" id="banner">
<div class="row">
<div class="col-lg-12 text-center">
<h1><strong>SRCT</strong>BOOKSHARE</h1>
<h1><strong>SRCT</strong>&#8203;BOOKSHARE</h1>
<p class="lead"><strong>Listing Charts</strong></p>
</div>
</div>
......
......@@ -6,6 +6,8 @@ SRCT Bookshare &bull; Homepage
{% block content %}
{% if not user.is_authenticated %}
<div class="container">
<div class="row">
<div class="col-md-12 license">
......@@ -17,14 +19,39 @@ SRCT Bookshare &bull; Homepage
</div>
</div>
</div>
{% if user.is_authenticated %}
<hr />
<div class="row">
<div class="col-sm-3 text-center">
<h3><i class="fa fa-key fa-lg"></i></h3>
<h4>Log in with your Mason credentials</h4>
</div>
<div class="col-sm-3 text-center">
<h3><i class="fa fa-gift fa-lg"></i></h3>
<h4>Exchange your used textbooks with Mason students</h4>
</div>
<div class="col-sm-3 text-center">
<h3><i class="fa fa-book fa-lg"></i></h3>
<h4>Search for textbooks by course and more</h4>
</div>
<div class="col-sm-3 text-center">
<h3><i class="fa fa-eye fa-lg"></i></h3>
<h4>Get updates when textbooks you want are posted</h4>
</div>
</div>
{% else %}
{% load trades_extras %}
<h1>Welcome, {{ request.user.student.get_first_name_or_uname }}!</h1>
<legend class="text-center">
<i class="fa fa-eye"></i> <strong>Your Lookouts</strong>
<small>(<a href="{% url 'create_lookout' %}">Create</a>)</small>
</legend>
{% if lookouts %}
{% for lookout in lookouts %}
......@@ -48,19 +75,14 @@ SRCT Bookshare &bull; Homepage
{% else %}
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-warning">
<div class="panel-body text-center">
<div class="row">
<div class="col-md-10 col-md-offset-1">
<h3>No lookout results.</h3>
<p>
You don't have any lookouts yet. Lookouts are automatic
searches for books you have an eye out for. You can
<a href="{% url 'create_lookout' %}">create some</a>, and
we'll immediately let you know when a listing you want
goes up.
</p>
<p>Lookouts are automatic searches for books you designate.</p>
<p><a href="{% url 'create_lookout' %}">Save an ISBN</a>,
and we'll immediately let you know when a book you want is listed.</p>
</div>
</div>
</div>
......@@ -68,28 +90,9 @@ SRCT Bookshare &bull; Homepage
</div>
</div>
{% endif %}
{% else %}
<hr />
<div class="row">
<div class="col-sm-3 text-center">
<h3><i class="fa fa-key fa-lg"></i></h3>
<h4>Log in with your Mason credentials</h4>
</div>
<div class="col-sm-3 text-center">
<h3><i class="fa fa-gift fa-lg"></i></h3>
<h4>Exchange your used textbooks with Mason students</h4>
</div>
<div class="col-sm-3 text-center">
<h3><i class="fa fa-book fa-lg"></i></h3>
<h4>Search for textbooks by course and more</h4>
</div>
<div class="col-sm-3 text-center">
<h3><i class="fa fa-eye fa-lg"></i></h3>
<h4>Get updates when textbooks you want are posted</h4>
</div>
</div>
{% endif %}
<hr />
<div class="row">
......
......@@ -95,7 +95,7 @@ class Listing(TimeStampedModel):
modified_plus_month = self.modified.date() + relativedelta(months=1)
# last login + two weeks
last_login_plus_two_weeks = self.poster.last_login.date() +\
last_login_plus_two_weeks = self.poster.user.last_login.date() +\
relativedelta(weeks=2)
last_poked = ((today > created_plus_month) or (today > modified_plus_month))
......
......@@ -6,12 +6,10 @@ SRCT Bookshare &bull; {{ bid.listing.title }} &bull; Edit Bid
{% block content %}
<div class="page-header" id="banner">
<div class="row">
<div class="col-lg-12 text-center">
<h1><strong>SRCT</strong>&#8203;BOOKSHARE</h1>
<p class="lead text-center"><strong>Edit Your Bid for {{ bid.listing.poster.user.get_full_name }}'s Listing <em>{{ bid.listing.title }}</em></strong></p>
</div>
<div class="row">
<div class="col-lg-12 text-center">
<h1><strong>SRCT</strong>&#8203;BOOKSHARE</h1>
<p class="lead text-center"><strong>Edit Your Bid for {{ bid.listing.poster.user.get_full_name }}'s Listing <em>{{ bid.listing.title }}</em></strong></p>
</div>
</div>
......
......@@ -6,12 +6,10 @@ SRCT Bookshare &bull; {{ bid.listing.title }} &bull; {{ bid.bidder.user.first_na
{% block content %}
<div class="page-header" id="banner">
<div class="row">
<div class="col-lg-12 text-center">
<h1><strong>SRCT</strong>&#8203;BOOKSHARE</h1>
<p class="lead text-center"><strong>Flag {{ bid.bidder.user.get_full_name }}'s Bid for ${{ bid.price }} on {{ bid.listing.poster.user.get_full_name }}'s Listing <em>{{ bid.listing.title }}</em></strong></p>
</div>
<div class="row">
<div class="col-lg-12 text-center">
<h1><strong>SRCT</strong>&#8203;BOOKSHARE</h1>
<p class="lead text-center"><strong>Flag {{ bid.bidder.user.get_full_name }}'s Bid for ${{ bid.price }} on {{ bid.listing.poster.user.get_full_name }}'s Listing <em>{{ bid.listing.title }}</em></strong></p>
</div>
</div>
......
......@@ -6,12 +6,10 @@ SRCT Bookshare &bull; {{ listing.title }} | Flag
{% block content %}
<div class="page-header" id="banner">
<div class="row">
<div class="col-lg-12 text-center">
<h1><strong>SRCT</strong>&#8203;BOOKSHARE</h1>
<p class="lead text-center"><strong>Flag {{ listing.poster.user.get_full_name }}'s Listing <em>{{ listing.title }}</em></strong></p>
</div>
<div class="row">
<div class="col-lg-12 text-center">
<h1><strong>SRCT</strong>&#8203;BOOKSHARE</h1>
<p class="lead text-center"><strong>Flag {{ listing.poster.user.get_full_name }}'s Listing <em>{{ listing.title }}</em></strong></p>
</div>
</div>
......
......@@ -6,12 +6,10 @@ SRCT Bookshare &bull; Create Listing
{% block content %}
<div class="page-header" id="banner">
<div class="row">
<div class="col-lg-12 text-center">
<h1><strong>SRCT</strong>&#8203;BOOKSHARE</h1>
<p class="lead text-center"><strong>Create Listing</strong></p>
</div>
<div class="row">
<div class="col-lg-12 text-center">
<h1><strong>SRCT</strong>&#8203;BOOKSHARE</h1>
<p class="lead text-center"><strong>Create Listing</strong></p>
</div>
</div>
......
......@@ -6,12 +6,10 @@ SRCT Bookshare &bull; {{ listing.title }} | Rate
{% block content %}
<div class="page-header" id="banner">
<div class="row">
<div class="col-lg-12 text-center">
<h1><strong>SRCT</strong>&#8203;BOOKSHARE</h1>
<p class="lead text-center"><strong>Rate Your Experience with {{ listing.poster.user.get_full_name}}'s Listing <em>{{ listing.title }}</em></strong></p>
</div>
<div class="row">
<div class="col-lg-12 text-center">
<h1><strong>SRCT</strong>&#8203;BOOKSHARE</h1>
<p class="lead text-center"><strong>Rate Your Experience with {{ listing.poster.user.get_full_name}}'s Listing <em>{{ listing.title }}</em></strong></p>
</div>
</div>
......
......@@ -6,31 +6,25 @@ SRCT Bookshare &bull; Latest Listings
{% block content %}
<div class="page-header" id="banner">
<div class="row">
<div class="col-lg-12">