Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finish backend #2

Merged
merged 19 commits into from Apr 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 23 additions & 0 deletions backend/api/migrations/0004_auto_20190413_1810.py
@@ -0,0 +1,23 @@
# Generated by Django 2.1.7 on 2019-04-13 18:10

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api', '0003_auto_20190325_1902'),
]

operations = [
migrations.AlterField(
model_name='logentry',
name='reaction',
field=models.CharField(choices=[('HA', 'Happy'), ('NE', 'Neutral'), ('TI', 'Tired'), ('AN', 'Angry'), ('SA', 'Sad')], max_length=2),
),
migrations.AlterField(
model_name='recommendation',
name='rec_typ',
field=models.CharField(choices=[('PO', 'Positive'), ('NE', 'Negative'), ('AV', 'Avoidance'), ('GE', 'Generic')], max_length=2),
),
]
18 changes: 18 additions & 0 deletions backend/api/migrations/0005_recommendation_rec_description.py
@@ -0,0 +1,18 @@
# Generated by Django 2.1.7 on 2019-04-13 18:12

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api', '0004_auto_20190413_1810'),
]

operations = [
migrations.AddField(
model_name='recommendation',
name='rec_description',
field=models.CharField(default='', max_length=512),
),
]
19 changes: 19 additions & 0 deletions backend/api/migrations/0006_logentry_content_class.py
@@ -0,0 +1,19 @@
# Generated by Django 2.1.7 on 2019-04-13 18:40

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api', '0005_recommendation_rec_description'),
]

operations = [
migrations.AddField(
model_name='logentry',
name='content_class',
field=models.CharField(choices=[('SM', 'Small Talk'), ('OP', 'One Personal'), ('BP', 'Both Personal')], default='SM', max_length=2),
preserve_default=False,
),
]
18 changes: 18 additions & 0 deletions backend/api/migrations/0007_user_has_generic_recs.py
@@ -0,0 +1,18 @@
# Generated by Django 2.1.7 on 2019-04-14 16:05

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api', '0006_logentry_content_class'),
]

operations = [
migrations.AddField(
model_name='user',
name='has_generic_recs',
field=models.BooleanField(default=False),
),
]
20 changes: 15 additions & 5 deletions backend/api/models.py
Expand Up @@ -4,6 +4,7 @@ class User(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=64)
email = models.CharField(max_length=512)
has_generic_recs = models.BooleanField(default=False)

def __str__(self):
return 'Person("{}")'.format(self.name)
Expand All @@ -19,11 +20,11 @@ def __str__(self):

class LogEntry(models.Model):
REACTION_CHOICES = (
('ha', 'Happy'),
('ne', 'Neutral'),
('ti', 'Tired'),
('an', 'Angry'),
('sa', 'Sad'),
('HA', 'Happy'),
('NE', 'Neutral'),
('TI', 'Tired'),
('AN', 'Angry'),
('SA', 'Sad'),
)

TIME_CHOICES = (
Expand All @@ -48,6 +49,12 @@ class LogEntry(models.Model):
('PH', 'Over The Phone'),
)

CONTENT_CHOICES = (
('SM', 'Small Talk'),
('OP', 'One Personal'),
('BP', 'Both Personal'),
)

id = models.AutoField(primary_key=True)
reaction = models.CharField(max_length=2, choices=REACTION_CHOICES)
# Who is logged about
Expand All @@ -56,6 +63,7 @@ class LogEntry(models.Model):
time_of_day = models.CharField(max_length=2, choices=TIME_CHOICES)
social_context = models.CharField(max_length=2, choices=SOCIAL_CHOICES)
interaction_medium = models.CharField(max_length=2, choices=MEDIUM_CHOICES)
content_class = models.CharField(max_length=2, choices=CONTENT_CHOICES)
other_loggable_text = models.CharField(max_length=512)

created_at = models.DateTimeField(auto_now_add=True)
Expand All @@ -67,9 +75,11 @@ class Recommendation(models.Model):
('PO', 'Positive'),
('NE', 'Negative'),
('AV', 'Avoidance'),
('GE', 'Generic'),
)
rec_typ = models.CharField(max_length=2, choices=RECOMMENDATION_CHOICES)
recommendation = models.CharField(max_length=128)
rec_description = models.CharField(max_length=512, default='')
recommend_person = models.ForeignKey('User', models.CASCADE, related_name='recommend_person')
about_person = models.ForeignKey('Friend', models.SET_NULL, related_name='about_person', null=True)

Expand Down
232 changes: 232 additions & 0 deletions backend/api/recommender.py
@@ -0,0 +1,232 @@
import copy

from .models import User, Friend, Recommendation
from collections import defaultdict

def _create_and_save_recommendation(rec, friend_id=None):
r = Recommendation()
r.rec_typ = rec['rec_type']
r.recommendation = rec['recommendation']
r.rec_description = rec['rec_description']
r.recommend_person = User.objects.get(id=rec['recommend_person'])
if friend_id:
r.about_person = Friend.objects.get(id=friend_id)
r.save()

def recommendations_from_logs(logs, user_id):
recommendations = {
"data": []
}
r = {
"recommend_person": user_id
}

user = User.objects.get(id=user_id)

total_logs = len(logs)
in_person = 0
online = 0
over_the_phone = 0

small_talk = defaultdict(lambda: {'small': 0, 'long': 0}) # key is friend ID
count_small_talk = 0

reactions = defaultdict(lambda: { # key is friend ID
'Happy': 0,
'Neutral': 0,
'Tired': 0,
'Angry': 0,
'Sad': 0,
'total': 0,
})

morning = 0
afternoon = 0
evening = 0

for log in logs:
if log.interaction_medium == 'IP':
in_person += 1
elif log.interaction_medium == 'ON':
online += 1
elif log.interaction_medium == 'PH':
over_the_phone += 1

if log.content_class == 'SM':
count_small_talk += 1
# TODO: need to handle null case
small_talk[log.loggee]['small'] += 1
else:
small_talk[log.loggee]['long'] += 1

reactions[log.loggee][log.reaction] += 1
reactions[log.loggee]['total'] += 1

if log.time_of_day == 'MO':
morning += 1
elif log.time_of_day == 'AF':
afternoon += 1
elif log.time_of_day == 'EV':
evening += 1

in_person_frac = in_person / total_logs * 100
if in_person_frac < 50:
r['rec_type'] = 'PA'
r["recommendation"] = "Try to have more in-person interactions."
r["rec_description"] = (
"In-person interactions allow for more control "
"over the interaction, like body language "
"and other visual cues. Online and over-the-phone "
"interactions should be used once a good relationship "
"is already built."
)
_create_and_save_recommendation(r)
recommendations['data'].append(copy.deepcopy(r))
for friend in reactions:
name = friend.name
total = reactions[friend]['total']
if (reactions[friend]['Happy']/total * 100) > 50:
r['rec_type'] = 'PO'
r["recommendation"] = "Spend more time with {}.".format(name)
r["rec_description"] = (
"Looks like your interactions with "
"this friend are typically positive - "
"try to have more of them!"
)
_create_and_save_recommendation(r)
recommendations['data'].append(copy.deepcopy(r))
elif (reactions[friend]['Tired']/total * 100) > 10:
r['rec_type'] = 'PO'
r["recommendation"] = "Talk to {} different times.".format(name)
r["rec_description"] = (
"It's okay to be tired! "
"Talk to this friend when you feel more awake, "
"like after your morning coffee."
)
_create_and_save_recommendation(r)
recommendations['data'].append(copy.deepcopy(r))
elif (reactions[friend]['Angry']/total * 100) > 10:
r['rec_type'] = 'NE'
r["recommendation"] = "Talk to {} less.".format(name)
r["rec_description"] = (
"If this friend makes you feel angry, "
"it's a good idea to re-evaluate your "
"relationship or the context around "
"interactions that make you angry."
)
_create_and_save_recommendation(r)
recommendations['data'].append(copy.deepcopy(r))
elif (reactions[friend]['Sad']/total * 100) > 10:
r['rec_type'] = 'AV'
r["recommendation"] = "Avoid talking to {}.".format(name)
r["rec_description"] = (
"People that make you feel sad "
"shouldn't be in your life, "
"it's that simple!"
)
_create_and_save_recommendation(r)
recommendations['data'].append(copy.deepcopy(r))
for friend in small_talk:
name = friend.name
small_frac = small_talk[friend]['small']/(small_talk[friend]['small'] + small_talk[friend]['long']) * 100
if small_frac > 50:
r['rec_type'] = 'AV'
r["recommendation"] = "Avoid small talk with {}.".format(name)
r["rec_description"] = (
"Studies have shown that having more small talk "
"than not has a negative impact on happiness "
"and interaction quality. Focus on having more "
"substantive conversations, especially with this "
"friend."
)
_create_and_save_recommendation(r)
recommendations['data'].append(copy.deepcopy(r))
morning_frac = morning / total_logs * 100
afternoon_frac = afternoon / total_logs * 100
evening_frac = evening / total_logs * 100
if morning_frac > 50 or afternoon_frac > 50 or evening_frac > 50:
r['rec_type'] = 'PO'
r["recommendation"] = "Spread your interactions throughout the day."
r["rec_description"] = (
"Concentrating all of your interactions "
"in one part of the day makes it easier "
"for negative ones to ruin your day "
"and harder ones to positively influence your "
"day. Remember: some people need their "
"coffee in the morning!"
)
_create_and_save_recommendation(r)
recommendations['data'].append(copy.deepcopy(r))
if not user.has_generic_recs:
user.has_generic_recs = True
user.save()
r['rec_type'] = "GE"

r["recommendation"] = "Laugh and smile when starting an interaction."
r["rec_description"] = (
"Starting a conversation with an authentic friendly "
"smile creates a friendly and open look that invites "
"contact. If the other person smiles back, "
"then you've already started a form of contact!"
)
_create_and_save_recommendation(r)
recommendations['data'].append(copy.deepcopy(r))

r["recommendation"] = "Start with a compliment."
r["rec_description"] = (
"Starting a conversation with a compliment "
"is usually more interesting than a remark "
"about the weather. "
"Pick any feature that actually interests you, "
"otherwise it won't come off as genuine. "
"Feel free to continue on the topic if they show "
"interest!"
)
_create_and_save_recommendation(r)
recommendations['data'].append(copy.deepcopy(r))

r["recommendation"] = "Use body language."
r["rec_description"] = (
"You can create an 'understanding' "
"without using words by mirroring "
"the other person's body attitude. "
"This typically makes relating much "
"easier."
)
_create_and_save_recommendation(r)
recommendations['data'].append(copy.deepcopy(r))

r["recommendation"] = "Evaluate and understand."
r["rec_description"] = (
"Remember to evaluate reactions from "
"the other person and regarding verbal "
"or non-verbal communication. "
"You should also express what "
"you have understood from their behavior."
)
_create_and_save_recommendation(r)
recommendations['data'].append(copy.deepcopy(r))

r["recommendation"] = "Use humor!"
r["rec_description"] = (
"Humor creates an open and conversational "
"atmosphere, but remember not to joke at "
"the expense of other people and respect "
"any other sentiments. "
)
_create_and_save_recommendation(r)
recommendations['data'].append(copy.deepcopy(r))

r["recommendation"] = "Open up."
r["rec_description"] = (
"You're not a reporter! The more you show "
"yourself, the more interested they become. "
"A good conversation involves being interested "
"in the other person as well as opening up. "
"Opening up makes it easier for others to "
"relate to you, but make sure you're authentic."
)
_create_and_save_recommendation(r)
recommendations['data'].append(copy.deepcopy(r))

return recommendations