Compare commits

...

5 Commits

Author SHA1 Message Date
Brian S. Stephan f78d407d4c move DiceRoller to its own module
while I'm doing that, standardize the usage of raising exceptions when parsing goes wrong
2019-06-21 16:53:40 -05:00
Brian S. Stephan 8528152483 remove dice cthulhutech roll. hasn't been used in forever 2019-06-21 16:51:09 -05:00
Brian S. Stephan f2fb0a26a4 remove unnecessary unicode_literal future imports, we py3 now 2019-06-21 15:23:33 -05:00
Brian S. Stephan 0f88715ffd remove unnecessary requirements-server.* 2019-06-21 10:06:17 -05:00
Brian S. Stephan 2f98a64cdd version bumps and migration to django 2.2 2019-06-21 10:05:40 -05:00
39 changed files with 405 additions and 559 deletions

View File

@ -1,7 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-02-23 02:25
from __future__ import unicode_literals
from django.db import migrations, models

View File

@ -1,7 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-02-24 01:06
from __future__ import unicode_literals
from django.db import migrations, models

View File

@ -1,7 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-02-24 01:12
from __future__ import unicode_literals
from django.db import migrations, models

View File

@ -1,36 +1,28 @@
"""Roll dice when asked, intended for RPGs."""
# this breaks yacc, but ply might be happy in py3
#from __future__ import unicode_literals
import logging
import math
import re
import random
from irc.client import NickMask
import ply.lex as lex
import ply.yacc as yacc
from dice.roller import DiceRoller
from ircbot.lib import Plugin
logger = logging.getLogger(__name__)
class Dice(Plugin):
"""Roll simple or complex dice strings."""
def __init__(self):
"""Set up the plugin."""
super(Dice, self).__init__()
self.roller = DiceRoller()
def start(self):
"""Set up the handlers."""
self.roller = DiceRoller()
self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], r'^!roll\s+(.*)$',
self.handle_roll, -20)
self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], r'^!ctech\s+(.*)$',
self.handle_ctech, -20)
self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], r'^!random\s+(.*)$',
self.handle_random, -20)
@ -38,9 +30,7 @@ class Dice(Plugin):
def stop(self):
"""Tear down handlers."""
self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_roll)
self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_ctech)
self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_random)
super(Dice, self).stop()
@ -68,368 +58,17 @@ class Dice(Plugin):
dicestr = match.group(1)
logger.debug(event.recursing)
try:
reply_str = self.roller.do_roll(dicestr)
except AssertionError as aex:
reply_str = f"Could not roll dice: {aex}"
except ValueError:
reply_str = "Unable to parse roll"
if event.recursing:
reply = "{0:s}".format(self.roller.do_roll(dicestr))
reply = "{0:s}".format(reply_str)
else:
reply = "{0:s}: {1:s}".format(nick, self.roller.do_roll(dicestr))
reply = "{0:s}: {1:s}".format(nick, reply_str)
return self.bot.reply(event, re.sub(r'(\d+)(.*?\s+)(\(.*?\))', r'\1\214\3', reply))
def handle_ctech(self, connection, event, match):
"""Handle cthulhutech dice rolls."""
nick = NickMask(event.source).nick
rollitrs = re.split(';\s*', match.group(1))
reply = ""
for count, roll in enumerate(rollitrs):
pattern = '^(\d+)d(?:(\+|\-)(\d+))?(?:\s+(.*))?'
regex = re.compile(pattern)
matches = regex.search(roll)
if matches is not None:
dice = int(matches.group(1))
modifier = 0
if matches.group(2) is not None and matches.group(3) is not None:
if str(matches.group(2)) == '-':
modifier = -1 * int(matches.group(3))
else:
modifier = int(matches.group(3))
result = roll + ': '
rolls = []
for d in range(dice):
rolls.append(random.randint(1, 10))
rolls.sort()
rolls.reverse()
# highest single die method
method1 = rolls[0]
# highest set method
method2 = 0
rolling_sum = 0
for i, r in enumerate(rolls):
# if next roll is same as current, sum and continue, else see if sum is best so far
if i+1 < len(rolls) and rolls[i+1] == r:
if rolling_sum == 0:
rolling_sum = r
rolling_sum += r
else:
if rolling_sum > method2:
method2 = rolling_sum
rolling_sum = 0
# check for set in progress (e.g. lots of 1s)
if rolling_sum > method2:
method2 = rolling_sum
# straight method
method3 = 0
rolling_sum = 0
count = 0
for i, r in enumerate(rolls):
# if next roll is one less as current, sum and continue, else check len and see if sum is best so far
if i+1 < len(rolls) and rolls[i+1] == r-1:
if rolling_sum == 0:
rolling_sum = r
count += 1
rolling_sum += r-1
count += 1
else:
if count >= 3 and rolling_sum > method3:
method3 = rolling_sum
rolling_sum = 0
# check for straight in progress (e.g. straight ending in 1)
if count >= 3 and rolling_sum > method3:
method3 = rolling_sum
# get best roll
best = max([method1, method2, method3])
# check for critical failure
botch = False
ones = 0
for r in rolls:
if r == 1:
ones += 1
if ones >= math.ceil(float(len(rolls))/2):
botch = True
if botch:
result += 'BOTCH'
else:
result += str(best + modifier)
rollres = ''
for i,r in enumerate(rolls):
rollres += str(r)
if i is not len(rolls)-1:
rollres += ','
result += ' [' + rollres
if modifier != 0:
if modifier > 0:
result += ' +' + str(modifier)
else:
result += ' -' + str(modifier * -1)
result += ']'
reply += result
if count is not len(rollitrs)-1:
reply += "; "
if reply is not "":
msg = "{0:s}: {1:s}".format(nick, reply)
return self.bot.reply(event, msg)
class DiceRoller(object):
tokens = ['NUMBER', 'TEXT', 'ROLLSEP']
literals = ['#', '/', '+', '-', 'd']
t_TEXT = r'\s+[^;]+'
t_ROLLSEP = r';\s*'
def build(self):
lex.lex(module=self)
yacc.yacc(module=self)
def t_NUMBER(self, t):
r'\d+'
t.value = int(t.value)
return t
def t_error(self, t):
t.lexer.skip(1)
precedence = (
('left', 'ROLLSEP'),
('left', '+', '-'),
('right', 'd'),
('left', '#'),
('left', '/')
)
output = ""
def roll_dice(self, keep, dice, size):
"""Takes the parsed dice string for a single roll (eg 3/4d20) and performs
the actual roll. Returns a string representing the result.
"""
a = list(range(dice))
for i in range(dice):
a[i] = random.randint(1, size)
if keep != dice:
b = sorted(a, reverse=True)
b = b[0:keep]
else:
b = a
total = sum(b)
outstr = "[" + ",".join(str(i) for i in a) + "]"
return (total, outstr)
def process_roll(self, trials, mods, comment):
"""Processes rolls coming from the parser.
This generates the inputs for the roll_dice() command, and returns
the full string representing the whole current dice string (the part
up to a semicolon or end of line).
"""
output = ""
repeat = 1
if trials != None:
repeat = trials
for i in range(repeat):
mode = 1
total = 0
curr_str = ""
if i > 0:
output += ", "
for m in mods:
keep = 0
dice = 1
res = 0
# if m is a tuple, then it is a die roll
# m[0] = (keep, num dice)
# m[1] = num faces on the die
if type(m) == tuple:
if m[0] != None:
if m[0][0] != None:
keep = m[0][0]
dice = m[0][1]
size = m[1]
if keep > dice or keep == 0:
keep = dice
if size < 1:
output = "# of sides for die is incorrect: %d" % size
return output
if dice < 1:
output = "# of dice is incorrect: %d" % dice
return output
res = self.roll_dice(keep, dice, size)
curr_str += "%d%s" % (res[0], res[1])
res = res[0]
elif m == "+":
mode = 1
curr_str += "+"
elif m == "-":
mode = -1
curr_str += "-"
else:
res = m
curr_str += str(m)
total += mode * res
if repeat == 1:
if comment != None:
output = "%d %s (%s)" % (total, comment.strip(), curr_str)
else:
output = "%d (%s)" % (total, curr_str)
else:
output += "%d (%s)" % (total, curr_str)
if i == repeat - 1:
if comment != None:
output += " (%s)" % (comment.strip())
return output
def p_roll_r(self, p):
# Chain rolls together.
# General idea I had when creating this grammar: A roll string is a chain
# of modifiers, which may be repeated for a certain number of trials. It can
# have a comment that describes the roll
# Multiple roll strings can be chained with semicolon
'roll : roll ROLLSEP roll'
global output
p[0] = p[1] + "; " + p[3]
output = p[0]
def p_roll(self, p):
# Parse a basic roll string.
'roll : trial modifier comment'
global output
mods = []
if type(p[2]) == list:
mods = p[2]
else:
mods = [p[2]]
p[0] = self.process_roll(p[1], mods, p[3])
output = p[0]
def p_roll_no_trials(self, p):
# Parse a roll string without trials.
'roll : modifier comment'
global output
mods = []
if type(p[1]) == list:
mods = p[1]
else:
mods = [p[1]]
p[0] = self.process_roll(None, mods, p[2])
output = p[0]
def p_comment(self, p):
# Parse a comment.
'''comment : TEXT
|'''
if len(p) == 2:
p[0] = p[1]
else:
p[0] = None
def p_modifier(self, p):
# Parse a modifier on a roll string.
'''modifier : modifier "+" modifier
| modifier "-" modifier'''
# Use append to prevent nested lists (makes dealing with this easier)
if type(p[1]) == list:
p[1].append(p[2])
p[1].append(p[3])
p[0] = p[1]
elif type(p[3]) == list:
p[3].insert(0, p[2])
p[3].insert(0, p[1])
p[0] = p[3]
else:
p[0] = [p[1], p[2], p[3]]
def p_die(self, p):
# Return the left side before the "d", and the number of faces.
'modifier : left NUMBER'
p[0] = (p[1], p[2])
def p_die_num(self, p):
'modifier : NUMBER'
p[0] = p[1]
def p_left(self, p):
# Parse the number of dice we are rolling, and how many we are keeping.
'left : keep dice'
if p[1] == None:
p[0] = [None, p[2]]
else:
p[0] = [p[1], p[2]]
def p_left_all(self, p):
'left : dice'
p[0] = [None, p[1]]
def p_left_e(self, p):
'left :'
p[0] = None
def p_total(self, p):
'trial : NUMBER "#"'
if len(p) > 1:
p[0] = p[1]
else:
p[0] = None
def p_keep(self, p):
'keep : NUMBER "/"'
if p[1] != None:
p[0] = p[1]
else:
p[0] = None
def p_dice(self, p):
'dice : NUMBER "d"'
p[0] = p[1]
def p_dice_one(self, p):
'dice : "d"'
p[0] = 1
def p_error(self, p):
# Provide the user with something (albeit not much) when the roll can't be parsed.
global output
output = "Unable to parse roll"
def get_result(self):
global output
return output
def do_roll(self, dicestr):
"""
Roll some dice and get the result (with broken out rolls).
Keyword arguments:
dicestr - format:
N#X/YdS+M label
N#: do the following roll N times (optional)
X/: take the top X rolls of the Y times rolled (optional)
Y : roll the die specified Y times (optional, defaults to 1)
dS: roll a S-sided die
+M: add M to the result (-M for subtraction) (optional)
"""
self.build()
yacc.parse(dicestr)
return self.get_result()
plugin = Dice

