From 5fff9411a528eda6113645dba34592e175bbb664 Mon Sep 17 00:00:00 2001 From: "Brian S. Stephan" Date: Sun, 16 Mar 2014 15:46:15 -0500 Subject: [PATCH] Races: add module to do a crude racebot thing join/start/update/finish/leave. good enough start i think --- dr_botzo/settings.py | 1 + ircbot/modules/Races.py | 209 ++++++++++++++++++ races/__init__.py | 0 races/admin.py | 9 + ...1_race_and_racer_and_race_update_models.py | 82 +++++++ races/migrations/__init__.py | 0 races/models.py | 54 +++++ 7 files changed, 355 insertions(+) create mode 100644 ircbot/modules/Races.py create mode 100644 races/__init__.py create mode 100644 races/admin.py create mode 100644 races/migrations/0001_race_and_racer_and_race_update_models.py create mode 100644 races/migrations/__init__.py create mode 100644 races/models.py diff --git a/dr_botzo/settings.py b/dr_botzo/settings.py index 2cf45f9..dfe601c 100644 --- a/dr_botzo/settings.py +++ b/dr_botzo/settings.py @@ -38,6 +38,7 @@ INSTALLED_APPS = ( 'django.contrib.staticfiles', 'django_extensions', 'south', + 'races', 'seen', ) diff --git a/ircbot/modules/Races.py b/ircbot/modules/Races.py new file mode 100644 index 0000000..1f3d61a --- /dev/null +++ b/ircbot/modules/Races.py @@ -0,0 +1,209 @@ +""" +Seen - track races and those racing them +Copyright (C) 2014 Brian S. Stephan + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +""" + +import re + +from races.models import Race, Racer, RaceUpdate + +from Module import Module + + +class Races(Module): + + """Track races and allow for logging updates.""" + + def register_handlers(self): + """Hook handler functions into the IRC library.""" + + self.irc.add_global_regex_handler(['pubmsg', 'privmsg'], + r'^!race\s+(\S+)\s+join$', + self.join) + self.irc.add_global_regex_handler(['pubmsg', 'privmsg'], + r'^!race\s+(\S+)\s+leave$', + self.leave) + self.irc.add_global_regex_handler(['pubmsg', 'privmsg'], + r'^!race\s+(\S+)\s+start$', + self.start) + self.irc.add_global_regex_handler(['pubmsg', 'privmsg'], + r'^!race\s+(\S+)\s+finish$', + self.finish) + self.irc.add_global_regex_handler(['pubmsg', 'privmsg'], + r'^!race\s+(\S+)\s+update\s+(.*)$', + self.update) + + def unregister_handlers(self): + """Unhook handler functions into the IRC library.""" + + self.irc.remove_global_regex_handler(['pubmsg', 'privmsg'], + r'^!race\s+(\S+)\s+join$', + self.join) + self.irc.remove_global_regex_handler(['pubmsg', 'privmsg'], + r'^!race\s+(\S+)\s+leave$', + self.leave) + self.irc.remove_global_regex_handler(['pubmsg', 'privmsg'], + r'^!race\s+(\S+)\s+start$', + self.start) + self.irc.remove_global_regex_handler(['pubmsg', 'privmsg'], + r'^!race\s+(\S+)\s+finish$', + self.finish) + self.irc.remove_global_regex_handler(['pubmsg', 'privmsg'], + r'^!race\s+(\S+)\s+update\s+(.*)$', + self.update) + + def join(self, nick, userhost, event, from_admin, groups): + """Join a race.""" + + race_id, = groups + try: + race = Race.objects.get(key=race_id) + + # is the nick already in the race + try: + racer = Racer.objects.get(race=race, nick=nick) + + # ideally they're not in the db, but if they aren't flagged as joined, + # then let them join again i guess + if racer.joined: + return self.irc.reply(event, "you are already in {0:s}.".format(race.name)) + except Racer.DoesNotExist: + racer = Racer() + racer.race = race + racer.nick = nick + + racer.joined = True + racer.started = False + racer.finished = False + racer.save() + + return self.irc.reply(event, + "you have joined {0:s}.".format(race.name)) + except Race.DoesNotExist: + return self.irc.reply(event, "race {0:s} not found.".format(race_id)) + + def leave(self, nick, userhost, event, from_admin, groups): + """Leave a race.""" + + race_id, = groups + try: + race = Race.objects.get(key=race_id) + + try: + racer = Racer.objects.get(race=race, nick=nick) + + if racer.finished: + return self.irc.reply(event, "you can't leave {0:s} since you finished it." + "".format(race.name)) + + if racer.joined: + racer.joined = False + racer.save() + + return self.irc.reply(event, "you have left {0:s}.".format(race.name)) + else: + return self.irc.reply(event, "you weren't in {0:s}.".format(race.name)) + except Racer.DoesNotExist: + return self.irc.reply(event, "you weren't in {0:s}.".format(race.name)) + except Race.DoesNotExist: + return self.irc.reply(event, "race {0:s} not found.".format(race_id)) + + def start(self, nick, userhost, event, from_admin, groups): + """Start racing.""" + + race_id, = groups + try: + race = Race.objects.get(key=race_id) + + try: + racer = Racer.objects.get(race=race, nick=nick) + + if racer.started: + return self.irc.reply(event, "you have already started {0:s}.".format(race.name)) + + if racer.finished: + return self.irc.reply(event, "you can't start {0:s} since you finished it." + "".format(race.name)) + + racer.started = True + racer.save() + + # create an event for this too + RaceUpdate.objects.create(racer=racer, update="Joined the race.") + + return self.irc.reply(event, "you have started {0:s}.".format(race.name)) + except Racer.DoesNotExist: + return self.irc.reply(event, "you have not joined {0:s}.".format(race.name)) + except Race.DoesNotExist: + return self.irc.reply(event, "race {0:s} not found.".format(race_id)) + + def finish(self, nick, userhost, event, from_admin, groups): + """Finish racing.""" + + race_id, = groups + try: + race = Race.objects.get(key=race_id) + + try: + racer = Racer.objects.get(race=race, nick=nick) + + if not racer.started: + return self.irc.reply(event, "you have not started {0:s}.".format(race.name)) + + if racer.finished: + return self.irc.reply(event, "you have already finished {0:s}." + "".format(race.name)) + + racer.finished = True + racer.save() + + # create an event for this too + RaceUpdate.objects.create(racer=racer, update="Finished the race.") + + return self.irc.reply(event, "you have finished {0:s}.".format(race.name)) + except Racer.DoesNotExist: + return self.irc.reply(event, "you have not joined {0:s}.".format(race.name)) + except Race.DoesNotExist: + return self.irc.reply(event, "race {0:s} not found.".format(race_id)) + + def update(self, nick, userhost, event, from_admin, groups): + """Add an update to an ongoing race.""" + + race_id, update = groups + try: + race = Race.objects.get(key=race_id) + + try: + racer = Racer.objects.get(race=race, nick=nick) + + if not racer.started: + return self.irc.reply(event, "you have not started {0:s}.".format(race.name)) + + if racer.finished: + return self.irc.reply(event, "you have already finished {0:s}." + "".format(race.name)) + + # create an event + RaceUpdate.objects.create(racer=racer, update=update) + + return self.irc.reply(event, "update logged.") + except Racer.DoesNotExist: + return self.irc.reply(event, "you have not joined {0:s}.".format(race.name)) + except Race.DoesNotExist: + return self.irc.reply(event, "race {0:s} not found.".format(race_id)) + +# vi:tabstop=4:expandtab:autoindent diff --git a/races/__init__.py b/races/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/races/admin.py b/races/admin.py new file mode 100644 index 0000000..5b9a39d --- /dev/null +++ b/races/admin.py @@ -0,0 +1,9 @@ +from django.contrib import admin + +from races.models import Race, Racer, RaceUpdate + +admin.site.register(Race) +admin.site.register(Racer) +admin.site.register(RaceUpdate) + +# vi:tabstop=4:expandtab:autoindent diff --git a/races/migrations/0001_race_and_racer_and_race_update_models.py b/races/migrations/0001_race_and_racer_and_race_update_models.py new file mode 100644 index 0000000..c773a9c --- /dev/null +++ b/races/migrations/0001_race_and_racer_and_race_update_models.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as 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 'Race' + db.create_table(u'races_race', ( + ('key', self.gf('django.db.models.fields.CharField')(max_length=16, primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('description', self.gf('django.db.models.fields.TextField')()), + )) + db.send_create_signal(u'races', ['Race']) + + # Adding model 'Racer' + db.create_table(u'races_racer', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('nick', self.gf('django.db.models.fields.CharField')(max_length=64)), + ('race', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['races.Race'])), + ('joined', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('started', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('finished', self.gf('django.db.models.fields.BooleanField')(default=False)), + )) + db.send_create_signal(u'races', ['Racer']) + + # Adding unique constraint on 'Racer', fields ['nick', 'race'] + db.create_unique(u'races_racer', ['nick', 'race_id']) + + # Adding model 'RaceUpdate' + db.create_table(u'races_raceupdate', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('racer', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['races.Racer'])), + ('update', self.gf('django.db.models.fields.TextField')()), + ('event_time', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), + )) + db.send_create_signal(u'races', ['RaceUpdate']) + + + def backwards(self, orm): + # Removing unique constraint on 'Racer', fields ['nick', 'race'] + db.delete_unique(u'races_racer', ['nick', 'race_id']) + + # Deleting model 'Race' + db.delete_table(u'races_race') + + # Deleting model 'Racer' + db.delete_table(u'races_racer') + + # Deleting model 'RaceUpdate' + db.delete_table(u'races_raceupdate') + + + models = { + u'races.race': { + 'Meta': {'object_name': 'Race'}, + 'description': ('django.db.models.fields.TextField', [], {}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '16', 'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + u'races.racer': { + 'Meta': {'unique_together': "(('nick', 'race'),)", 'object_name': 'Racer'}, + 'finished': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'joined': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'nick': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'race': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['races.Race']"}), + 'started': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + u'races.raceupdate': { + 'Meta': {'object_name': 'RaceUpdate'}, + 'event_time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'racer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['races.Racer']"}), + 'update': ('django.db.models.fields.TextField', [], {}) + } + } + + complete_apps = ['races'] \ No newline at end of file diff --git a/races/migrations/__init__.py b/races/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/races/models.py b/races/models.py new file mode 100644 index 0000000..913f3c8 --- /dev/null +++ b/races/models.py @@ -0,0 +1,54 @@ +from django.db import models +from django.utils import timezone + + +class Race(models.Model): + + """Track a race.""" + + key = models.CharField(max_length=16, primary_key=True) + name = models.CharField(max_length=255) + description = models.TextField() + + def __unicode__(self): + """Text representation of a race.""" + + return u"{0:s} ({1:s})".format(self.name, self.key) + + +class Racer(models.Model): + + """Track a racer in a race.""" + + nick = models.CharField(max_length=64) + race = models.ForeignKey(Race) + + joined = models.BooleanField(default=False) + started = models.BooleanField(default=False) + finished = models.BooleanField(default=False) + + class Meta: + unique_together = ('nick', 'race') + + def __unicode__(self): + """Text representation of a race racer.""" + + return u"{0:s} in {1:s}".format(self.nick, self.race.name) + + +class RaceUpdate(models.Model): + + """Periodic updates for a racer.""" + + racer = models.ForeignKey(Racer) + update = models.TextField() + event_time = models.DateTimeField(default=timezone.now) + + def __unicode__(self): + """Text representation of a race update.""" + + local_time = timezone.localtime(self.event_time) + return u"{0:s} in {1:s} @ {2:s}".format(self.racer.nick, self.racer.race.name, + local_time.strftime('%Y-%m-%d %H:%M:%S %Z')) + +# vi:tabstop=4:expandtab:autoindent