Skip to content

Commit

Permalink
Flask webapp with dirty horoscope support.
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelsproul committed Feb 13, 2014
1 parent a9ed2cc commit 904630a
Show file tree
Hide file tree
Showing 15 changed files with 382 additions and 346 deletions.
6 changes: 4 additions & 2 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Horoscope Generator
===================
Bullshit: Horoscope Generator
=============================

Generate random horoscopes!

Adjust the silliness by adding words to the word list.

Zen Koan generator coming soon.
11 changes: 11 additions & 0 deletions bin/horoscope
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env python3

import argparse

from bullshit import horoscope

if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--dirty", action='store_true', help="Enable offensive horoscopes.")
args = parser.parse_args()
print(horoscope.generate(dirty=args.dirty))
Empty file added bullshit/__init__.py
Empty file.
52 changes: 52 additions & 0 deletions bullshit/common/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Bullshit Generator, by Michael Sproul."""

import random

vowels = {"a", "e", "i", "o", "u"}

def choose_uniq(exclude, *args):
"""Choose a unique random item from a variable number of lists."""
item = choose_from(*args)
while item in exclude:
item = choose_from(*args)
return item


def choose_from(*args):
"""Choose a random item from a variable number of lists."""
num_words = sum([len(x) for x in args])

# Take the ith item from the lists
i = random.randint(0, num_words - 1)
for (j, x) in enumerate(args):
if i < len(x):
return x[i]
i -= len(x)


def sentence_case(sentence, exciting=False):
"""Capitalise the first letter of the sentence and add a full stop."""
sentence = sentence[0].upper() + sentence[1:]

if sentence[-1] in {'.', '!', '?'}:
return sentence
elif exciting:
return sentence + "!"
else:
return sentence + "."


def ing_to_ed(word):
"""Convert `ing' endings to `ed' endings."""
if word[-3:] == "ing":
return (word[:-3] + "ed")
else:
return word


def an(word):
"""Prefix with 'a' or 'an', as appropriate."""
if word[0] in vowels:
return "an %s" % word
else:
return "a %s" % word
2 changes: 2 additions & 0 deletions bullshit/horoscope/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .horoscope import generate
__all__ = ["generate"]
30 changes: 30 additions & 0 deletions bullshit/horoscope/dirtywords.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
avoid_list = [ "poor people",
"mamallian flesh",
"white people",
"black people",
"foreigners",
"oral sex",
"bullshit artists",
"cats that look like Jesus",
"anarcho-syndicalists",
"athesists",
"Christians",
"Mormons",
"hard drugs",
"bogans"
]

strange_people = [ "a talking dog",
"a tiny horse",
"Jesus",
"Muhammad (peace be upon him)",
"God",
"Allah",
"Daniel Radcliffe",
"Obama",
"Tony Abbott",
"a sexual tiger",
"a clone of yourself",
"Alan Jones",
"Richard Dawkins"
]
232 changes: 232 additions & 0 deletions bullshit/horoscope/horoscope.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
"""Random Horoscope Generator, by Michael Sproul."""

import random

from datetime import date, timedelta

from ..common import choose_from, choose_uniq, sentence_case, ing_to_ed, an
from .wordlist import wordlist

def generate(dirty=False):
"""Generate a three to four sentence horoscope."""
# Pick a mood (usually positive)
mood = "good" if random.random() <= 0.8 else "bad"

discussion_s = choose_from([relationship_s, encounter_s])
sentences = [feeling_statement_s, cosmic_implication_s, warning_s, discussion_s]

# Select 2 or 3 sentences
k = random.randint(2, 3)
sentences = random.sample(sentences, k)
final_text = " ".join([sentence(mood, dirty) for sentence in sentences])

# Optionally add a date prediction
if random.random() <= 0.5 and k == 2:
final_text += " " + date_prediction_s(mood, dirty)

return final_text


def relationship_s(mood, dirty):
"""Generate a sentence about a relationship."""
if mood == "good":
verb = "strengthened"
talk = "discussion"
else:
verb = "strained"
talk = "argument"

# Wordlists
familiar_people = wordlist("familiar_people", dirty)
conversation_topics = wordlist("conversation_topics", dirty)

person = choose_from(familiar_people)
topic = choose_from(conversation_topics)
s = "Your relationship with %s may be %s " % (person, verb)
s += "as the result of %s about %s" % (an(talk), topic)

return sentence_case(s)


def encounter_s(mood, dirty):
"""Generate a few sentences about a meeting with another person."""
# Sentence 1: The meeting
familiar_people = wordlist("familiar_people", dirty)
strange_people = wordlist("strange_people", dirty)
locations = wordlist("locations", dirty)

person = choose_from(familiar_people, strange_people)
location = choose_from(locations)
preposition = location[0]
location = location[1]
s1 = "You may meet %s %s %s." % (person, preposition, location)

# Sentence 2: The discussion
discussions = wordlist("neutral_discussions", dirty)
discussions += wordlist("_discussions", dirty, prefix=mood)
feeling_nouns = wordlist("_feeling_nouns", dirty, prefix=mood)
emotive_nouns = wordlist("_emotive_nouns", dirty, prefix=mood)
conversation_topics = wordlist("conversation_topics", dirty)

discussion = choose_from(discussions)
if random.random() <= 0.5:
feeling = choose_from(feeling_nouns)
feeling = "feelings of %s" % feeling
else:
feeling = choose_from(emotive_nouns)
topic = choose_from(conversation_topics)

s2 = "%s about %s may lead to %s." % (an(discussion), topic, feeling)
s2 = sentence_case(s2)
return "%s %s" % (s1, s2)


def date_prediction_s(mood, dirty):
"""Generate a random prediction sentence containing a date."""
days_in_future = random.randint(2, 8)
significant_day = date.today() + timedelta(days=days_in_future)
month = significant_day.strftime("%B")
day = significant_day.strftime("%d").lstrip('0')

r = random.random()

if r <= 0.5:
s = "%s %s will be an important day for you" % (month, day)
elif r <= 0.8:
s = "Interesting things await you on %s %s" % (month, day)
else:
s = "The events of %s %s have the potential to change your life." % (month, day)

return sentence_case(s)


def feeling_statement_s(mood, dirty):
"""Generate a sentence that asserts a mood-based feeling."""
adjectives = wordlist("_feeling_adjs", dirty, prefix=mood)
degrees = wordlist("neutral_degrees", dirty) + wordlist("_degrees", dirty, prefix=mood)

adj = choose_from(adjectives)
adj = ing_to_ed(adj)
degree = choose_from(degrees)
ending = positive_intensifier if mood == "good" else consolation
exciting = (mood == "good" and random.random() <= 0.5)
are = random.choice([" are", "'re"])
s = "You%s feeling %s %s" % (are, degree, adj)
s += ending(dirty)
return sentence_case(s, exciting)


def positive_intensifier(dirty):
"""Extend a statement of positive feelings."""
r = random.random()

# Increase the probability of not giving a fuck as appropriate.
dirty_factor = 2 if dirty else 1

if r <= (0.5/dirty_factor):
verb = random.choice(["say", "do"])
return ", and there's nothing anyone can %s to stop you" % verb
elif r <= (0.95/dirty_factor):
return ", and you don't care who knows it"
else:
return ", and you don't give a fuck"


def consolation(dirty):
"""Provide a consolation for feeling bad."""
r = random.random()

if r <= 0.6:
when = random.choice(["shortly", "soon", "in due time"])
return ", but don't worry, everything will improve %s" % when
elif r <= 0.9:
return ", perhaps you need a change in your life?"
else:
return "..."


def warning_s(mood, dirty):
r = random.random()
avoid_list = wordlist("avoid_list", dirty)
bad_thing = random.choice(avoid_list)

if r <= 0.27:
s = "You would be well advised to avoid %s" % bad_thing
elif r <= 0.54:
s = "Avoid %s at all costs" % bad_thing
elif r <= 0.81:
s = "Steer clear of %s for a stress-free week" % bad_thing
else:
also_bad = choose_uniq({bad_thing}, avoid_list)
s = "For a peaceful week, avoid %s and %s" % (bad_thing, also_bad)

return sentence_case(s)


def cosmic_implication_s(mood, dirty):
"""Generate a sentence about the influence of a cosmic event."""
c_event = cosmic_event(dirty)
prediction_verbs = wordlist("prediction_verbs", dirty)
verb = choose_from(prediction_verbs)

# Bad mood = End of good, or start of bad
# Good mood = End of bad, or start of good
r = random.random()
beginnings = wordlist("beginnings", dirty)
endings = wordlist("endings", dirty)
if mood == 'bad' and r <= 0.5:
junction = choose_from(beginnings)
e_event = emotive_event('bad', dirty)
elif mood == 'bad':
junction = choose_from(endings)
e_event = emotive_event('good', dirty)
elif mood == 'good' and r <= 0.5:
junction = choose_from(beginnings)
e_event = emotive_event('good', dirty)
else:
junction = choose_from(endings)
e_event = emotive_event('bad', dirty)

s = "%s %s the %s of %s" % (c_event, verb, junction, e_event)
return sentence_case(s)


def cosmic_event(dirty):
r = random.random()

planets = wordlist("planets", dirty)
stars = wordlist("stars", dirty)
wanky_events = wordlist("wanky_events", dirty)
aspects = wordlist("aspects", dirty)

if r <= 0.25:
return random.choice(planets) + " in retrograde"
elif r <= 0.5:
c_event = "the " + random.choice(["waxing", "waning"])
c_event += " of " + choose_from(planets, ["the moon"], stars)
return c_event
elif r <= 0.6:
return "the " + random.choice(["New", "Full"]) + " Moon"
elif r <= 0.75:
return random.choice(wanky_events)
else:
first = choose_from(planets, stars, ["Moon"])
second = choose_uniq({first}, planets, stars, ["Moon"])
return "The %s/%s %s" % (first, second, choose_from(aspects))


def emotive_event(mood, dirty):
"""Generate a sentence about a prolonged emotion."""
feeling_adjs = wordlist("_feeling_adjs", dirty, prefix=mood)
emotive_adjs = wordlist("_emotive_adjs", dirty, prefix=mood)
feeling_nouns = wordlist("_feeling_nouns", dirty, prefix=mood)
emotive_nouns = wordlist("_emotive_nouns", dirty, prefix=mood)
time_periods = wordlist("time_periods", dirty)
time_period = random.choice(time_periods)

if random.random() <= 0.5:
adj = choose_from(feeling_adjs, emotive_adjs)
return "%s %s" % (adj, time_period)
else:
noun = choose_from(feeling_nouns, emotive_nouns)
return "%s of %s" % (time_period, noun)
33 changes: 32 additions & 1 deletion wordlist.py → bullshit/horoscope/wordlist.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
import sys

from . import dirtywords

this_module = sys.modules[__name__]

def _setup():
"""Create lists in this module with both "clean" & dirty words."""
for name in dir(dirtywords):
if name[:1] == "_":
continue

named_object = getattr(dirtywords, name)
if isinstance(named_object, list):
clean_list = getattr(this_module, name)
dirty_list = clean_list + named_object
dirty_name = "dirty_" + name
setattr(this_module, dirty_name, dirty_list)


def wordlist(name, dirty=False, prefix=""):
"""Get a word list by name, with optional filth."""
name = prefix + name
dirty_name = "dirty_" + name
if dirty and hasattr(this_module, dirty_name):
return getattr(this_module, dirty_name)
return getattr(this_module, name)


# Astrological words
planets = ["Mercury", "Venus", "Mars", "Jupiter", "Saturn", "Uranus",
"Neptune", "Pluto"]
Expand Down Expand Up @@ -46,7 +75,6 @@
bad_emotive_nouns = ["bad luck", "misfortune", "déjà vu"]

# Misc
vowels = {'a', 'e', 'i', 'o', 'u'}
prediction_verbs = ["heralds", "marks", "foreshadows", "signifies"]

# You would be well advised to avoid...
Expand Down Expand Up @@ -163,3 +191,6 @@
"your feelings",
"their work"
]

# Run the setup function once all names have been loaded
_setup()
Empty file added bullshit/webapp/__init__.py
Empty file.
File renamed without changes
Loading

0 comments on commit 904630a

Please sign in to comment.