263
dice/roller.py Normal file
View File

@ -0,0 +1,263 @@
"""Dice rollers used by the views, bots, etc."""
import logging
import random
import ply.lex as lex
import ply.yacc as yacc
logger = logging.getLogger(__name__)
class DiceRoller(object):
tokens = ['NUMBER', 'TEXT', 'ROLLSEP']
literals = ['#', '/', '+', '-', 'd']
t_TEXT = r'\s+[^;]+'
t_ROLLSEP = r';\s*'
def build(self):
lex.lex(module=self)
yacc.yacc(module=self)
def t_NUMBER(self, t):
r'\d+'
t.value = int(t.value)
return t
def t_error(self, t):
t.lexer.skip(1)
precedence = (
('left', 'ROLLSEP'),
('left', '+', '-'),
('right', 'd'),
('left', '#'),
('left', '/')
)
output = ""
def roll_dice(self, keep, dice, size):
"""Takes the parsed dice string for a single roll (eg 3/4d20) and performs
the actual roll. Returns a string representing the result.
"""
a = list(range(dice))
for i in range(dice):
a[i] = random.randint(1, size)
if keep != dice:
b = sorted(a, reverse=True)
b = b[0:keep]
else:
b = a
total = sum(b)
outstr = "[" + ",".join(str(i) for i in a) + "]"
return (total, outstr)
def process_roll(self, trials, mods, comment):
"""Processes rolls coming from the parser.
This generates the inputs for the roll_dice() command, and returns
the full string representing the whole current dice string (the part
up to a semicolon or end of line).
"""
rolls = mods[0][0][1] if mods[0][0][1] else 1
assert trials <= 10, "Too many rolls (max: 10)."
assert rolls <= 50, "Too many dice (max: 50) in roll."
output = ""
repeat = 1
if trials != None:
repeat = trials
for i in range(repeat):
mode = 1
total = 0
curr_str = ""
if i > 0:
output += ", "
for m in mods:
keep = 0
dice = 1
res = 0
# if m is a tuple, then it is a die roll
# m[0] = (keep, num dice)
# m[1] = num faces on the die
if type(m) == tuple:
if m[0] != None:
if m[0][0] != None:
keep = m[0][0]
dice = m[0][1]
size = m[1]
if keep > dice or keep == 0:
keep = dice
if size < 1:
output = "# of sides for die is incorrect: %d" % size
return output
if dice < 1:
output = "# of dice is incorrect: %d" % dice
return output
res = self.roll_dice(keep, dice, size)
curr_str += "%d%s" % (res[0], res[1])
res = res[0]
elif m == "+":
mode = 1
curr_str += "+"
elif m == "-":
mode = -1
curr_str += "-"
else:
res = m
curr_str += str(m)
total += mode * res
if repeat == 1:
if comment != None:
output = "%d %s (%s)" % (total, comment.strip(), curr_str)
else:
output = "%d (%s)" % (total, curr_str)
else:
output += "%d (%s)" % (total, curr_str)
if i == repeat - 1:
if comment != None:
output += " (%s)" % (comment.strip())
return output
def p_roll_r(self, p):
# Chain rolls together.
# General idea I had when creating this grammar: A roll string is a chain
# of modifiers, which may be repeated for a certain number of trials. It can
# have a comment that describes the roll
# Multiple roll strings can be chained with semicolon
'roll : roll ROLLSEP roll'
global output
p[0] = p[1] + "; " + p[3]
output = p[0]
def p_roll(self, p):
# Parse a basic roll string.
'roll : trial modifier comment'
global output
mods = []
if type(p[2]) == list:
mods = p[2]
else:
mods = [p[2]]
p[0] = self.process_roll(p[1], mods, p[3])
output = p[0]
def p_roll_no_trials(self, p):
# Parse a roll string without trials.
'roll : modifier comment'
global output
mods = []
if type(p[1]) == list:
mods = p[1]
else:
mods = [p[1]]
p[0] = self.process_roll(None, mods, p[2])
output = p[0]
def p_comment(self, p):
# Parse a comment.
'''comment : TEXT
|'''
if len(p) == 2:
p[0] = p[1]
else:
p[0] = None
def p_modifier(self, p):
# Parse a modifier on a roll string.
'''modifier : modifier "+" modifier
| modifier "-" modifier'''
# Use append to prevent nested lists (makes dealing with this easier)
if type(p[1]) == list:
p[1].append(p[2])
p[1].append(p[3])
p[0] = p[1]
elif type(p[3]) == list:
p[3].insert(0, p[2])
p[3].insert(0, p[1])
p[0] = p[3]
else:
p[0] = [p[1], p[2], p[3]]
def p_die(self, p):
# Return the left side before the "d", and the number of faces.
'modifier : left NUMBER'
p[0] = (p[1], p[2])
def p_die_num(self, p):
'modifier : NUMBER'
p[0] = p[1]
def p_left(self, p):
# Parse the number of dice we are rolling, and how many we are keeping.
'left : keep dice'
if p[1] == None:
p[0] = [None, p[2]]
else:
p[0] = [p[1], p[2]]
def p_left_all(self, p):
'left : dice'
p[0] = [None, p[1]]
def p_left_e(self, p):
'left :'
p[0] = None
def p_total(self, p):
'trial : NUMBER "#"'
if len(p) > 1:
p[0] = p[1]
else:
p[0] = None
def p_keep(self, p):
'keep : NUMBER "/"'
if p[1] != None:
p[0] = p[1]
else:
p[0] = None
def p_dice(self, p):
'dice : NUMBER "d"'
p[0] = p[1]
def p_dice_one(self, p):
'dice : "d"'
p[0] = 1
def p_error(self, p):
"""Raise ValueError on unparseable strings."""
raise ValueError("Error occurred in parser")
def get_result(self):
global output
return output
def do_roll(self, dicestr):
"""
Roll some dice and get the result (with broken out rolls).
Keyword arguments:
dicestr - format:
N#X/YdS+M label
N#: do the following roll N times (optional)
X/: take the top X rolls of the Y times rolled (optional)
Y : roll the die specified Y times (optional, defaults to 1)
dS: roll a S-sided die
+M: add M to the result (-M for subtraction) (optional)
"""
self.build()
yacc.parse(dicestr)
return self.get_result()

View File

@ -29,6 +29,6 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='dispatcheraction',
name='dispatcher',
field=models.ForeignKey(to='dispatch.Dispatcher'),
field=models.ForeignKey(to='dispatch.Dispatcher', on_delete=models.CASCADE),
),
]

View File

@ -17,6 +17,6 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='dispatcheraction',
name='dispatcher',
field=models.ForeignKey(related_name='actions', to='dispatch.Dispatcher'),
field=models.ForeignKey(related_name='actions', to='dispatch.Dispatcher', on_delete=models.CASCADE),
),
]

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models

View File

@ -36,7 +36,7 @@ class DispatcherAction(models.Model):
(FILE_TYPE, "Write to file"),
)
dispatcher = models.ForeignKey('Dispatcher', related_name='actions')
dispatcher = models.ForeignKey('Dispatcher', related_name='actions', on_delete=models.CASCADE)
type = models.CharField(max_length=16, choices=TYPE_CHOICES)
destination = models.CharField(max_length=200)
include_key = models.BooleanField(default=False)

View File

@ -11,7 +11,7 @@ https://docs.djangoproject.com/en/1.6/ref/settings/
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
from django.core.urlresolvers import reverse_lazy
from django.urls import reverse_lazy
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
@ -57,12 +57,11 @@ INSTALLED_APPS = (
'twitter',
)
MIDDLEWARE_CLASSES = (
MIDDLEWARE = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',

View File

@ -20,5 +20,5 @@ urlpatterns = [
url(r'^races/', include('races.urls')),
url(r'^accounts/', include('registration.backends.default.urls')),
url(r'^admin/', include(admin.site.urls)),
url(r'^admin/', admin.site.urls),
]

View File

@ -26,6 +26,6 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='fact',
name='category',
field=models.ForeignKey(to='facts.FactCategory'),
field=models.ForeignKey(to='facts.FactCategory', on_delete=models.CASCADE),
),
]

View File

@ -1,7 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-02-11 15:00
from __future__ import unicode_literals
from django.db import migrations, models

View File

@ -52,7 +52,7 @@ class Fact(models.Model):
"""Define facts."""
fact = models.TextField()
category = models.ForeignKey(FactCategory)
category = models.ForeignKey(FactCategory, on_delete=models.CASCADE)
nickmask = models.CharField(max_length=200, default='', blank=True)
time = models.DateTimeField(auto_now_add=True)

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
@ -27,7 +25,7 @@ class Migration(migrations.Migration):
('code_reviews_necessary', models.PositiveSmallIntegerField(default=0)),
('code_reviewers', models.TextField(blank=True, default='')),
('code_review_final_merge_assignees', models.TextField(blank=True, default='')),
('gitlab_config', models.ForeignKey(to='gitlab_bot.GitlabConfig', null=True)),
('gitlab_config', models.ForeignKey(to='gitlab_bot.GitlabConfig', null=True, on_delete=models.CASCADE)),
],
),
]

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models

View File

@ -23,7 +23,7 @@ class GitlabProjectConfig(models.Model):
"""Maintain settings for a particular project in GitLab."""
gitlab_config = models.ForeignKey('GitlabConfig', null=True)
gitlab_config = models.ForeignKey('GitlabConfig', null=True, on_delete=models.CASCADE)
project_id = models.CharField(max_length=64, unique=True)
manage_merge_request_code_reviews = models.BooleanField(default=False)

View File

@ -15,7 +15,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='botadmin',
name='user',
field=models.ForeignKey(default=1, to=settings.AUTH_USER_MODEL),
field=models.ForeignKey(default=1, to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE),
preserve_default=False,
),
]

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models

View File

@ -47,7 +47,7 @@ class BotUser(models.Model):
"""Configure bot users, which can do things through the bot and standard Django auth."""
nickmask = models.CharField(max_length=200, unique=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
class Meta:
permissions = (

View File

@ -23,7 +23,7 @@ class Migration(migrations.Migration):
('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')),
('key', models.ForeignKey(to='karma.KarmaKey', on_delete=models.CASCADE)),
],
),
]

View File

@ -115,7 +115,7 @@ class KarmaLogEntryManager(models.Manager):
class KarmaLogEntry(models.Model):
"""Track each karma increment/decrement."""
key = models.ForeignKey('KarmaKey')
key = models.ForeignKey('KarmaKey', on_delete=models.CASCADE)
delta = models.SmallIntegerField()
nickmask = models.CharField(max_length=200, default='', blank=True)
created = models.DateTimeField(auto_now_add=True)

View File

@ -27,7 +27,7 @@ class Migration(migrations.Migration):
('k2', models.CharField(max_length=128)),
('v', models.CharField(max_length=128)),
('count', models.IntegerField(default=0)),
('context', models.ForeignKey(to='markov.MarkovContext')),
('context', models.ForeignKey(to='markov.MarkovContext', on_delete=models.CASCADE)),
],
options={
'permissions': set([('teach_line', 'Can teach lines'), ('import_log_file', 'Can import states from a log file')]),
@ -40,7 +40,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(unique=True, max_length=64)),
('chatter_chance', models.IntegerField(default=0)),
('context', models.ForeignKey(to='markov.MarkovContext')),
('context', models.ForeignKey(to='markov.MarkovContext', on_delete=models.CASCADE)),
],
options={
},

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models

View File

@ -28,7 +28,7 @@ class MarkovTarget(models.Model):
"""Define IRC targets that relate to a context, and can occasionally be talked to."""
name = models.CharField(max_length=200, unique=True)
context = models.ForeignKey(MarkovContext)
context = models.ForeignKey(MarkovContext, on_delete=models.CASCADE)
chatter_chance = models.IntegerField(default=0)
@ -51,7 +51,7 @@ class MarkovState(models.Model):
v = models.CharField(max_length=128)
count = models.IntegerField(default=0)
context = models.ForeignKey(MarkovContext)
context = models.ForeignKey(MarkovContext, on_delete=models.CASCADE)
class Meta:
index_together = [

View File

@ -29,7 +29,7 @@ class Migration(migrations.Migration):
('joined', models.BooleanField(default=False)),
('started', models.BooleanField(default=False)),
('finished', models.BooleanField(default=False)),
('race', models.ForeignKey(to='races.Race')),
('race', models.ForeignKey(to='races.Race', on_delete=models.CASCADE)),
],
options={
},
@ -41,8 +41,8 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('update', models.TextField()),
('event_time', models.DateTimeField(default=django.utils.timezone.now)),
('race', models.ForeignKey(to='races.Race')),
('racer', models.ForeignKey(to='races.Racer')),
('race', models.ForeignKey(to='races.Race', on_delete=models.CASCADE)),
('racer', models.ForeignKey(to='races.Racer', on_delete=models.CASCADE)),
],
options={
'ordering': ['event_time'],

View File

@ -28,7 +28,7 @@ class Racer(models.Model):
"""Track a racer in a race."""
nick = models.CharField(max_length=64)
race = models.ForeignKey(Race)
race = models.ForeignKey(Race, on_delete=models.CASCADE)
joined = models.BooleanField(default=False)
started = models.BooleanField(default=False)
@ -47,8 +47,8 @@ class RaceUpdate(models.Model):
"""Periodic updates for a racer."""
race = models.ForeignKey(Race)
racer = models.ForeignKey(Racer)
race = models.ForeignKey(Race, on_delete=models.CASCADE)
racer = models.ForeignKey(Racer, on_delete=models.CASCADE)
update = models.TextField()
event_time = models.DateTimeField(default=timezone.now)

View File

@ -2,64 +2,68 @@
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile --output-file requirements-dev.txt requirements-dev.in
# pip-compile --output-file=requirements-dev.txt requirements-dev.in
#
appdirs==1.4.0 # via setuptools
astroid==1.4.9 # via pylint, pylint-celery, pylint-flask, pylint-plugin-utils, requirements-detector
click==6.7 # via pip-tools
astroid==2.2.5 # via pylint, pylint-celery, pylint-flask, requirements-detector
certifi==2019.6.16 # via requests
chardet==3.0.4 # via requests
click==7.0 # via pip-tools
django-adminplus==0.5
django-bootstrap3==8.1.0
django-extensions==1.7.6
django-registration-redux==1.4
django==1.10.5
djangorestframework==3.5.3
django-bootstrap3==11.0.0
django-extensions==2.1.9
django-registration-redux==2.6
django==2.2.2
djangorestframework==3.9.4
dodgy==0.1.9 # via prospector
first==2.0.1 # via pip-tools
future==0.16.0 # via parsedatetime
inflect==0.2.5 # via jaraco.itertools
irc==15.0.6
isort==4.2.5 # via pylint
jaraco.classes==1.4 # via jaraco.collections
jaraco.collections==1.5 # via irc, jaraco.text
jaraco.functools==1.15.1 # via irc, jaraco.text
jaraco.itertools==2.0 # via irc
jaraco.logging==1.5 # via irc
jaraco.stream==1.1.1 # via irc
jaraco.text==1.9 # via irc, jaraco.collections
lazy-object-proxy==1.2.2 # via astroid
logilab-common==1.3.0
future==0.17.1 # via parsedatetime
idna==2.8 # via requests
importlib-metadata==0.18 # via irc
inflect==2.1.0 # via jaraco.itertools
irc==17.1
isort==4.3.20 # via pylint
jaraco.classes==2.0 # via jaraco.collections
jaraco.collections==2.0 # via irc
jaraco.functools==2.0 # via irc, jaraco.text, tempora
jaraco.itertools==4.4.2 # via irc
jaraco.logging==2.0 # via irc
jaraco.stream==2.0 # via irc
jaraco.text==3.0 # via irc, jaraco.collections
lazy-object-proxy==1.4.1 # via astroid
logilab-common==1.4.2
mccabe==0.6.1 # via prospector, pylint
more-itertools==2.5.0 # via irc, jaraco.functools, jaraco.itertools
oauthlib==2.0.1 # via requests-oauthlib
packaging==16.8 # via setuptools
parsedatetime==2.2
more-itertools==7.0.0 # via irc, jaraco.functools, jaraco.itertools
oauthlib==3.0.1 # via requests-oauthlib
parsedatetime==2.4
pep8-naming==0.4.1 # via prospector
pip-tools==1.8.0
ply==3.10
prospector==0.12.4
pycodestyle==2.0.0 # via prospector
pydocstyle==1.0.0 # via prospector
pyflakes==1.5.0 # via prospector
pip-tools==3.8.0
ply==3.11
prospector==1.1.6.4
pycodestyle==2.4.0 # via prospector
pydocstyle==3.0.0 # via prospector
pyflakes==1.6.0 # via prospector
pylint-celery==0.3 # via prospector
pylint-common==0.2.2 # via prospector
pylint-django==0.7.2 # via prospector
pylint-flask==0.5 # via prospector
pylint-plugin-utils==0.2.4 # via prospector, pylint-celery, pylint-django, pylint-flask
pylint==1.6.5 # via prospector, pylint-celery, pylint-common, pylint-django, pylint-flask, pylint-plugin-utils
pyparsing==2.1.10 # via packaging
python-dateutil==2.6.0
python-gitlab==0.18
python-mpd2==0.5.5
pytz==2016.10
pyyaml==3.12 # via prospector
requests-oauthlib==0.7.0 # via twython
requests==2.13.0 # via python-gitlab, requests-oauthlib, twython
requirements-detector==0.5.2 # via prospector
pylint-django==2.0.9 # via prospector
pylint-flask==0.6 # via prospector
pylint-plugin-utils==0.5 # via prospector, pylint-celery, pylint-django, pylint-flask
pylint==2.3.1 # via prospector, pylint-celery, pylint-django, pylint-flask, pylint-plugin-utils
python-dateutil==2.8.0
python-gitlab==1.9.0
python-mpd2==1.0.0
pytz==2019.1
pyyaml==5.1.1 # via prospector
requests-oauthlib==1.2.0 # via twython
requests==2.22.0 # via python-gitlab, requests-oauthlib, twython
requirements-detector==0.6 # via prospector
setoptconf==0.2.0 # via prospector
six==1.10.0 # via astroid, django-extensions, irc, jaraco.classes, jaraco.collections, jaraco.itertools, jaraco.logging, jaraco.stream, logilab-common, more-itertools, packaging, pip-tools, pylint, python-dateutil, python-gitlab, setuptools, tempora
tempora==1.6.1 # via irc, jaraco.logging
twython==3.4.0
wrapt==1.10.8 # via astroid
six==1.12.0 # via astroid, django-extensions, jaraco.classes, jaraco.collections, jaraco.itertools, jaraco.logging, jaraco.stream, logilab-common, pip-tools, pydocstyle, python-dateutil, python-gitlab, tempora
snowballstemmer==1.2.1 # via pydocstyle
sqlparse==0.3.0 # via django
tempora==1.14.1 # via irc, jaraco.logging
twython==3.7.0
typed-ast==1.4.0 # via astroid
urllib3==1.25.3 # via requests
wrapt==1.11.2 # via astroid
zipp==0.5.1 # via importlib-metadata
# The following packages are considered to be unsafe in a requirements file:
# setuptools # via logilab-common
# setuptools==41.0.1 # via logilab-common

View File

@ -1,3 +0,0 @@
-r requirements.in
psycopg2

View File

@ -1,36 +0,0 @@
#
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile --output-file requirements-server.txt requirements-server.in
#
django-adminplus==0.5
django-bootstrap3==8.1.0
django-extensions==1.7.6
django-registration-redux==1.4
django==1.10.5
djangorestframework==3.5.3
future==0.16.0 # via parsedatetime
inflect==0.2.5 # via jaraco.itertools
irc==15.0.6
jaraco.classes==1.4 # via jaraco.collections
jaraco.collections==1.5 # via irc, jaraco.text
jaraco.functools==1.15.1 # via irc, jaraco.text
jaraco.itertools==2.0 # via irc
jaraco.logging==1.5 # via irc
jaraco.stream==1.1.1 # via irc
jaraco.text==1.9 # via irc, jaraco.collections
more-itertools==2.5.0 # via irc, jaraco.functools, jaraco.itertools
oauthlib==2.0.1 # via requests-oauthlib
parsedatetime==2.2
ply==3.10
psycopg2==2.6.2
python-dateutil==2.6.0
python-gitlab==0.18
python-mpd2==0.5.5
pytz==2016.10
requests-oauthlib==0.7.0 # via twython
requests==2.13.0 # via python-gitlab, requests-oauthlib, twython
six==1.10.0 # via django-extensions, irc, jaraco.classes, jaraco.collections, jaraco.itertools, jaraco.logging, jaraco.stream, more-itertools, python-dateutil, python-gitlab, tempora
tempora==1.6.1 # via irc, jaraco.logging
twython==3.4.0

View File

@ -2,34 +2,41 @@
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile --output-file requirements.txt requirements.in
# pip-compile --output-file=requirements.txt requirements.in
#
certifi==2019.6.16 # via requests
chardet==3.0.4 # via requests
django-adminplus==0.5
django-bootstrap3==8.1.0
django-extensions==1.7.6
django-registration-redux==1.4
django==1.10.5
djangorestframework==3.5.3
future==0.16.0 # via parsedatetime
inflect==0.2.5 # via jaraco.itertools
irc==15.0.6
jaraco.classes==1.4 # via jaraco.collections
jaraco.collections==1.5 # via irc, jaraco.text
jaraco.functools==1.15.1 # via irc, jaraco.text
jaraco.itertools==2.0 # via irc
jaraco.logging==1.5 # via irc
jaraco.stream==1.1.1 # via irc
jaraco.text==1.9 # via irc, jaraco.collections
more-itertools==2.5.0 # via irc, jaraco.functools, jaraco.itertools
oauthlib==2.0.1 # via requests-oauthlib
parsedatetime==2.2
ply==3.10
python-dateutil==2.6.0
python-gitlab==0.18
python-mpd2==0.5.5
pytz==2016.10
requests-oauthlib==0.7.0 # via twython
requests==2.13.0 # via python-gitlab, requests-oauthlib, twython
six==1.10.0 # via django-extensions, irc, jaraco.classes, jaraco.collections, jaraco.itertools, jaraco.logging, jaraco.stream, more-itertools, python-dateutil, python-gitlab, tempora
tempora==1.6.1 # via irc, jaraco.logging
twython==3.4.0
django-bootstrap3==11.0.0
django-extensions==2.1.9
django-registration-redux==2.6
django==2.2.2
djangorestframework==3.9.4
future==0.17.1 # via parsedatetime
idna==2.8 # via requests
importlib-metadata==0.18 # via irc
inflect==2.1.0 # via jaraco.itertools
irc==17.1
jaraco.classes==2.0 # via jaraco.collections
jaraco.collections==2.0 # via irc
jaraco.functools==2.0 # via irc, jaraco.text, tempora
jaraco.itertools==4.4.2 # via irc
jaraco.logging==2.0 # via irc
jaraco.stream==2.0 # via irc
jaraco.text==3.0 # via irc, jaraco.collections
more-itertools==7.0.0 # via irc, jaraco.functools, jaraco.itertools
oauthlib==3.0.1 # via requests-oauthlib
parsedatetime==2.4
ply==3.11
python-dateutil==2.8.0
python-gitlab==1.9.0
python-mpd2==1.0.0
pytz==2019.1
requests-oauthlib==1.2.0 # via twython
requests==2.22.0 # via python-gitlab, requests-oauthlib, twython
six==1.12.0 # via django-extensions, jaraco.classes, jaraco.collections, jaraco.itertools, jaraco.logging, jaraco.stream, python-dateutil, python-gitlab, tempora
sqlparse==0.3.0 # via django
tempora==1.14.1 # via irc, jaraco.logging
twython==3.7.0
urllib3==1.25.3 # via requests
zipp==0.5.1 # via importlib-metadata

View File

@ -1,7 +1,4 @@
"""Use dr.botzo's Dispatch to send mpd notifications."""
from __future__ import unicode_literals
import argparse
import getpass
import logging

View File

@ -29,7 +29,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('line', models.TextField(default='')),
('time', models.DateTimeField(auto_now_add=True)),
('game', models.ForeignKey(to='storycraft.StorycraftGame')),
('game', models.ForeignKey(to='storycraft.StorycraftGame', on_delete=models.CASCADE)),
],
),
migrations.CreateModel(
@ -38,12 +38,12 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('nick', models.CharField(max_length=64)),
('nickmask', models.CharField(max_length=200)),
('game', models.ForeignKey(to='storycraft.StorycraftGame')),
('game', models.ForeignKey(to='storycraft.StorycraftGame', on_delete=models.CASCADE)),
],
),
migrations.AddField(
model_name='storycraftline',
name='player',
field=models.ForeignKey(to='storycraft.StorycraftPlayer'),
field=models.ForeignKey(to='storycraft.StorycraftPlayer', on_delete=models.CASCADE),
),
]

View File

@ -13,16 +13,16 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='storycraftline',
name='game',
field=models.ForeignKey(related_name='lines', to='storycraft.StorycraftGame'),
field=models.ForeignKey(related_name='lines', to='storycraft.StorycraftGame', on_delete=models.CASCADE),
),
migrations.AlterField(
model_name='storycraftline',
name='player',
field=models.ForeignKey(related_name='lines', to='storycraft.StorycraftPlayer'),
field=models.ForeignKey(related_name='lines', to='storycraft.StorycraftPlayer', on_delete=models.CASCADE),
),
migrations.AlterField(
model_name='storycraftplayer',
name='game',
field=models.ForeignKey(related_name='players', to='storycraft.StorycraftGame'),
field=models.ForeignKey(related_name='players', to='storycraft.StorycraftGame', on_delete=models.CASCADE),
),
]

View File

@ -89,7 +89,7 @@ class StorycraftPlayer(models.Model):
"""Contain entire games of storycraft."""
game = models.ForeignKey('StorycraftGame', related_name='players')
game = models.ForeignKey('StorycraftGame', related_name='players', on_delete=models.CASCADE)
nick = models.CharField(max_length=64)
nickmask = models.CharField(max_length=200)
@ -103,8 +103,8 @@ class StorycraftLine(models.Model):
"""Handle requests to dispatchers and do something with them."""
game = models.ForeignKey('StorycraftGame', related_name='lines')
player = models.ForeignKey('StorycraftPlayer', related_name='lines')
game = models.ForeignKey('StorycraftGame', related_name='lines', on_delete=models.CASCADE)
player = models.ForeignKey('StorycraftPlayer', related_name='lines', on_delete=models.CASCADE)
line = models.TextField(default="")
time = models.DateTimeField(auto_now_add=True)

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
@ -19,6 +17,6 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='twitterclient',
name='mentions_output_channel',
field=models.ForeignKey(blank=True, related_name='mentions_twitter_client', null=True, to='ircbot.IrcChannel', default=None),
field=models.ForeignKey(blank=True, related_name='mentions_twitter_client', null=True, to='ircbot.IrcChannel', default=None, on_delete=models.CASCADE),
),
]

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models

View File

@ -18,7 +18,7 @@ class TwitterClient(models.Model):
oauth_token_secret = models.CharField(max_length=256, default='', blank=True)
mentions_output_channel = models.ForeignKey(IrcChannel, related_name='mentions_twitter_client', default=None,
null=True, blank=True)
null=True, blank=True, on_delete=models.CASCADE)
mentions_since_id = models.PositiveIntegerField(default=1, blank=True)
class Meta: