Commit 060f8035 authored by Daniel W Bond's avatar Daniel W Bond

merge commit

parents fd34bda0 90acd97e
......@@ -14,6 +14,8 @@ class Student(TimeStampedModel):
slug = AutoSlugField(populate_from='user', unique=True)
emails_sent = models.PositiveIntegerField(default=0)
def get_absolute_url(self):
return reverse('profile', kwargs={'slug': self.slug})
......
from django.test import TestCase
from django.http import HttpRequest
from django.core.urlresolvers import resolve
from .models import Student
from .views import DetailStudent, StudentRatings
class ProfileTest(TestCase):
def test_username_resolves_to_profile(self):
found = resolve('/student/username/')
self.assertEqual(found.func, home_page)
request = HttpResponse()
reponse = profile(request)
self.assertTrue(response.content.startswith('<html>'))
self.assertIn('<title>user.get_full_name</title>', response.content)
self.assertTrue(response.content.endswith('</html>'))
# Create your tests here.
from django.contrib import admin
# Register your models here.
from django.db import models
# Create your models here.
{% extends 'layouts/base.html' %}
{% block title %}
SRCT Bookshare &bull; Mod
{% endblock title %}
{% block content %}
<div class="row">
<div class="col-md-10 col-md-offset-1 text-center">
<h1>Ratio Emails Sent to Listings Posted</h1>
</div>
</div>
<div class="row">
<div class="col-md-10 col-md-offset-1">
<table class="table table-bordered table-hover table-condensed text-center">
<thead>
<td class="col-md-2"><h4><strong>Emails Sent</strong></h4></td>
<td class="col-md-2"><h4><strong>Listings Posted</strong></h4></td>
<td class="col-md-1"><h4><strong>Ratio</strong></h4></td>
<td class="col-md-5"><h4><strong>Student</strong></h4></td>
<td class="col-md-2"><h4><strong>Action</strong></h4></td>
</thead>
<tbody>
{% for student in email_happy_students %}
<tr>
<td>{{ student.emails_sent }}</td>
<td>{{ student.num_books }}</td>
{% widthratio student.emails_sent student.num_books 1 as ratio %}
<td>{{ ratio }}</td>
<td><a href="{{ student.get_absolute_url }}" target="_blank">
<strong>{{ student.user.get_full_name }}</strong> (<em>{{ student.user.username }}</em>)
</a></td>
<td>
<a href="#">
<span class="label label-warning"><strong>Email</strong></span>
</a>
<a href="#">
<span class="label label-danger"><strong>Make Inactive</strong></span>
</a>
</td>
</tr>
{% empty %}
Nothing Here!
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock content %}
......@@ -35,8 +35,6 @@ SRCT Bookshare &bull; Mod
<span class="label label-danger"><strong>Delete</strong></span>
</a></td>
</tr>
{% empty %}
Nothing Here!
{% endfor %}
</tbody>
</table>
......
{% extends 'layouts/base.html' %}
{% block title %}
SRCT Bookshare &bull; Mod
{% endblock title %}
{% block content %}
<div class="row">
<div class="col-md-10 col-md-offset-1 text-center">
<h1>Users by number of Listings</h1>
</div>
</div>
<div class="row">
<div class="col-md-10 col-md-offset-1">
<table class="table table-bordered table-hover table-condensed text-center">
<thead>
<td class="col-md-2"><h4><strong>Number</strong></h4></td>
<td class="col-md-8"><h4><strong>Student</strong></h4></td>
<td class="col-md-2"><h4><strong>Action</strong></h4></td>
</thead>
<tbody>
{% for student in students %}
<tr>
<td>{{ student.num_books }}</td>
<td><a href="{{ student.get_absolute_url }}" target="_blank">
<strong>{{ student.user.get_full_name }}</strong> (<em>{{ student.user.username }}</em>)
</a></td>
<td>
<a href="#">
<span class="label label-warning"><strong>Email</strong></span>
</a>
<a href="#">
<span class="label label-danger"><strong>Make Inactive</strong></span>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock content %}
{% extends 'layouts/base.html' %}
{% block title %}
SRCT Bookshare &bull; Mod
{% endblock title %}
{% block content %}
<div class="row">
<div class="col-md-10 col-md-offset-1 text-center">
<h1>Moderation</h1>
</div>
</div>
<div class="row">
<div class="col-md-10 col-md-offset-1">
<legend class="text-center">Moderate by...</legend>
<div class="col-md-4">
<a href="{% url 'flag_mod' %}" class="btn btn-primary btn-lg btn-block text-center">Flags</a>
</div>
<div class="col-md-4">
<a href="{% url 'listing_nums' %}" class="btn btn-primary btn-lg btn-block text-center">Listings</a>
</div>
<div class="col-md-4">
<a href="{% url 'email_ratio' %}" class="btn btn-primary btn-lg btn-block text-center">Emails</a>
</div>
</div>
</div>
{% endblock content %}
from django.test import TestCase
# Create your tests here.
# core django imports
from django.conf.urls import patterns, url
from django.views.decorators.cache import cache_page
# imports from your apps
from .views import ModLandingView, FlagModView, ListingNumModView,\
UserEmailRatioModView
urlpatterns = patterns('',
url(r'^$', cache_page(60 * 15)(ModLandingView.as_view()), name='mod_page'),
url(r'^flags/$', FlagModView.as_view(), name='flag_mod'),
url(r'^listing-nums/$', ListingNumModView.as_view(), name='listing_nums'),
url(r'^email-ratio/$', UserEmailRatioModView.as_view(), name='email_ratio'),
)
# core django imports
from django.views.generic import TemplateView, ListView
from django.db.models import Count
# third party imports
from braces.views import LoginRequiredMixin, SuperuserRequiredMixin
# imports from your apps
from trades.models import Listing
from core.models import Student
class ModLandingView(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView):
template_name = 'mod.html'
login_url = 'login'
class FlagModView(LoginRequiredMixin, SuperuserRequiredMixin, ListView):
queryset = Listing.objects.annotate(num_flags=Count('flag')).order_by('-num_flags')[:20]
context_object_name = 'listings'
template_name = 'flag_mod.html'
login_url = 'login'
class ListingNumModView(LoginRequiredMixin, SuperuserRequiredMixin, ListView):
queryset = Student.objects.annotate(num_books=Count('listing')).order_by('-num_books')[:20]
context_object_name = 'students'
template_name = 'listing_num_mod.html'
login_url = 'login'
class UserEmailRatioModView(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView):
template_name = 'email_ratio_mod.html'
def get_context_data(self, **kwargs):
context = super(UserEmailRatioModView, self).get_context_data(**kwargs)
students_by_emails = Student.objects.order_by('-emails_sent')[:20]
students_listings = students_by_emails.annotate(num_books=Count('listing'))
context['email_happy_students'] = students_listings
return context
......@@ -37,6 +37,7 @@ INSTALLED_APPS = (
'trades',
'core',
'lookouts',
'mod',
# packages
'randomslugfield',
'django_gravatar',
......
......@@ -6,7 +6,7 @@ from django.conf import settings
from django.contrib import admin
from django.views.decorators.cache import cache_page
# imports from your apps
from .views import HomepageView, ChartsView, ModView
from .views import HomepageView, ChartsView
admin.autodiscover()
......@@ -20,6 +20,7 @@ urlpatterns = patterns('',
url(r'^share/', include('trades.urls')),
url(r'^student/', include('core.urls')),
url(r'^lookouts/', include('lookouts.urls')),
url(r'^mod/', include('mod.urls')),
# search
url(r'^search/', include('haystack.urls'), name='search'),
......@@ -30,8 +31,6 @@ urlpatterns = patterns('',
url(r'^$', HomepageView.as_view(), name='homepage'),
url(r'^charts/?$', cache_page(60 * 10)(ChartsView.as_view()), name='charts'),
url(r'^mod/?$', ModView.as_view(), name='mod'),
# static pages
url(r'^about/?$',
cache_page(60 * 15)(TemplateView.as_view(template_name='about.html')),
......
# standard library imports
from collections import Counter
# core django imports
from django.views.generic import TemplateView, ListView
from django.views.generic import TemplateView
from django.db.models import Sum, Count
# third party imports
from braces.views import LoginRequiredMixin, SuperuserRequiredMixin
from braces.views import LoginRequiredMixin
# imports from your apps
from lookouts.models import Lookout
from trades.models import Listing, Bid
......@@ -55,9 +55,3 @@ class ChartsView(LoginRequiredMixin, TemplateView):
context['total_students'] = Student.objects.count()
context['total_proceeds'] = total_proceeds
return context
class ModView(LoginRequiredMixin, SuperuserRequiredMixin, ListView):
queryset = Listing.objects.annotate(num_flags=Count('flag')).order_by('-num_flags')[:20]
context_object_name = 'listings'
template_name = 'mod.html'
login_url = 'login'
......@@ -47,8 +47,10 @@ class Listing(TimeStampedModel):
title = models.CharField(max_length=200)
author = models.CharField(max_length=200)
isbn = models.CharField(max_length=20,
validators=[RegexValidator('^[0-9xX-]{10,20}',
message='Please enter a valid ISBN.')])
validators=[RegexValidator(
regex='^[0-9xX-]{10,20}',
message='Please enter a valid ISBN.',
code='invalid_isbn')])
year = models.IntegerField(null=True, blank=True,
# some professors may assign books still to be officially published
validators=[MaxValueValidator(date.today().year+1)])
......@@ -61,8 +63,10 @@ class Listing(TimeStampedModel):
access_code = models.CharField(choices=ACCESS_CODE_CHOICES,
max_length=30, default=NOT_APPLICABLE)
course_abbr = models.CharField(max_length=10, blank=True,
validators=[RegexValidator('^([a-zA-Z]){2,4} (\d){3}$',
message='Please enter a valid course.')])
validators=[RegexValidator(
regex='^([a-zA-Z]){2,4} (\d){3}$',
message='Please enter a valid course.',
code='invalid_course_abbr')])
description = models.TextField(blank=True, max_length=2000)
price = models.PositiveIntegerField(default=0,
validators=[MaxValueValidator(1000)])
......
......@@ -403,6 +403,9 @@ class ExchangeListing(LoginRequiredMixin, FormValidMessageMixin, UpdateView):
msg.attach_alternative(html_content, "text/html")
msg.send()
self.obj.poster.emails_sent += 2
self.obj.poster.save()
return super(ExchangeListing, self).form_valid(form)
def get_context_data(self, **kwargs):
......@@ -468,6 +471,9 @@ class UnExchangeListing(LoginRequiredMixin, FormValidMessageMixin, UpdateView):
msg.attach_alternative(html_content, "text/html")
msg.send()
self.obj.poster.emails_sent += 2
self.obj.poster.save()
# this has to come after the email has been sent, otherwise these are
# cleaned out
form.instance.exchanged = False
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment