# -*- coding: utf-8 -*-
import itertools
import re
import six
from okcupyd import util
from okcupyd.question import UserQuestion
[docs]class maps(six.with_metaclass(util.GetAttrGetItem)):
bodytype = util.IndexedREMap(
'rather not say', 'thin', 'overweight', 'skinny', 'average', 'fit',
'athletic', 'jacked', 'a little extra', 'curvy', 'full figured',
'used up'
)
orientation = util.IndexedREMap('straight', 'gay', 'bisexual')
smokes = util.IndexedREMap(
'yes', 'sometimes', 'when drinking', 'trying to quit', 'no'
)
drugs = util.IndexedREMap('never', 'sometimes', 'often',
default=3, offset=0)
drinks = util.IndexedREMap('very often', 'often', 'socially', 'rarely',
'desperately', 'not at all')
ethnicities = util.IndexedREMap(
'asian', 'middle eastern', 'black', 'native american', 'indian',
'pacific islander', ('hispanic', 'latin'), 'white', 'other'
)
job = util.IndexedREMap(
'student', ('art', 'music', 'writing'), ('banking', 'finance'),
'administration', 'technology', 'construction', 'education',
('entertainment', 'media'), 'management', 'hospitality', 'law',
'medicine', 'military', ('politics', 'government'),
('sales', 'marketing'), ('science', 'engineering'),
'transportation', 'unemployed', 'other', 'rather not say',
'retired'
)
status = util.IndexedREMap(
'single', 'seeing someone', 'married', 'in an open relationship'
)
monogamy = util.IndexedREMap('(:?[^\-]monogamous)|(:?^monogamous)',
'non-monogamous')
strictness = util.IndexedREMap('mostly', 'strictly')
# Doesn't want kids is index 6 for some reason.
has_kids = util.IndexedREMap('has a kid', 'has kids', (), (), (),
"doesn't have kids")
wants_kids = util.IndexedREMap('might want', 'wants', "doesn't want")
education_status = util.IndexedREMap('graduated', 'working', 'dropped out',
default=0)
education_level = util.IndexedREMap(
'high ?school', 'two[- ]year college', 'university', 'college',
'masters program', 'law school', 'med school', 'ph.d program',
'space camp'
)
religion = util.IndexedREMap('agnosticism', 'atheism', 'christianity',
'judaism', 'catholicism', 'islam', 'hinduism',
'buddhism', 'other', default=1, offset=2)
seriousness = util.IndexedREMap('very serious', 'somewhat serious',
'not too serious', 'laughing')
sign = util.IndexedREMap(
'aquarius', 'pisces', 'aries', 'taurus', 'gemini', 'cancer', 'leo',
'virgo', 'libra', 'scorpio', 'sagittarius', 'capricorn',
)
importance = util.IndexedREMap(
"doesn't matter", "matters alot", "fun to think about"
)
dogs = util.REMap.from_string_pairs(
(('dislikes dogs', 3), ('has dogs', 1), ('likes dogs', 2)),
default=0
)
cats = util.REMap.from_string_pairs(
(('dislikes cats', 3), ('has cats', 1), ('likes cats', 2)),
default=0
)
language_level = util.IndexedREMap('fluently', 'okay', 'poorly')
diet_strictness = util.IndexedREMap('[Mm]ostly', '[Ss]trictly')
diet = util.IndexedREMap('anything', 'vegetarian', 'vegan',
'kosher', 'halal', 'other')
income = util.IndexedREMap(
'less than $20,000', '$20,000-$30,000', '$30,000-$40,000',
'$40,000-$50,000', '$50,000-$60,000', '$60,000-$70,000',
'$70,000-$80,000', '$80,000-$100,000', '$100,000-$150,000',
'$150,000-$250,000', '$250,000-$500,000', '$500,000-$1,000,000',
'More than $1,000,000'
)
[docs]class MappingUpdater(object):
def __init__(self, mapping):
self.mapping = mapping
def __call__(self, id_name, value):
if isinstance(value, six.string_types):
value = value.lower() # :/
return {id_name: self.mapping[value]}
[docs]class SimpleFilterBuilder(object):
def __init__(self, filter_number, mapping, offset=0):
self.filter_number = filter_number
self.mapping = mapping
[docs] def get_number(self, values):
acc = 0
for value in values:
acc += 2 ** int(self.mapping[value])
return acc
[docs] def get_filter(self, values):
return '{0},{1}'.format(self.filter_number, self.get_number(values))
__call__ = get_filter
[docs]class filters(six.with_metaclass(util.GetAttrGetItem)):
bodytype = SimpleFilterBuilder(30, maps.bodytype)
smokes = SimpleFilterBuilder(11, maps.smokes)
drinks = SimpleFilterBuilder(12, maps.drinks)
drugs = SimpleFilterBuilder(13, maps.drugs)
education_level = SimpleFilterBuilder(19, maps.education_level)
job = SimpleFilterBuilder(15, maps.job)
income = SimpleFilterBuilder(14, maps.income)
religion = SimpleFilterBuilder(8, maps.religion)
monogamy = SimpleFilterBuilder(73, maps.monogamy)
diet = SimpleFilterBuilder(54, maps.diet)
sign = SimpleFilterBuilder(21, maps.sign)
ethnicities = SimpleFilterBuilder(9, maps.ethnicities)
dogs = SimpleFilterBuilder(16, maps.dogs)
cats = SimpleFilterBuilder(17, maps.cats)
imperial_re = re.compile(u"([0-9])['′\u2032] ?([0-9][0-1]?)[\"″\u2033]",
flags=re.UNICODE)
metric_re = re.compile(u"([0-2]\.[0-9]{1,2})m")
sep_replacements = ('\\', '/', '.', '-', ' ', '$', ',', '(', ')')
gentation_to_number = {
'women who like men': '34',
'women': '34',
'men who like women': '17',
'women who like women': '40',
'men who like men': '20',
'men and women who like bi men': '54',
'men and women who like bi women': '57',
'both who like bi men': '54',
'both who like bi women': '57',
'straight women only': '2',
'straight men only': '1',
'gay women only': '8',
'gay men only': '4',
'bi women only': '32',
'bi men only': '16',
'bi men and women': '48',
'everybody': '63',
'girls who like guys': '34',
'guys who like girls': '17',
'girls who like girls': '40',
'guys who like guys': '20',
'guys and girls who like bi guys': '54',
'guys and girls who like bi girls': '57',
'both who like bi guys': '54',
'both who like bi girls': '57',
'straight girls only': '2',
'straight guys only': '1',
'gay girls only': '8',
'gay guys only': '4',
'bi girls only': '32',
'bi guys only': '16',
'bi guys and girls': '48',
'everybody': '63',
'': '63'
}
looking_for_re_numbers = ((re.compile("[Ff]riends"), 1),
(re.compile("[Ll]ong.*[Dd]ating"), 2),
(re.compile("[Ss]hort.*[Dd]ating"), 3),
(re.compile("[Ss]ex"), 6))
[docs]def inches_to_centimeters(inches):
return inches * 2.54
[docs]def get_height_filter(height_min=None, height_max=None):
min_int = 0
max_int = 99999
if isinstance(height_min, six.string_types):
min_int = 100 * parse_height_string(height_min)
elif isinstance(height_min, int):
min_int = 100 * inches_to_centimeters(height_min)
if isinstance(height_max, six.string_types):
max_int = 100 * parse_height_string(height_max)
elif isinstance(height_max, int):
max_int = 100 * inches_to_centimeters(height_max)
return '10,{0},{1}'.format(min_int, max_int)
[docs]def parse_height_string(height_string):
if not height_string:
return 0
match = imperial_re.search(height_string)
if match:
return int(round(inches_to_centimeters(int(match.group(1)) * 12 +
int(match.group(2)))))
match = metric_re.search(height_string)
if match:
meters = float(match.group(1))
centimeters = meters * 100
return int(round(centimeters))
else:
raise ValueError("The provided height did not match any of "
"the accepted formats.")
[docs]def get_kids_filter(has_kids=(), wants_kids=()):
return '18,{0}'.format(get_kids_int(has_kids, wants_kids))
[docs]def get_kids_int(has_kids, wants_kids):
has_kids = util.makelist(has_kids or ())
wants_kids = util.makelist(wants_kids)
wants_was_unknown = False
has_was_unknown = False
kids_int = 0
has_kids_ints = [maps.has_kids[matchable] for matchable in has_kids]
if len(has_kids_ints) == 0 or sum(has_kids_ints) == 0:
has_kids_ints = list(maps.has_kids.values()) + [0]
has_was_unknown = True
wants_kids_ints = [maps.wants_kids[matchable] for matchable in wants_kids]
if len(wants_kids_ints) == 0 or sum(wants_kids_ints) == 0:
wants_kids_ints = list(maps.wants_kids.values()) + [0]
wants_was_unknown = True
wants_kids_ints = [v * 8 for v in wants_kids_ints]
kids_int += sum(2 ** (hk_int + wk_int)
for hk_int, wk_int
in itertools.product(has_kids_ints, wants_kids_ints))
if not (wants_was_unknown or has_was_unknown):
# ... Why? Apparently this is needed...
kids_int = subtract_has_kids_exponents(kids_int)
# This is super crazy and gross. I'm not really sure why they do this.
if not wants_was_unknown and has_was_unknown:
if 3 in [maps.wants_kids[matchable] for matchable in wants_kids]:
kids_int += 48
return kids_int
[docs]def subtract_has_kids_exponents(value):
has_kids_values = maps.has_kids.values()
for exponent in yield_exponents_of_two(value):
if exponent in has_kids_values:
value -= exponent
return value
[docs]def yield_exponents_of_two(value):
power = 0
while value > 0:
if value & 0b1:
yield power
power += 1
value >>= 1
[docs]def get_language_query(language):
return '22,{0}'.format(language_map[language.lower()])
hour = 60*60
join_date_string_to_int = {
'hour': hour,
'day': hour*24,
'week': hour*24*7,
'month': hour*24*30,
'year': 365*24*hour
}
[docs]def get_join_date_filter(join_date):
date_int = 0
if isinstance(join_date, str):
if join_date in join_date_string_to_int:
date_int = join_date_string_to_int[join_date]
else:
date_int = int(join_date)
return '6,{0}'.format(date_int)
[docs]def get_question_filter(question, question_answers=None):
question_id = question
if isinstance(question, UserQuestion):
question_id = question.id
if question_answers is None:
question_answers = [answer_option.id
for answer_option in question.answer_options
if answer_option.is_match]
answers_int = 0
for answer_int in question_answers:
answers_int += 2 ** answer_int
return '65,{0},{1}'.format(question_id, answers_int)
language_map_2 = {
'afrikaans': 'af',
'albanian': 'sq',
'arabic': 'ar',
'armenian': 'hy',
'basque': 'eu',
'belarusan': 'be',
'bengali': 'bn',
'breton': 'br',
'bulgarian': 'bg',
'catalan': 'ca',
'cebuano': '11',
'chechen': 'ce',
'chinese': 'zh',
'c++': '71',
'croatian': 'hr',
'czech': 'cs',
'danish': 'da',
'dutch': 'nl',
'english': 'en',
'esperanto': 'eo',
'estonian': 'et',
'farsi': '20',
'finnish': 'fi',
'french': 'fr',
'frisian': '23',
'georgian': '24',
'german': 'de',
'greek': 'el',
'gujarati': 'gu',
'ancient greek': '27',
'hawaiian': '28',
'hebrew': 'he',
'hindi': 'hi',
'hungarian': 'hu',
'icelandic': 'is',
'ilongo': '32',
'indonesian': 'id',
'irish': 'ga',
'italian': 'it',
'japanese': 'ja',
'khmer': '37',
'korean': 'ko',
'latin': 'la',
'latvian': 'lv',
'lisp': '72',
'lithuanian': 'lt',
'malay': 'ms',
'maori': 'mi',
'mongolian': 'mn',
'norwegian': 'no',
'occitan': 'oc',
'other': '73',
'persian': 'fa',
'polish': 'pl',
'portuguese': 'pt',
'romanian': 'ro',
'rotuman': '51',
'russian': 'ru',
'sanskrit': 'sa',
'sardinian': '54',
'serbian': 'sr',
'sign language': '1',
'slovak': 'sk',
'slovenian': 'sl',
'spanish': 'es',
'swahili': 'sw',
'swedish': 'sv',
'tagalog': 'tl',
'tamil': 'ta',
'thai': 'th',
'tibetan': 'bo',
'turkish': 'tr',
'ukrainian': 'uk',
'urdu': 'ur',
'vietnamese': 'vi',
'welsh': 'cy',
'yiddish': 'yi'
}
language_map = {
'afrikaans': '2',
'albanian': '3',
'ancient greek': '27',
'arabic': '4',
'armenian': '76',
'basque': '5',
'belarusan': '6',
'bengali': '7',
'breton': '8',
'bulgarian': '9',
'c++': '71',
'catalan': '10',
'cebuano': '11',
'chechen': '12',
'chinese': '13',
'croatian': '14',
'czech': '15',
'danish': '16',
'dutch': '17',
'english': '74',
'esperanto': '18',
'estonian': '19',
'farsi': '20',
'finnish': '21',
'french': '22',
'frisian': '23',
'georgian': '24',
'german': '25',
'greek': '26',
'gujarati': '77',
'hawaiian': '28',
'hebrew': '29',
'hindi': '75',
'hungarian': '30',
'icelandic': '31',
'ilongo': '32',
'indonesian': '33',
'irish': '34',
'italian': '35',
'japanese': '36',
'khmer': '37',
'korean': '38',
'lisp': '72',
'latin': '39',
'latvian': '40',
'lithuanian': '41',
'malay': '42',
'maori': '43',
'mongolian': '44',
'norwegian': '45',
'occitan': '46',
'other': '73',
'persian': '47',
'polish': '48',
'portuguese': '49',
'romanian': '50',
'rotuman': '51',
'russian': '52',
'sanskrit': '53',
'sardinian': '54',
'serbian': '55',
'sign language': '1',
'slovak': '56',
'slovenian': '57',
'spanish': '58',
'swahili': '59',
'swedish': '60',
'tagalog': '61',
'tamil': '62',
'thai': '63',
'tibetan': '64',
'turkish': '65',
'ukrainian': '66',
'urdu': '67',
'vietnamese': '68',
'welsh': '69',
'yiddish': '70',
}