Skip to content

Commit

Permalink
Merge branch 'release-1.1b22'
Browse files Browse the repository at this point in the history
  • Loading branch information
bbengfort committed Jul 8, 2016
2 parents 256a4e6 + 93745ec commit ebd553f
Show file tree
Hide file tree
Showing 50 changed files with 1,686 additions and 283 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,19 @@ The release versions that are sent to the Python package index (PyPI) are also t

The versioning uses a three part version system, "a.b.c" - "a" represents a major release that may not be backwards compatible. "b" is incremented on minor releases that may contain extra features, but are backwards compatible. "c" releases are bug fixes or other micro changes that developers should feel free to immediately update to.

### Version 1.1 Beta 2

* **tag**: [v1.1b2](https://github.com/DistrictDataLabs/minimum-entropy/releases/tag/v1.1b2)
* **deployment**: Friday, July 8, 2016
* **commit**: [see tag](#)

The second beta release fixes a couple of bugs with the older profile system, and a lingering topic item from Kyudo. This release goes a bit further and creates a tagging system for questions (replacing topics) and allows for the ordering and search of questions in a more meaningful way. Hopefully this is the last official Beta version and Minimum Entropy can actually start to be used in a more meaningful way.

### Version 1.0 Beta 1

* **tag**: [v1.0b1](https://github.com/DistrictDataLabs/minimum-entropy/releases/tag/v1.0b1)
* **deployment**: Tuesday, July 5, 2016
* **commit**: [see tag](#)
* **commit**: [256a4e6](https://github.com/DistrictDataLabs/minimum-entropy/commit/256a4e6eb45d49b0e1927a3bcd201848f474b5c0)

This beta release for Version 1.0 simply moves the code over from Kyudo and modifies it to remove the research components and only present a question and answer system. Things are not perfect since the app was designed for a different research project. However, the core functionality - asking questions and answering them with Markdown, as well as up and down voting exists. This is a good start to beta to our faculty to see what they think!

Expand Down
10 changes: 9 additions & 1 deletion docs/about.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,18 @@ The release versions that are sent to the Python package index (PyPI) are also t

The versioning uses a three part version system, "a.b.c" - "a" represents a major release that may not be backwards compatible. "b" is incremented on minor releases that may contain extra features, but are backwards compatible. "c" releases are bug fixes or other micro changes that developers should feel free to immediately update to.

### Version 1.1 Beta 2

* **tag**: [v1.1b2](https://github.com/DistrictDataLabs/minimum-entropy/releases/tag/v1.1b2)
* **deployment**: Friday, July 8, 2016
* **commit**: [see tag](#)

The second beta release fixes a couple of bugs with the older profile system, and a lingering topic item from Kyudo. This release goes a bit further and creates a tagging system for questions (replacing topics) and allows for the ordering and search of questions in a more meaningful way. Hopefully this is the last official Beta version and Minimum Entropy can actually start to be used in a more meaningful way.

### Version 1.0 Beta 1

* **tag**: [v1.0b1](https://github.com/DistrictDataLabs/minimum-entropy/releases/tag/v1.0b1)
* **deployment**: Tuesday, July 5, 2016
* **commit**: [see tag](#)
* **commit**: [256a4e6](https://github.com/DistrictDataLabs/minimum-entropy/commit/256a4e6eb45d49b0e1927a3bcd201848f474b5c0)

This beta release for Version 1.0 simply moves the code over from Kyudo and modifies it to remove the research components and only present a question and answer system. Things are not perfect since the app was designed for a different research project. However, the core functionality - asking questions and answering them with Markdown, as well as up and down voting exists. This is a good start to beta to our faculty to see what they think!
5 changes: 5 additions & 0 deletions fugato/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@

"""
The fugato app is designed to collect questions.
I'm not sure why this app is called Fugato, which is an Italian term for a
section of classical music. The term originated with Kyudo, but was never
exposed or properly explained. Other apps also carry classical music terms,
hopefully with explainations about why they're named the way they are.
"""

##########################################################################
Expand Down
74 changes: 73 additions & 1 deletion fugato/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,49 @@
##########################################################################

from django.db import models
from minent.utils import signature
from minent.utils import signature, normalize_query


##########################################################################
## Tag QuerySet
##########################################################################

class QuestionQuerySet(models.query.QuerySet):
"""
Adds special methods to a query set of question objects.
"""

def unanswered(self):
"""
Returns any question that is unanswered
"""
return self.count_answers().filter(num_answers=0)

def count_votes(self):
"""
Returns questions annotated with the number of votes they have.
"""
return self.annotate(num_votes=models.Count('votes'))

def count_answers(self):
"""
Returns questions annotated with the number of answers they have.
"""
return self.annotate(num_answers=models.Count('answers'))

def search(self, terms):
"""
Produces an icontains lookup on the question title.
"""
query = None # Query to search for every search term

# Build the query with Q and icontains
for term in normalize_query(terms):
q = models.Q(text__icontains=term) | models.Q(details__icontains=term)
query = q if query is None else query & q

return self.filter(query)


##########################################################################
## Questions Manager
Expand All @@ -43,3 +85,33 @@ def dedupe(self, raise_for_exceptions=False, **kwargs):
return query.first(), False

return self.create(**kwargs), True

def unanswered(self):
"""
Returns any question that is unanswered
"""
return self.get_queryset().unanswered()

def count_votes(self):
"""
Returns questions annotated with the number of votes they have.
"""
return self.get_queryset().count_votes()

def count_answers(self):
"""
Returns questions annotated with the number of answers they have.
"""
return self.get_queryset().count_answers()

def search(self, terms):
"""
Produces an icontains lookup on the question title.
"""
return self.get_queryset().search(terms)

def get_queryset(self):
"""
Return a QuestionQuerySet instead of the standard queryset.
"""
return QuestionQuerySet(self.model, using=self._db)
26 changes: 26 additions & 0 deletions fugato/migrations/0002_auto_20160707_1705.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-07-07 21:05
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('tagging', '0001_initial'),
('fugato', '0001_initial'),
]

operations = [
migrations.AddField(
model_name='question',
name='tags',
field=models.ManyToManyField(related_name='questions', to='tagging.Tag'),
),
migrations.AlterField(
model_name='question',
name='related',
field=models.ManyToManyField(blank=True, related_name='_question_related_+', to='fugato.Question'),
),
]
23 changes: 23 additions & 0 deletions fugato/migrations/0003_auto_20160707_2054.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-07-08 00:54
from __future__ import unicode_literals

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('fugato', '0002_auto_20160707_1705'),
]

operations = [
migrations.AlterModelOptions(
name='answer',
options={},
),
migrations.AlterOrderWithRespectTo(
name='answer',
order_with_respect_to='question',
),
]
27 changes: 26 additions & 1 deletion fugato/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
from django.core.urlresolvers import reverse
from django.contrib.contenttypes.fields import GenericRelation

from operator import itemgetter

##########################################################################
## Qustion and Answer Models
##########################################################################
Expand All @@ -42,6 +44,7 @@ class Question(TimeStampedModel):
related = models.ManyToManyField( 'self', editable=True, blank=True ) # Links between related questions
author = models.ForeignKey( 'auth.User', related_name='questions' ) # The author of the question
votes = GenericRelation( Vote, related_query_name='questions' ) # Vote on whether or not the question is relevant
tags = models.ManyToManyField('tagging.Tag', related_name='questions') # Tag each question with terms for easy lookup

## Set custom manager on Question
objects = QuestionManager()
Expand All @@ -58,6 +61,28 @@ def get_api_detail_url(self):
"""
return reverse('api:question-detail', args=(self.pk,))

def has_tag(self, tag):
"""
Returns True if the tag (a string) is in the list of tags.
"""
return tag in [tag.text for tag in self.tags.all()]

def set_answer_order_by_votes(self):
"""
A helper function that calls the `set_answer_order` function and
passes in the order based on the sum of the votes.
"""
# Construct the aggregation query
query = self.answers.values('id')
query = query.annotate(votes=models.Sum('votes__vote'))

order = [
a['id'] for a in sorted(query, key=itemgetter('votes'), reverse=True)
]

self.set_answer_order(order)


class Meta:
db_table = "questions"
get_latest_by = 'created'
Expand All @@ -80,7 +105,7 @@ class Answer(TimeStampedModel):

class Meta:
db_table = "answers"
get_latest_by = 'created'
order_with_respect_to = 'question'

def get_api_detail_url(self):
"""
Expand Down
8 changes: 6 additions & 2 deletions fugato/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@ class QuestionSerializer(serializers.HyperlinkedModelSerializer):
read_only=True,
)

page_url = serializers.SerializerMethodField()
page_url = serializers.SerializerMethodField(read_only=True)
tags = serializers.StringRelatedField(many=True, read_only=True)

class Meta:
model = Question
fields = ('url', 'text', 'author', 'page_url', 'details', 'details_rendered')
fields = (
'url', 'text', 'author', 'page_url',
'details', 'details_rendered', 'tags'
)
extra_kwargs = {
'url': {'view_name': 'api:question-detail',},
'details_rendered': {'read_only': True},
Expand Down
18 changes: 18 additions & 0 deletions fugato/templatetags/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# fugato.templatetags
# Template tags for fugato (though also generic usage as well).
#
# Author: Benjamin Bengfort <[email protected]>
# Created: Fri Jul 08 12:37:23 2016 -0400
#
# Copyright (C) 2016 District Data Labs
# For license information, see LICENSE.txt
#
# ID: __init__.py [] [email protected] $

"""
Template tags for fugato (though also generic usage as well).
"""

##########################################################################
## Imports
##########################################################################
75 changes: 75 additions & 0 deletions fugato/templatetags/paginator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# fugato.templatetags.paginator
# Provides a paginator tag context for digg-style pagination.
#
# Author: Benjamin Bengfort <[email protected]>
# Created: Fri Jul 08 12:38:38 2016 -0400
#
# Copyright (C) 2016 District Data Labs
# For license information, see LICENSE.txt
#
# ID: paginator.py [] [email protected] $

"""
Provides a paginator tag context for digg-style pagination.
Based on: http://www.djangosnippets.org/snippets/73/
Modified by Sean Reifschneider to be smarter about surrounding page
link context. For usage documentation see:
http://www.tummy.com/Community/Articles/django-pagination/
"""

##########################################################################
## Imports
##########################################################################

from django import template

##########################################################################
## Module Constants
##########################################################################

register = template.Library()


##########################################################################
## Inclusion Tags
##########################################################################

@register.inclusion_tag('components/pagination.html', takes_context=True)
def paginator(context, adjacent_pages=2):
"""
To be used in conjunction with the object_list generic view.
Adds pagination context variables for use in displaying first, adjacent and
last page links in addition to those created by the object_list generic
view.
"""

# Collect the pagination objects from the context
page_obj = context['page_obj']
paginator = context['paginator']

# Determine the start page for the paginator
startPage = max(page_obj.number - adjacent_pages, 1)
if startPage <= 3: startPage = 1

# Determine the end page for the paginatio
endPage = page_obj.number + adjacent_pages + 1
if endPage >= paginator.num_pages - 1: endPage = paginator.num_pages + 1

# Create a list of page numbers to iterate over on the front end.
page_numbers = [
idx for idx in range(startPage, endPage)
if idx > 0 and idx <= paginator.num_pages
]

# Return a new context with the computed pagination ranges.
return {
'page_obj': page_obj,
'paginator': paginator,
'page_numbers': page_numbers,
'show_first': 1 not in page_numbers,
'show_last': paginator.num_pages not in page_numbers,
}
Loading

0 comments on commit ebd553f

Please sign in to comment.