collapsing all of dr_botzo one directory

This commit is contained in:
2017-02-04 11:48:55 -06:00
parent 38d14bb0d2
commit cd23f062a9
194 changed files with 0 additions and 0 deletions

0
karma/__init__.py Normal file
View File

9
karma/admin.py Normal file
View File

@@ -0,0 +1,9 @@
"""Manage karma models in the admin interface."""
from django.contrib import admin
from karma.models import KarmaKey, KarmaLogEntry
admin.site.register(KarmaKey)
admin.site.register(KarmaLogEntry)

161
karma/ircplugin.py Normal file
View File

@@ -0,0 +1,161 @@
"""Karma hooks for the IRC bot."""
import logging
import re
import irc.client
from django.conf import settings
from ircbot.lib import Plugin
from karma.models import KarmaKey, KarmaLogEntry
log = logging.getLogger('karma.ircplugin')
class Karma(Plugin):
"""Track karma and report on it."""
def start(self):
"""Set up the handlers."""
self.connection.add_global_handler('pubmsg', self.handle_chatter, -20)
self.connection.add_global_handler('privmsg', self.handle_chatter, -20)
self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'],
r'^!karma\s+rank\s+(.*)$',
self.handle_rank, -20)
self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'],
(r'^!karma\s+report\s+(highest|lowest|positive|negative'
r'|top|opinionated)'),
self.handle_report, -20)
self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'],
r'^!karma\s+stats\s+([\S]+)',
self.handle_stats, -20)
super(Karma, self).start()
def stop(self):
"""Tear down handlers."""
self.connection.remove_global_handler('pubmsg', self.handle_chatter)
self.connection.remove_global_handler('privmsg', self.handle_chatter)
self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_rank)
self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_report)
self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_stats)
super(Karma, self).stop()
@staticmethod
def handle_chatter(connection, event):
"""Watch karma from IRC chatter."""
what = event.arguments[0].lower()
karma_pattern = r'(?:\((.+?)\)|(\S+))(\+\+|--|\+-|-\+)(\s+|$)'
where = event.target
if where in settings.KARMA_IGNORE_CHATTER_TARGETS:
log.debug("ignoring chatter in {0:s}".format(where))
return
# check the line for karma
log.debug("searching '%s' for karma", what)
matches = re.findall(karma_pattern, what, re.IGNORECASE)
for match in matches:
key = match[0] if match[0] else match[1]
value = match[2]
karma_key, c = KarmaKey.objects.get_or_create(key=key)
if value == '++':
KarmaLogEntry.objects.create(key=karma_key, delta=1, nickmask=event.source)
elif value == '--':
KarmaLogEntry.objects.create(key=karma_key, delta=-1, nickmask=event.source)
elif value == '+-':
KarmaLogEntry.objects.create(key=karma_key, delta=1, nickmask=event.source)
KarmaLogEntry.objects.create(key=karma_key, delta=-1, nickmask=event.source)
elif value == '-+':
KarmaLogEntry.objects.create(key=karma_key, delta=-1, nickmask=event.source)
KarmaLogEntry.objects.create(key=karma_key, delta=1, nickmask=event.source)
def handle_rank(self, connection, event, match):
"""Report on the rank of a karma item."""
where = event.target
if where in settings.KARMA_IGNORE_COMMAND_TARGETS:
log.debug("ignoring command in {0:s}".format(where))
return
key = match.group(1).lower().rstrip()
try:
karma_key = KarmaKey.objects.get(key=key)
return self.bot.reply(event, "{0:s} has {1:d} points of karma (rank {2:d})".format(karma_key.key,
karma_key.score(),
karma_key.rank()))
except KarmaKey.DoesNotExist:
return self.bot.reply(event, "i have not seen any karma for {0:s}".format(match.group(1)))
def handle_report(self, connection, event, match):
"""Provide some karma reports."""
where = event.target
if where in settings.KARMA_IGNORE_COMMAND_TARGETS:
log.debug("ignoring command in {0:s}".format(where))
return
report = match.group(1).lower()
if report == 'highest':
sorted_keys = KarmaKey.objects.ranked_scored_order()
msg = "top 5 recipients: {0:s}".format(", ".join([str(x) for x in sorted_keys[:5]]))
return self.bot.reply(event, msg)
elif report == 'lowest':
sorted_keys = KarmaKey.objects.ranked_scored_reverse_order()
msg = "bottom 5 recipients: {0:s}".format(", ".join([str(x) for x in sorted_keys[:5]]))
return self.bot.reply(event, msg)
elif report == 'positive':
karmaers = KarmaLogEntry.objects.optimists()
karmaer_list = ", ".join(["{0:s} ({1:d})".format(irc.client.NickMask(x['nickmask']).nick,
x['karma_outlook']) for x in karmaers])
msg = "top 5 optimists: {0:s}".format(karmaer_list)
return self.bot.reply(event, msg)
elif report == 'negative':
karmaers = KarmaLogEntry.objects.pessimists()
karmaer_list = ", ".join(["{0:s} ({1:d})".format(irc.client.NickMask(x['nickmask']).nick,
x['karma_outlook']) for x in karmaers])
msg = "top 5 pessimists: {0:s}".format(karmaer_list)
return self.bot.reply(event, msg)
elif report == 'top' or report == 'opinionated':
karmaers = KarmaLogEntry.objects.most_opinionated()
karmaer_list = ", ".join(["{0:s} ({1:d})".format(irc.client.NickMask(x['nickmask']).nick,
x['karma_count']) for x in karmaers])
msg = "top 5 opinionated users: {0:s}".format(karmaer_list)
return self.bot.reply(event, msg)
def handle_stats(self, connection, event, match):
"""Provide stats on a karma user."""
where = event.target
if where in settings.KARMA_IGNORE_COMMAND_TARGETS:
log.debug("ignoring command in {0:s}".format(where))
return
karmaer = match.group(1)
log_entries = KarmaLogEntry.objects.filter(nickmask=karmaer)
if len(log_entries) == 0:
# fallback, try to match the nick part of nickmask
log_entries = KarmaLogEntry.objects.filter(nickmask__startswith='{0:s}!'.format(karmaer))
if len(log_entries) == 0:
return self.bot.reply(event, "karma user {0:s} not found".format(karmaer))
total_karma = log_entries.count()
positive_karma = log_entries.filter(delta__gt=0).count()
negative_karma = log_entries.filter(delta__lt=0).count()
msg = ("{0:s} has given {1:d} postive karma and {2:d} negative karma, for a total of {3:d} karma"
"".format(karmaer, positive_karma, negative_karma, total_karma))
return self.bot.reply(event, msg)
plugin = Karma

View File

@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
]
operations = [
migrations.CreateModel(
name='KarmaKey',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('key', models.CharField(unique=True, max_length=200)),
],
),
migrations.CreateModel(
name='KarmaLogEntry',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('delta', models.SmallIntegerField()),
('nickmask', models.CharField(default='', max_length=200, blank=True)),
('created', models.DateTimeField(auto_now_add=True)),
('key', models.ForeignKey(to='karma.KarmaKey')),
],
),
]

View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('karma', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='karmalogentry',
options={'verbose_name_plural': 'karma log entries'},
),
]

View File

136
karma/models.py Normal file
View File

@@ -0,0 +1,136 @@
"""Karma logging models."""
from datetime import timedelta
import logging
import pytz
from irc.client import NickMask
from django.conf import settings
from django.db import models
from django.utils import timezone
log = logging.getLogger('karma.models')
def perdelta(start, end, delta):
curr = start
while curr < end:
yield curr
curr += delta
yield end
class KarmaKeyManager(models.Manager):
"""Manage handy queries for KarmaKey."""
def ranked_scored_order(self):
keys = self.annotate(karma_score=models.Sum('karmalogentry__delta')).order_by('-karma_score')
return keys
def ranked_scored_reverse_order(self):
keys = self.annotate(karma_score=models.Sum('karmalogentry__delta')).order_by('karma_score')
return keys
class KarmaKey(models.Model):
"""Track a thing being karmaed."""
key = models.CharField(max_length=200, unique=True)
objects = KarmaKeyManager()
def __str__(self):
"""String representation."""
return "{0:s} ({1:d})".format(self.key, self.score())
def score(self):
"""Determine the score for this karma entry."""
return KarmaLogEntry.objects.filter(key=self).aggregate(models.Sum('delta'))['delta__sum']
def rank(self):
"""Determine the rank of this karma entry relative to the others."""
sorted_keys = KarmaKey.objects.ranked_scored_order()
for rank, key in enumerate(sorted_keys):
if key == self:
return rank+1
return None
def history(self, mode='delta'):
"""Determine the score for this karma entry at every delta."""
history = []
if mode == 'delta':
entries = KarmaLogEntry.objects.filter(key=self).order_by('created')
for entry in entries:
timestamp = entry.created
delta = entry.delta
score = KarmaLogEntry.objects.filter(key=self).filter(created__lte=entry.created).aggregate(
models.Sum('delta'))['delta__sum']
history.append((timestamp, delta, score))
elif mode == 'date':
first_entry = KarmaLogEntry.objects.filter(key=self).order_by('created')[0]
slice_begin = first_entry.created.date()
slice_end = timezone.now().date()
for timeslice in perdelta(slice_begin, slice_end, timedelta(days=1)):
score = KarmaLogEntry.objects.filter(key=self).filter(
created__lte=timeslice+timedelta(days=1)).aggregate(models.Sum('delta'))['delta__sum']
if not score:
score = 0
try:
prev_score = history[-1][2]
except IndexError:
prev_score = 0
delta = score - prev_score
history.append((timeslice, delta, score))
return history
class KarmaLogEntryManager(models.Manager):
"""Manage handy queries for KarmaLogEntry."""
def optimists(self):
karmaers = self.values('nickmask').annotate(karma_outlook=models.Sum('delta')).order_by('-karma_outlook')
return karmaers
def pessimists(self):
karmaers = self.values('nickmask').annotate(karma_outlook=models.Sum('delta')).order_by('karma_outlook')
return karmaers
def most_opinionated(self):
karmaers = self.values('nickmask').annotate(karma_count=models.Count('delta')).order_by('-karma_count')
return karmaers
class KarmaLogEntry(models.Model):
"""Track each karma increment/decrement."""
key = models.ForeignKey('KarmaKey')
delta = models.SmallIntegerField()
nickmask = models.CharField(max_length=200, default='', blank=True)
created = models.DateTimeField(auto_now_add=True)
objects = KarmaLogEntryManager()
class Meta:
verbose_name_plural = 'karma log entries'
def __str__(self):
"""String representation."""
tz = pytz.timezone(settings.TIME_ZONE)
return "{0:s}{1:s} @ {2:s} by {3:s}".format(self.key.key, '++' if self.delta > 0 else '--',
self.created.astimezone(tz).strftime('%Y-%m-%d %H:%M:%S %Z'),
NickMask(self.nickmask).nick)

12
karma/serializers.py Normal file
View File

@@ -0,0 +1,12 @@
"""Serializers for the karma objects."""
from rest_framework import serializers
from karma.models import KarmaKey
class KarmaKeySerializer(serializers.ModelSerializer):
class Meta:
model = KarmaKey
fields = ('id', 'key', 'score', 'rank')

View File

@@ -0,0 +1,44 @@
{% extends 'base.html' %}
{% load static %}
{% block extra_media %}
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/t/bs/jqc-1.12.0,dt-1.10.11/datatables.min.css"/>
<script type="text/javascript" src="https://cdn.datatables.net/t/bs/jqc-1.12.0,dt-1.10.11/datatables.min.js"></script>
{% endblock %}
{% block title %}karma{% endblock %}
{% block content %}
<div style="width: 75%; margin-left: auto; margin-right: auto;">
<table id="karma" class="display table table-striped table-bordered" cellspacing="0" width="100%">
<thead>
<tr>
<th>Key</th>
<th>Score</th>
</tr>
</thead>
<tfoot>
<tr>
<th>Key</th>
<th>Score</th>
</tr>
</tfoot>
<tbody>
{% for entry in entries %}
<tr>
<td><a href="{% url 'karma_key_detail' entry.key %}">{{ entry.key }}</a></td>
<td>{{ entry.score }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<script type="text/javascript">
$(document).ready(function() {
$('#karma').DataTable( {
"order": [[ 1, "desc" ]],
"lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
} );
} );
</script>
{% endblock %}

View File

@@ -0,0 +1,67 @@
{% extends 'base.html' %}
{% load static %}
{% block extra_media %}
<script type="text/javascript" src="{% get_static_prefix %}js/Chart.min.js"></script>
{% endblock %}
{% block title %}karma: {{ entry.key }}{% endblock %}
{% block content %}
<h3>{{ entry.key }}</h3>
<canvas id="karma_history"></canvas>
<script type="text/javascript">
Chart.defaults.global.responsive = true;
Chart.defaults.global.maintainAspectRatio = true;
Chart.types.Line.extend({name : "AltLine",
initialize : function(data) {
Chart.types.Line.prototype.initialize.apply(this, arguments);
this.scale.draw = function() {
if (this.display && (this.xLabelRotation > 90)) {
this.endPoint = this.height - 5;
}
Chart.Scale.prototype.draw.apply(this, arguments);
};
}
});
var ctx = $("#karma_history").get(0).getContext("2d");
var data = {
labels: [{% for x in entry_history %}"{{ x.0 }}", {% endfor %}],
datasets: [
{
label: "{{ entry.key }}",
fillColor: "rgba(150,150,220,0.2)",
strokeColor: "rgba(100,100,220,1)",
pointColor: "rgba(50,50,220,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(220,220,220,1)",
data: [{% for x in entry_history %}{{ x.2 }}, {% endfor %}]
}
]
};
var myLineChart = new Chart(ctx).AltLine(data, {
pointDot: false,
{% if entry_history|length > 100 %}
showTooltips: false,
scaleShowVerticalLines: false,
{% endif %}
tooltipEvents: ["click"]
});
</script>
<h5>Log</h5>
<table border="1px">
<tr>
<th>Date</th>
<th>Delta</th>
<th>Score</th>
</tr>
{% for delta in entry_history %}
<tr>
<td>{{ delta.0 }}</td>
<td>{{ delta.1 }}</td>
<td>{{ delta.2 }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}

16
karma/urls.py Normal file
View File

@@ -0,0 +1,16 @@
"""URL patterns for the karma views."""
from django.conf.urls import patterns, url, include
from rest_framework.routers import DefaultRouter
from karma.views import key_detail, index, KarmaKeyViewSet
router = DefaultRouter()
router.register(r'keys', KarmaKeyViewSet)
urlpatterns = patterns('races.views',
url(r'^$', index, name='karma_index'),
url(r'^key/(?P<karma_key>.+)/', key_detail, name='karma_key_detail'),
url(r'^api/', include(router.urls)),
)

35
karma/views.py Normal file
View File

@@ -0,0 +1,35 @@
"""Present karma data."""
import logging
from django.shortcuts import get_object_or_404, render
from rest_framework import viewsets
from karma.models import KarmaKey
from karma.serializers import KarmaKeySerializer
log = logging.getLogger('karma.views')
def index(request):
"""Display all karma keys."""
entries = KarmaKey.objects.all().order_by('key')
return render(request, 'karma/index.html', {'entries': entries})
def key_detail(request, karma_key):
"""Display the requested karma key."""
entry = get_object_or_404(KarmaKey, key=karma_key.lower())
return render(request, 'karma/karma_key.html', {'entry': entry, 'entry_history': entry.history(mode='date')})
class KarmaKeyViewSet(viewsets.ReadOnlyModelViewSet):
"""Provide list and detail actions for karma keys."""
queryset = KarmaKey.objects.all()
serializer_class = KarmaKeySerializer