Commit 06f82ae8 authored by Colin Scott-Fleming's avatar Colin Scott-Fleming
Browse files

Merge branch 'release/0.1.0a'

parents 9496c469 c331e82e
*.pyc
*.sqlite
import.txt
issues.csv
# Behavior Modeler for LIvES
**Be[e]**havior **Mo**deler is a project designed to use [NLTK](http://nltk.org) and [Orange](http://orange.biolab.si) to extract and examine trends in participant and coach behavior within the [LIvES Project](http://ovarianlives.org).
**Be[e]havior Modeler** (Beemo) is an AngularJS project designed to apply a [_k_-means clustering](http://en.wikipedia.org/wiki/K-means_clustering) analysis to visualize trends in participant and coach behavior within the [LIvES Project](http://ovarianlives.org). Beemo utilizes a [Django REST Framework](http://django-rest-framework.org) backend and [d3.js](http://d3js.org) to visualize the data via [cmaurer's nvd3 angular directives](https://github.com/cmaurer/angularjs-nvd3-directives).
## Dependencies
Beemo relies heavily on several frameworks and package managers, but the system dependencies are as follows:
- [Python 2.7.5](https://www.python.org/download/releases/2.7.5/)
- [NodeJS](http://nodejs.org/)
### Installing Python Dependencies with [pip](http://pip.readthedocs.org/):
```shell
~ $ pip install -r requirements.txt
```
### Installing JavaScript Dependencies:
```shell
# _
# __ ___ _ __ (_)_ _ __ _ ___ ___ ___ _ _
# / _/ _ \ ' \| | ' \/ _` | (_-</ _ \/ _ \ ' \ _ _ _
# \__\___/_|_|_|_|_||_\__, | /__/\___/\___/_||_(_|_|_)
# |___/
```
## Setup
Coming soon...
beemo
=====
#### backend
* good for now - may need some tweaks to JSON output fields/formatting
#### frontend
* horizon is clear
### post-semester
* check with study to see the best way to add to this
0.1.0a
======
This is an alpha release to demonstrate the simplicity and agility of an AngularJS/D3 implementation. Also I need to graduate.
from django.db import models
# Create your models here.
# Import models
from app.models import Call, Participant
from rest_framework import serializers
class CallSerializer(serializers.ModelSerializer):
class Meta:
model = Call
class ParticipantSerializer(serializers.ModelSerializer):
class Meta:
model = Participant
"""
# Example serializers
class AssetSerializer(serializers.ModelSerializer):
# campus = serializers.CharField(source='campus')
# building = serializers.CharField(source='building')
# room = serializers.CharField(source='room')
# inventory_unit = serializers.CharField(source='inventory_unit')
scans = ScanSerializer(many=True)
campus = serializers.CharField(source='campus')
building = serializers.CharField(source='building')
room = serializers.CharField(source='room')
department = serializers.CharField(source='department')
scanned = serializers.SerializerMethodField('was_scanned')
def was_scanned(self, asset):
return Scan.objects.filter(asset=asset, scan_date__gt=asset.updated).count() > 0
class Meta:
model = Asset
"""
"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)
from django.conf.urls import patterns, include, url
from api import views
urlpatterns = patterns('',
url(r'^participants/$', views.ParticipantList.as_view()),
url(r'^participants/(?P<pk>[0-9]+)/$', views.ParticipantDetail.as_view()),
url(r'^calls/$', views.CallList.as_view()),
url(r'^calls/(?P<pk>[0-9]+)/$', views.CallDetail.as_view())
)
# Import Models
from app.models import Call, Participant
# Import Serializers
from api.serializers import CallSerializer, ParticipantSerializer
from rest_framework import generics
from rest_framework import filters
import django_filters
class CallList(generics.ListAPIView):
serializer_class = CallSerializer
filter_backends = (filters.OrderingFilter,)
ordering = ['-completed_date']
def get_queryset(self):
return Call.objects.all()
class CallDetail(generics.RetrieveAPIView):
queryset = Call.objects.all()
serializer_class = CallSerializer
class ParticipantList(generics.ListAPIView):
serializer_class = ParticipantSerializer
paginate_by = 100
paginate_by_param = 'page_size'
max_paginate_by = 500
def get_queryset(self):
return Participant.objects.all()
class ParticipantDetail(generics.RetrieveAPIView):
queryset = Participant.objects.all()
serializer_class = ParticipantSerializer
from django.contrib import admin
from app import models
admin.site.register(models.Email)
admin.site.register(models.Phone)
admin.site.register(models.Participant)
admin.site.register(models.ParticipantProblem)
admin.site.register(models.Call)
import pprint, csv
from django.core.management.base import BaseCommand
from sync.models import Session, update_participants, update_phone_numbers, update_emails, update_calls, update_problems
class Command(BaseCommand):
help = "Updates beemo's database from remote database."
def handle(self, *args, **options):
session = Session()
issue_list = list()
update_participants(session, issue_list)
update_phone_numbers(session, issue_list)
update_emails(issue_list)
update_calls(session, issue_list)
update_problems(session, issue_list)
keys = ['participant', 'call_num', 'field', 'reason']
with open('issues.csv', 'wb') as csvfile:
dw = csv.DictWriter(csvfile, delimiter=',', fieldnames=keys)
headers = {}
for n in keys:
headers[n] = n
dw.writerow(headers)
for row in issue_list:
dw.writerow(row)
session.close()
from django.core.management.base import BaseCommand
from app.models import Call, Participant, calculate_adherence_score
def update_adherence_scores():
for participant in Participant.objects.all():
for call in participant.calls.all():
call.adherence_score = calculate_adherence_score(participant, call)
call.save()
class Command(BaseCommand):
help = "Calculates and updates call adherence scores."
def handle(self, *args, **options):
update_adherence_scores()
import requests
from imapclient import IMAPClient
from django.core.management.base import BaseCommand
from beemo.settings import GMAIL_INFO, TWILIO_INFO
from app.models import Participant
twilio_base_url = 'https://api.twilio.com/2010-04-01/Accounts/%s/' % TWILIO_INFO['sid']
twilio_auth = (TWILIO_INFO['sid'], TWILIO_INFO['token'])
def update_email_counts(participant, server):
emails_in = 0
emails_out = 0
for email in participant.emails.all():
messages = server.search('FROM %s' % email.email)
emails_out += len(messages)
messages = server.search('TO %s' % email.email)
emails_in += len(messages)
participant.emails_in = emails_in
participant.emails_out = emails_out
participant.save()
def update_call_counts(participant):
calls_url = twilio_base_url + 'Calls.json'
calls_in = 0
calls_out = 0
for phone in participant.phone_numbers.all():
phone_number = '+1' + phone.number
response = requests.get(
calls_url,
auth=twilio_auth,
params={'To': phone_number, 'PageSize': 1}
).json()
calls_in += response['total']
response = requests.get(
calls_url,
auth=twilio_auth,
params={'From': phone_number, 'PageSize': 1}
).json()
calls_out += response['total']
participant.calls_in = calls_in
participant.calls_out = calls_out
participant.save()
def update_sms_counts(participant):
sms_number = '+1' + participant.sms_number.number
messages_url = twilio_base_url + 'Messages.json'
response = requests.get(
messages_url,
auth=twilio_auth,
params={'To': sms_number}
).json()
sms_in = response['total']
payload = {'From': sms_number}
response = requests.get(
messages_url,
auth=twilio_auth,
params={'From': sms_number}
).json()
sms_out = response['total']
participant.sms_in = sms_in
participant.sms_out = sms_out
participant.save()
def update_technology_touches():
gmail = IMAPClient(
'imap.gmail.com',
use_uid=True,
ssl=True
)
gmail.login(GMAIL_INFO['user'], GMAIL_INFO['pass'])
gmail.select_folder('[Gmail]/All Mail')
for participant in Participant.objects.all():
update_email_counts(participant, gmail)
update_call_counts(participant)
if participant.sms_number:
update_sms_counts(participant)
gmail.logout()
class Command(BaseCommand):
help = "Updates technology touch counts using IMAPClient and Twilio's REST API."
def handle(self, *args, **options):
update_technology_touches()
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'Email'
db.create_table(u'app_email', (
('email', self.gf('django.db.models.fields.EmailField')(max_length=75, primary_key=True)),
('participant', self.gf('django.db.models.fields.related.ForeignKey')(related_name='emails', to=orm['app.Participant'])),
))
db.send_create_signal('app', ['Email'])
# Adding model 'Phone'
db.create_table(u'app_phone', (
('number', self.gf('django.db.models.fields.CharField')(max_length=10, primary_key=True)),
))
db.send_create_signal('app', ['Phone'])
# Adding model 'Participant'
db.create_table(u'app_participant', (
('pid', self.gf('django.db.models.fields.CharField')(max_length=60, primary_key=True)),
('creation_date', self.gf('django.db.models.fields.DateField')()),
('sms_number', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='sms_participant', null=True, to=orm['app.Phone'])),
('base_fat_goal', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True)),
('base_step_goal', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True)),
))
db.send_create_signal('app', ['Participant'])
# Adding M2M table for field phone_numbers on 'Participant'
m2m_table_name = db.shorten_name(u'app_participant_phone_numbers')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('participant', models.ForeignKey(orm['app.participant'], null=False)),
('phone', models.ForeignKey(orm['app.phone'], null=False))
))
db.create_unique(m2m_table_name, ['participant_id', 'phone_id'])
# Adding model 'Call'
db.create_table(u'app_call', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('number', self.gf('django.db.models.fields.IntegerField')()),
('participant', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['app.Participant'])),
('completed_date', self.gf('django.db.models.fields.DateField')()),
('goal_met', self.gf('django.db.models.fields.BooleanField')(default=False)),
('veg_servings', self.gf('django.db.models.fields.PositiveIntegerField')()),
('fruit_servings', self.gf('django.db.models.fields.PositiveIntegerField')()),
('fiber_grams', self.gf('django.db.models.fields.PositiveIntegerField')()),
('fat_grams', self.gf('django.db.models.fields.PositiveIntegerField')()),
('steps', self.gf('django.db.models.fields.PositiveIntegerField')()),
))
db.send_create_signal('app', ['Call'])
# Adding model 'ParticipantProblem'
db.create_table(u'app_participantproblem', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('participant', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['app.Participant'])),
('date', self.gf('django.db.models.fields.DateField')()),
('problem', self.gf('django.db.models.fields.TextField')(blank=True)),
))
db.send_create_signal('app', ['ParticipantProblem'])
def backwards(self, orm):
# Deleting model 'Email'
db.delete_table(u'app_email')
# Deleting model 'Phone'
db.delete_table(u'app_phone')
# Deleting model 'Participant'
db.delete_table(u'app_participant')
# Removing M2M table for field phone_numbers on 'Participant'
db.delete_table(db.shorten_name(u'app_participant_phone_numbers'))
# Deleting model 'Call'
db.delete_table(u'app_call')
# Deleting model 'ParticipantProblem'
db.delete_table(u'app_participantproblem')
models = {
'app.call': {
'Meta': {'object_name': 'Call'},
'completed_date': ('django.db.models.fields.DateField', [], {}),
'fat_grams': ('django.db.models.fields.PositiveIntegerField', [], {}),
'fiber_grams': ('django.db.models.fields.PositiveIntegerField', [], {}),
'fruit_servings': ('django.db.models.fields.PositiveIntegerField', [], {}),
'goal_met': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'number': ('django.db.models.fields.IntegerField', [], {}),
'participant': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['app.Participant']"}),
'steps': ('django.db.models.fields.PositiveIntegerField', [], {}),
'veg_servings': ('django.db.models.fields.PositiveIntegerField', [], {})
},
'app.email': {
'Meta': {'object_name': 'Email'},
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'primary_key': 'True'}),
'participant': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emails'", 'to': "orm['app.Participant']"})
},
'app.participant': {
'Meta': {'object_name': 'Participant'},
'base_fat_goal': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
'base_step_goal': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
'creation_date': ('django.db.models.fields.DateField', [], {}),
'phone_numbers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['app.Phone']", 'symmetrical': 'False'}),
'pid': ('django.db.models.fields.CharField', [], {'max_length': '60', 'primary_key': 'True'}),
'sms_number': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'sms_participant'", 'null': 'True', 'to': "orm['app.Phone']"})
},
'app.participantproblem': {
'Meta': {'object_name': 'ParticipantProblem'},
'date': ('django.db.models.fields.DateField', [], {}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'participant': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['app.Participant']"}),
'problem': ('django.db.models.fields.TextField', [], {'blank': 'True'})
},
'app.phone': {
'Meta': {'object_name': 'Phone'},
'number': ('django.db.models.fields.CharField', [], {'max_length': '10', 'primary_key': 'True'})
}
}
complete_apps = ['app']
\ No newline at end of file
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Call.adherence_score'
db.add_column(u'app_call', 'adherence_score',
self.gf('django.db.models.fields.FloatField')(null=True),
keep_default=False)
# Changing field 'Call.fiber_grams'
db.alter_column(u'app_call', 'fiber_grams', self.gf('django.db.models.fields.PositiveIntegerField')(null=True))
# Changing field 'Call.veg_servings'
db.alter_column(u'app_call', 'veg_servings', self.gf('django.db.models.fields.PositiveIntegerField')(null=True))
# Changing field 'Call.fat_grams'
db.alter_column(u'app_call', 'fat_grams', self.gf('django.db.models.fields.PositiveIntegerField')(null=True))
# Changing field 'Call.steps'
db.alter_column(u'app_call', 'steps', self.gf('django.db.models.fields.PositiveIntegerField')(null=True))
# Changing field 'Call.fruit_servings'
db.alter_column(u'app_call', 'fruit_servings', self.gf('django.db.models.fields.PositiveIntegerField')(null=True))
def backwards(self, orm):
# Deleting field 'Call.adherence_score'
db.delete_column(u'app_call', 'adherence_score')
# Changing field 'Call.fiber_grams'
db.alter_column(u'app_call', 'fiber_grams', self.gf('django.db.models.fields.PositiveIntegerField')(default=None))
# Changing field 'Call.veg_servings'
db.alter_column(u'app_call', 'veg_servings', self.gf('django.db.models.fields.PositiveIntegerField')(default=None))
# Changing field 'Call.fat_grams'
db.alter_column(u'app_call', 'fat_grams', self.gf('django.db.models.fields.PositiveIntegerField')(default=None))
# Changing field 'Call.steps'
db.alter_column(u'app_call', 'steps', self.gf('django.db.models.fields.PositiveIntegerField')(default=None))
# Changing field 'Call.fruit_servings'
db.alter_column(u'app_call', 'fruit_servings', self.gf('django.db.models.fields.PositiveIntegerField')(default=None))
models = {
'app.call': {
'Meta': {'object_name': 'Call'},
'adherence_score': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
'completed_date': ('django.db.models.fields.DateField', [], {}),
'fat_grams': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
'fiber_grams': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
'fruit_servings': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
'goal_met': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'number': ('django.db.models.fields.IntegerField', [], {}),
'participant': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'calls'", 'to': "orm['app.Participant']"}),
'steps': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
'veg_servings': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'})
},
'app.email': {
'Meta': {'object_name': 'Email'},
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'primary_key': 'True'}),
'participant': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emails'", 'to': "orm['app.Participant']"})
},
'app.participant': {
'Meta': {'object_name': 'Participant'},
'base_fat_goal': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
'base_step_goal': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
'creation_date': ('django.db.models.fields.DateField', [], {}),
'phone_numbers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['app.Phone']", 'symmetrical': 'False'}),
'pid': ('django.db.models.fields.CharField', [], {'max_length': '60', 'primary_key': 'True'}),
'sms_number': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'sms_participant'", 'null': 'True', 'to': "orm['app.Phone']"})
},
'app.participantproblem': {
'Meta': {'object_name': 'ParticipantProblem'},
'date': ('django.db.models.fields.DateField', [], {}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'participant': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['app.Participant']"}),
'problem': ('django.db.models.fields.TextField', [], {'blank': 'True'})
},
'app.phone': {
'Meta': {'object_name': 'Phone'},
'number': ('django.db.models.fields.CharField', [], {'max_length': '10', 'primary_key': 'True'})
}
}
complete_apps = ['app']
\ No newline at end of file