Skip to content

Commit 393211a

Browse files
committed
Merge branch 'release-0.1b1'
2 parents 80822db + a680d0c commit 393211a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+4180
-41
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ DJANGO_TEST_SETTINGS_MODULE = $(PROJECT).settings.$(TEST_SETTINGS)
2424
DJANGO_TEST_POSTFIX := --settings=$(DJANGO_TEST_SETTINGS_MODULE) --pythonpath=$(PYTHONPATH)
2525

2626
# Apps to test
27-
APPS := partisan
27+
APPS := partisan members
2828

2929
# Export targets not associated with files
3030
.PHONY: test showenv coverage bootstrap pip virtualenv clean virtual_env_set truncate

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,20 @@ This small web application is intended to highlight how to operationalize machin
1616

1717
The image used in this README, [Partisan Fail][partisan.jpg] by [David Colarusso](https://www.flickr.com/photos/dcolarusso/) is licensed under [CC BY-NC 2.0](https://creativecommons.org/licenses/by-nc/2.0/)
1818

19+
## Changelog
20+
21+
The release versions that are deployed to the web servers are also tagged in GitHub. You can see the tags through the GitHub web application and download the tarball of the version you'd like.
22+
23+
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.
24+
25+
### Version 0.1 Beta 1
26+
27+
* **tag**: [v0.1b1](https://github.com/DistrictDataLabs/partisan-discourse/releases/tag/v0.1b1)
28+
* **deployment**: Monday, July 18, 2016
29+
* **commit**: [see tag](#)
30+
31+
This is the first beta release of the Political Discourse application. Right now this simple web application allows users to sign in, then add links to go fetch web content to the global corpus. These links are then preprocessed using NLP foo. Users can tag the documents as Republican or Democrat, allowing us to build a political classifier.
32+
1933
<!-- References -->
2034
[travis_img]: https://travis-ci.org/DistrictDataLabs/partisan-discourse.svg
2135
[travis_href]: https://travis-ci.org/DistrictDataLabs/partisan-discourse

corpus/__init__.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# corpus
2+
# An application that allows the management of our classifier corpus.
3+
#
4+
# Author: Benjamin Bengfort <[email protected]>
5+
# Created: Sun Jul 17 19:29:55 2016 -0400
6+
#
7+
# Copyright (C) 2016 District Data Labs
8+
# For license information, see LICENSE.txt
9+
#
10+
# ID: __init__.py [] [email protected] $
11+
12+
"""
13+
An application that allows the management of our classifier corpus.
14+
"""
15+
16+
##########################################################################
17+
## Imports
18+
##########################################################################
19+
20+
21+
##########################################################################
22+
## Configuration
23+
##########################################################################
24+
25+
default_app_config = 'corpus.apps.CorpusConfig'

corpus/admin.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# corpus.admin
2+
# Register models with the Django Admin for the corpus app.
3+
#
4+
# Author: Benjamin Bengfort <[email protected]>
5+
# Created: Sun Jul 17 19:30:33 2016 -0400
6+
#
7+
# Copyright (C) 2016 District Data Labs
8+
# For license information, see LICENSE.txt
9+
#
10+
# ID: admin.py [] [email protected] $
11+
12+
"""
13+
Register models with the Django Admin for the corpus app.
14+
"""
15+
16+
##########################################################################
17+
## Imports
18+
##########################################################################
19+
20+
from django.contrib import admin
21+
from corpus.models import Document, Annotation, Label
22+
23+
##########################################################################
24+
## Register Admin
25+
##########################################################################
26+
27+
admin.site.register(Label)
28+
admin.site.register(Annotation)
29+
admin.site.register(Document)

corpus/apps.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# corpus.apps
2+
# Application definition for the corpus app.
3+
#
4+
# Author: Benjamin Bengfort <[email protected]>
5+
# Created: Sun Jul 17 19:31:28 2016 -0400
6+
#
7+
# Copyright (C) 2016 District Data Labs
8+
# For license information, see LICENSE.txt
9+
#
10+
# ID: apps.py [] [email protected] $
11+
12+
"""
13+
Application definition for the corpus app.
14+
"""
15+
16+
##########################################################################
17+
## Imports
18+
##########################################################################
19+
20+
from django.apps import AppConfig
21+
22+
23+
##########################################################################
24+
## Corpus Config
25+
##########################################################################
26+
27+
class CorpusConfig(AppConfig):
28+
29+
name = 'corpus'
30+
verbose_name = "Corpora"
31+
32+
def ready(self):
33+
import corpus.signals

corpus/bitly.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# corpus.bitly
2+
# Access the bit.ly url shortening service.
3+
#
4+
# Author: Benjamin Bengfort <[email protected]>
5+
# Created: Mon Jul 18 09:59:27 2016 -0400
6+
#
7+
# Copyright (C) 2016 District Data Labs
8+
# For license information, see LICENSE.txt
9+
#
10+
# ID: bitly.py [] [email protected] $
11+
12+
"""
13+
Access the bit.ly url shortening service.
14+
"""
15+
16+
##########################################################################
17+
## Imports
18+
##########################################################################
19+
20+
import requests
21+
22+
from django.conf import settings
23+
from urllib.parse import urljoin
24+
from corpus.exceptions import BitlyAPIError
25+
26+
##########################################################################
27+
## Shorten function
28+
##########################################################################
29+
30+
def shorten(url, token=None):
31+
"""
32+
Shortens a URL using the bit.ly API.
33+
"""
34+
35+
# Get the bit.ly access token from settings
36+
token = settings.BITLY_ACCESS_TOKEN or token
37+
if not token:
38+
raise BitlyAPIError(
39+
"Cannot call shorten URL without a bit.ly access token"
40+
)
41+
42+
# Compute and make the request to the API
43+
endpoint = urljoin(settings.BITLY_API_ADDRESS, "v3/shorten")
44+
params = {
45+
"access_token": token,
46+
"longUrl": url,
47+
}
48+
49+
# bit.ly tends not to send status code errors
50+
response = requests.get(endpoint, params=params)
51+
52+
# Parse and return the result
53+
data = response.json()
54+
if data['status_code'] != 200:
55+
raise BitlyAPIError(
56+
"Could not shorten link: {}".format(data['status_txt'])
57+
)
58+
return data['data']['url']

corpus/exceptions.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# corpus.exceptions
2+
# Custom exceptions for corpus handling.
3+
#
4+
# Author: Benjamin Bengfort <[email protected]>
5+
# Created: Mon Jul 18 09:57:26 2016 -0400
6+
#
7+
# Copyright (C) 2016 District Data Labs
8+
# For license information, see LICENSE.txt
9+
#
10+
# ID: exceptions.py [] [email protected] $
11+
12+
"""
13+
Custom exceptions for corpus handling.
14+
"""
15+
16+
##########################################################################
17+
## Corpus Exceptions
18+
##########################################################################
19+
20+
class CorpusException(Exception):
21+
"""
22+
Something went wrong in the corpus app.
23+
"""
24+
pass
25+
26+
27+
class BitlyAPIError(CorpusException):
28+
"""
29+
Something went wrong trying to shorten a url.
30+
"""
31+
pass
32+
33+
34+
class FetchError(CorpusException):
35+
"""
36+
Something went wrong trying to fetch a url using requests.
37+
"""
38+
pass

corpus/fixtures/labels.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
[
2+
{
3+
"model": "corpus.label",
4+
"pk": 1,
5+
"fields": {
6+
"created": "2016-07-18T14:56:26.706Z",
7+
"modified": "2016-07-18T14:56:26.708Z",
8+
"name": "USA Political Parties",
9+
"slug": "usa-political-parties",
10+
"parent": null
11+
}
12+
},
13+
{
14+
"model": "corpus.label",
15+
"pk": 2,
16+
"fields": {
17+
"created": "2016-07-18T14:57:33.059Z",
18+
"modified": "2016-07-18T14:57:33.062Z",
19+
"name": "Democratic",
20+
"slug": "democratic",
21+
"parent": 1
22+
}
23+
},
24+
{
25+
"model": "corpus.label",
26+
"pk": 3,
27+
"fields": {
28+
"created": "2016-07-18T14:57:59.531Z",
29+
"modified": "2016-07-18T14:57:59.534Z",
30+
"name": "Republican",
31+
"slug": "republican",
32+
"parent": 1
33+
}
34+
}
35+
]

corpus/managers.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# corpus.managers
2+
# Model managers for the corpus application.
3+
#
4+
# Author: Benjamin Bengfort <[email protected]>
5+
# Created: Mon Jul 18 23:09:19 2016 -0400
6+
#
7+
# Copyright (C) 2016 District Data Labs
8+
# For license information, see LICENSE.txt
9+
#
10+
# ID: managers.py [] [email protected] $
11+
12+
"""
13+
Model managers for the corpus application.
14+
"""
15+
16+
##########################################################################
17+
## Imports
18+
##########################################################################
19+
20+
from django.db import models
21+
22+
23+
##########################################################################
24+
## Annotation Manager
25+
##########################################################################
26+
27+
class AnnotationManager(models.Manager):
28+
29+
def republican(self):
30+
"""
31+
Filters the annotations for only republican annotations.
32+
"""
33+
return self.filter(label__slug='republican')
34+
35+
def democratic(self):
36+
"""
37+
Filters the annotations for only democratic annotations.
38+
"""
39+
return self.filter(label__slug='democratic')

corpus/migrations/0001_initial.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.9.7 on 2016-07-18 17:31
3+
from __future__ import unicode_literals
4+
5+
import autoslug.fields
6+
from django.conf import settings
7+
from django.db import migrations, models
8+
import django.db.models.deletion
9+
import django.utils.timezone
10+
import model_utils.fields
11+
import picklefield.fields
12+
13+
14+
class Migration(migrations.Migration):
15+
16+
initial = True
17+
18+
dependencies = [
19+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
20+
]
21+
22+
operations = [
23+
migrations.CreateModel(
24+
name='Annotation',
25+
fields=[
26+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
27+
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
28+
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
29+
],
30+
options={
31+
'db_table': 'annotations',
32+
'get_latest_by': 'created',
33+
},
34+
),
35+
migrations.CreateModel(
36+
name='Document',
37+
fields=[
38+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
39+
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
40+
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
41+
('title', models.CharField(blank=True, default=None, max_length=255, null=True)),
42+
('long_url', models.URLField(max_length=2000, unique=True)),
43+
('short_url', models.URLField(blank=True, default=None, max_length=30, null=True)),
44+
('raw_html', models.TextField(blank=True, default=None, null=True)),
45+
('content', picklefield.fields.PickledObjectField(blank=True, default=None, editable=False, null=True)),
46+
('signature', models.CharField(blank=True, default=None, editable=False, max_length=44, null=True)),
47+
('n_words', models.SmallIntegerField(blank=True, default=None, null=True)),
48+
('n_vocab', models.SmallIntegerField(blank=True, default=None, null=True)),
49+
('users', models.ManyToManyField(related_name='documents', through='corpus.Annotation', to=settings.AUTH_USER_MODEL)),
50+
],
51+
options={
52+
'db_table': 'documents',
53+
'get_latest_by': 'created',
54+
},
55+
),
56+
migrations.CreateModel(
57+
name='Label',
58+
fields=[
59+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
60+
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
61+
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
62+
('name', models.CharField(max_length=64, unique=True)),
63+
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='name', unique=True)),
64+
('documents', models.ManyToManyField(related_name='labels', through='corpus.Annotation', to='corpus.Document')),
65+
('parent', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='corpus.Label')),
66+
],
67+
options={
68+
'db_table': 'labels',
69+
'get_latest_by': 'created',
70+
},
71+
),
72+
migrations.AddField(
73+
model_name='annotation',
74+
name='document',
75+
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='annotations', to='corpus.Document'),
76+
),
77+
migrations.AddField(
78+
model_name='annotation',
79+
name='label',
80+
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='annotations', to='corpus.Label'),
81+
),
82+
migrations.AddField(
83+
model_name='annotation',
84+
name='user',
85+
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='annotations', to=settings.AUTH_USER_MODEL),
86+
),
87+
migrations.AlterUniqueTogether(
88+
name='document',
89+
unique_together=set([('long_url', 'short_url')]),
90+
),
91+
migrations.AlterUniqueTogether(
92+
name='annotation',
93+
unique_together=set([('document', 'user')]),
94+
),
95+
]

0 commit comments

Comments
 (0)