示例#1
0
def get_deck(id):
    id = str(id)

    url = 'http://api.gracefulstats.com/1.0/deck/view?id=' + id + '&cards=true'
    try:
        con = core.urlopen(url)
    except urllib2.HTTPError as e:
        logging.warning('Someone posted a bad URL: ' + url + ' (%s) ' % str(e))
        return None

    deck = set()

    deckObject = json.loads(con)

    colorIdentity = deckObject['deck']['color_identity']
    name = deckObject['deck']['name']

    deckFormat = deckObject['deck']['format']['name']

    if (deckFormat != 'Commander'):
        raise ValueError("This isn't a commander deck, try to change the type to commander")

    commander = deckObject['deck']['commander']['name']

    for card in deckObject['deck']['cards']:
        deck.add(core.sanitize_cardname(card['name']))

    out_deck = {
        'commander': core.sanitize_cardname(commander),
        'cards': sorted(list(deck)),
        'date': deckObject['deck']['created'],
        'ref': 'gracefulstats'
    }

    return out_deck
示例#2
0
def scrapedeck(url_str):
    logging.debug('attempting to scrape the deckstats url: %s ' % url_str)

    url_fetch = url_str + EXPORT_APPEND

    logging.debug("going to go fetch '%s'" %url_fetch)

    try:
        content = urllib2.urlopen(url_fetch).readlines()
    except:
        raise ValueError("Invalid URL '%s'" % url_str)

    text = content[0][len('//NAME: '):-len('from DeckStats.net') - 2]
    logging.debug('The name of this deck is: %s' % text)

    cards = set()
    sideboard = set()
    for line in content:
        line = line.split('//')[0]
        line = line.split('#')[0]
        line = line.strip()

        if len(line) == 0:
            continue

        if line.startswith('SB:'):
            sideboard.add(core.sanitize_cardname(line.split(' ', 2)[2]))
            line = line[4:]

        if not line[0] in '0123456789':
            raise ValueError("This isn't a valid line of the form '# Card Name': %s " % line)

        cardname = core.sanitize_cardname(line.split(' ', 1)[1])
        

        cards.add(cardname)

    commander = None
    if len(sideboard) == 1:
        cardname = list(sideboard)[0]
        card = core.lookup_card(cardname)

        if card.has_key('supertypes') and 'Legendary' in card['supertypes']:
            commander = list(sideboard)[0]

    if commander is None:
        commander = guess_commander(cards, text)

    out = {}

    out['url'] = url_str
    out['scrapedate'] = str(datetime.datetime.now())
    out['commander'] = commander
    out['cards'] = sorted( cards )
    out['ref'] = 'deckstats'


    return out
示例#3
0
def get_deck(url):
    try:
        # I tack on /?fmt=txt because it gives th list of cards in a somewhat nice
        #   text format. If only there was an API...
        con = core.urlopen(url.rstrip('/') + '/?fmt=txt')
    except urllib2.HTTPError as e:
        # This will happen on 404 or any other error.
        logging.warning("Someone posted a bad URL: " + url + " (%s)" % str(e))
        return None

    deck = set()

    # For each line in the content of the web page....
    for line in con.splitlines():
        line = line.strip()
        if len(line) == 0:
            continue

        if not line[0] in '0123456789':
            continue

        # At this point, the line is not empty and the line starts with a number
        # This, we know, is a card

        # The line is tab delimited like this: "1\tAustere Command\n"
        card = line.split('\t')[1]

        try:
            deck.add(core.sanitize_cardname(card))
        except KeyError:
            pass
        except  ValueError as e:
            logging.warning("Ignored this card because of some sort of bad value")
        

    # Call out to get_tappedout_info to grab the deck info
    cmdr, colors, date = get_tappedout_info(url)

    # if they didn't post the commander, i'm going to try to figure out who it is
    if cmdr is None:
        for card in deck:
            cd = core.lookup_card(card)

            if not cd.has_key('supertypes'):
                continue

            if 'Legendary' in cd['supertypes'] and sorted(list(core.color_identity(card))) == sorted(list(colors)):
                # ok, we've got a legenadry with the colors i think the deck should be. i'll just stop here. 
                cmdr = card
                break
        else:
            logging.warn("there was no legendary creature here.... and none was specified... something f'd up is going on")
            cmdr = 'jedit ojanen'    

    deck.add(cmdr)

    out_deck = { 'commander' : cmdr, 'cards' : sorted(list(deck)), 'date' : date }

    return out_deck
示例#4
0
def get_deck(url):
    try:
        # I tack on /?fmt=txt because it gives th list of cards in a somewhat nice
        #   text format. If only there was an API...
        con = core.urlopen(url.rstrip('/') + '/?fmt=txt')
    except urllib2.HTTPError as e:
        # This will happen on 404 or any other error.
        logging.warning("Someone posted a bad URL: " + url + " (%s)" % str(e))
        return None

    deck = set()

    # For each line in the content of the web page....
    for line in con.splitlines():
        line = line.strip()
        if len(line) == 0:
            continue

        if not line[0] in '0123456789':
            continue

        # At this point, the line is not empty and the line starts with a number
        # This, we know, is a card

        # The line is tab delimited like this: "1\tAustere Command\n"
        card = line.split('\t')[1]

        try:
            deck.add(core.sanitize_cardname(card))
        except KeyError:
            pass
        except  ValueError as e:
            logging.warning("Ignored this card because of some sort of bad value")
        

    # Call out to get_tappedout_info to grab the deck info
    cmdr, colors, date = get_tappedout_info(url)

    # if they didn't post the commander, i'm going to try to figure out who it is
    if cmdr is None:
        for card in deck:
            cd = core.lookup_card(card)

            if not cd.has_key('supertypes'):
                continue

            if 'Legendary' in cd['supertypes'] and sorted(list(core.color_identity(card))) == sorted(list(colors)):
                # ok, we've got a legenadry with the colors i think the deck should be. i'll just stop here. 
                cmdr = card
                break
        else:
            logging.warn("there was no legendary creature here.... and none was specified... something f'd up is going on")
            cmdr = 'jedit ojanen'    

    deck.add(cmdr)

    out_deck = { 'commander' : cmdr, 'cards' : sorted(list(deck)), 'date' : date }

    return out_deck
示例#5
0
    def cmdrdeck(self, commander):
        commander = commander[:50]
        cherrypy.response.headers['Access-Control-Allow-Origin'] = "*"
        cherrypy.response.headers['Content-Type']= 'application/json'


        try:
            cmdr_out = json.loads(self.cmdr(commander))
        except ZeroDivisionError:
            return "Unfortunately, there are not enough decks in my database for '%s', so I can't generate a list" % commander

        colors = [ clr for clr, cnt in cmdr_out['stats']['colors'].items() if cnt > 0 ]

        lands = cmdr_out['stats']['lands']
        nonlands = cmdr_out['stats']['nonlands']
        outdeck = []

        landmap = {'Red' : 'Mountain', 'Blue' : 'Island', 'Black' : 'Swamp', 'White' : 'Plains', 'Green' : 'Forest' }
        lands -= len(colors) 

        for rec_dict in cmdr_out['recs']:
             if 'Land' in rec_dict['card_info']['types'] and lands > 0:
                 outdeck.append(rec_dict)
                 lands -= 1
                 continue

             if (not 'Land' in rec_dict['card_info']['types']) and nonlands > 0:
                 outdeck.append(rec_dict)
                 nonlands -= 1
                 continue
             
        # fill out the lands
        total = sum( cnt for clr, cnt in cmdr_out['stats']['colors'].items() )
        old_lands = lands
        basics = dict(zip(colors, [0] * len(colors)))

        for color, count in cmdr_out['stats']['colors'].items():
            if count == 0 : continue
            num = int( float(count) / total * old_lands ) + 1
            lands -= num
            basics[color] += num

        # tack on a card if we are at 99
        if lands + nonlands > 2:
            logging.warn('deck built had less than 98 cards... thats weird... %d' % len(outdeck))

        while lands + nonlands > 0:
            basics[random.choice(colors)] += 1
            lands -= 1

        out = {}
        out['cards'] = outdeck
        out['basics'] = [ (landmap[color], count) for color, count in basics.items()] 
        out['commander'] = cmdr_out['commander']

        out['stats'] = deckstats.tally([ { 'cards' : [ core.sanitize_cardname(c['card_info']['name']) for c in out['cards'] ] }  ])

        return json.dumps(out)
示例#6
0
def load_cards_from_json(file_path):
    r = core.get_redis()

    writecount = 0

    # Go through each card in the set...
    for card, conts in json.load(open(file_path)).items():
        # If it has names, it's a split card or fuse card or something
        if conts.has_key('names'):
            cardname = core.sanitize_cardname('/'.join(conts['names'])).lower()

            for name in conts['names']:
                r.hset('CARDS_JSON', core.sanitize_cardname(name), json.dumps(conts))

            r.hset('CARDS_JSON', core.sanitize_cardname(cardname), json.dumps(conts)) 
            r.hset('CARDS_JSON', core.sanitize_cardname(cardname.replace('/', ' // ')), json.dumps(conts)) 
            r.hset('CARDS_JSON', core.sanitize_cardname(cardname.replace('/', ' / ')), json.dumps(conts)) 

        else:
            cardname = core.sanitize_cardname(conts['name']).lower()

            r.hset('CARDS_JSON', core.sanitize_cardname(cardname), json.dumps(conts))      

        writecount += 1

    logging.debug('We just wrote ' + str(writecount) + ' card entries into Redis.')
示例#7
0
def load_cards_from_json(file_path):
    r = core.get_redis()

    writecount = 0

    # Go through each card in the set...
    for card, conts in json.load(open(file_path)).items():
        # If it has names, it's a split card or fuse card or something
        if conts.has_key('names'):
            cardname = core.sanitize_cardname('/'.join(conts['names'])).lower()

            for name in conts['names']:
                r.hset('CARDS_JSON', core.sanitize_cardname(name),
                       json.dumps(conts))

            r.hset('CARDS_JSON', core.sanitize_cardname(cardname),
                   json.dumps(conts))
            r.hset('CARDS_JSON',
                   core.sanitize_cardname(cardname.replace('/', ' // ')),
                   json.dumps(conts))
            r.hset('CARDS_JSON',
                   core.sanitize_cardname(cardname.replace('/', ' / ')),
                   json.dumps(conts))

        else:
            cardname = core.sanitize_cardname(conts['name']).lower()

            r.hset('CARDS_JSON', core.sanitize_cardname(cardname),
                   json.dumps(conts))

        writecount += 1

    logging.debug('We just wrote ' + str(writecount) +
                  ' card entries into Redis.')
示例#8
0
def get_commander_stats(commander):

    ds = []
    for deck in core.get_decks(core.color_identity(commander)):
        if deck['commander'] == core.sanitize_cardname(commander):
            ds.append(deck)

    out = tally(ds)
    out['commander'] = core.cap_cardname(commander)

    return out
示例#9
0
def get_commander_stats(commander):

    ds = []
    for deck in core.get_decks(core.color_identity(commander)):
        if deck['commander'] == core.sanitize_cardname(commander):
           ds.append(deck)

    out = tally(ds)
    out['commander'] = core.cap_cardname(commander)

    return out
示例#10
0
def closest_commander(partial_name):
    pn = core.sanitize_cardname(partial_name)

    for cn in COMMANDERS:
        if pn == cn:
            return cn

    for cn in COMMANDERS:
        if cn.startswith(pn):
            return cn

    for cn in COMMANDERS:
        if pn in cn:
            return cn
示例#11
0
文件: api.py 项目: kmg1434/edhrec
def closest_commander(partial_name):
    pn = core.sanitize_cardname(partial_name)

    for cn in COMMANDERS:
        if pn == cn:
            return cn

    for cn in COMMANDERS:
        if cn.startswith(pn):
            return cn

    for cn in COMMANDERS:
        if pn in cn:
            return cn
示例#12
0
def get_deck(id):
    id = str(id)

    url = 'http://api.gracefulstats.com/1.0/deck/view?id=' + id + '&cards=true'
    try:
        con = core.urlopen(url)
    except urllib2.HTTPError as e:
        logging.warning('Someone posted a bad URL: ' + url + ' (%s) ' % str(e))
        return None

    deck = set()

    deckObject = json.loads(con)

    colorIdentity = deckObject['deck']['color_identity']
    name = deckObject['deck']['name']

    deckFormat = deckObject['deck']['format']['name']

    if (deckFormat != 'Commander'):
        raise ValueError(
            "This isn't a commander deck, try to change the type to commander")

    commander = deckObject['deck']['commander']['name']

    for card in deckObject['deck']['cards']:
        deck.add(core.sanitize_cardname(card['name']))

    out_deck = {
        'commander': core.sanitize_cardname(commander),
        'cards': sorted(list(deck)),
        'date': deckObject['deck']['created'],
        'ref': 'gracefulstats'
    }

    return out_deck
示例#13
0
    def cmdr(self, commander):
        commander = commander[:50]

        cherrypy.response.headers['Access-Control-Allow-Origin'] = "*"

        r = core.get_redis()

        ckey = 'CACHE_COMMANDER_' + commander.replace(' ', '_')
        if r.exists(ckey):
            return r.get(ckey)

        commander = core.sanitize_cardname(commander)

        commander = closest_commander(commander)

        colors = core.color_identity(commander)

        decks = [ deck for deck in core.get_decks(colors) if deck['commander'] == commander]

        out = {}
        out['numdecks'] = len(decks)

        cards = {}
        for deck in decks:
            for card in deck['cards']:

                cards[card] = {'count' : 0, 'cardname' : card, 'card_info' : core.lookup_card(card)}

        for deck in decks:
            for card in deck['cards']:
                if card == commander: continue
                if card in ['swamp', 'island', 'mountain', 'forest', 'plains']: continue

                cards[card]['count'] += 1

        out['recs'] = [ pp for pp in sorted(cards.values(), key = (lambda x: -1 * x['count'])) if pp['count'] > 1 and pp['count'] > .1 * len(decks) ]

        out['commander'] = core.cap_cardname(commander)

        r.set(ckey, json.dumps(out), ex=60*60*24*7) # 7 day cache

        return json.dumps(out)
示例#14
0
def scrape_deck(url_str):
    logging.debug('scraping a deck for %s' % url_str)

    content = urllib2.urlopen(url_str).read()
    parsed = bs.BeautifulSoup(content)
    tables = parsed.findAll('table')

    deck = []
    # find the deck
    for t in tables:
        attrs = dict(t.attrs)
        if attrs['class'] != u'deck':
            continue

        data = json.loads(attrs['data-card-list'])

        num_cards = 0
        for card in data['Deck']:
            num_cards += card['Qty']
            deck.append(core.sanitize_cardname(card['CardName']))

        if num_cards < 95 or num_cards > 102:
#            raise ValueError("This deck has %d cards... that's bad." % num_cards)
             pass

        if not core.lookup_card(deck[0]).has_key(u'supertypes') or not u'Legendary' in core.lookup_card(deck[0])[u'supertypes']:
            raise ValueError("The first card in this deck is not legendary.")

        break
    else:
        raise ValueError("I couldn't find a deck in this post")

    out = {}
    out['url'] = url_str
    out['mtgsalvation'] = url_str
    out['date'] = datetime.datetime.now().toordinal()
    out['scrapedate'] = str(datetime.datetime.now())
    out['commander'] = deck[0]
    out['cards'] = sorted(deck)
    out['ref'] = 'mtgsalvation'

    return out
示例#15
0
def get_tappedout_info(url, assume_now = True):

    con = core.urlopen(url).splitlines()

    # GET COMMANDER
    cmdr = None
    for line in con:
        # First, we need to find the commander we're talking about here.
        if line.strip().startswith('<a href="/mtg-card/'):
            cmdr_url = 'http://tappedout.net' + line.split('"')[1]

            # Unfortunately, it's not easy to grab the name from the commander immage
            # We have to go deeper... (the commander's specific page)
            ccon = core.urlopen(cmdr_url).splitlines()

            # Look for the title because that contains the commander name
            for cline in ccon:
                if "<title>" in cline:
                    cmdr = core.sanitize_cardname(cline.split('>')[1].split('(')[0])
                    break

            break
    
    # GET COLORS
    # GET COLORS from the pie on the right
    colors = set([])         
    for line in con:
        if line.strip().startswith('buildColorChart'):
            if 'Green' in line: colors.add('GREEN')
            if 'Red'   in line: colors.add('RED')
            if 'Blue'  in line: colors.add('BLUE')
            if 'Black' in line: colors.add('BLACK')
            if 'White' in line: colors.add('WHITE')

            break

    colors = sorted(list(colors))

    # override pie colors if we have a good commander
    if cmdr is not None:
        try:
            colors = core.color_identity(cmdr)
        except ValueError:
            logging.warn('I have a commander that I don\'t think should exist')
            cmdr = None
            # this will happen if the commander is one that tappedout sees but is not in allcards.json (i.e. if it is new)
            pass

    # GET DATE
    if assume_now: 
        date = datetime.datetime.now().toordinal()  # Note: we use ordinal dates to represent the day.
    else:
        # Go fetch the time. Tappedout makes it hard because they say "3 days ago" or "2 years ago"
        #  and it's got so many options. So, this scraping is pretty fickle but seems to work fine.
        for line in con:
            line = line.lower()
            if '<td>' in line and len(line) < 21 and ('day' in line or 'hour' in line or 'month' in line or 'year' in line):
                num, unit = line.strip()[4:].split('<')[0].split()
                num = int(num)
                unit = unit.strip('s')

                now = datetime.datetime.now().toordinal()

                if unit == 'hour':
                    date = now
                elif unit == 'day':
                    date = now - num
                elif unit == 'month':
                    date = int(now - num * (365. / 12.))
                elif unit == 'year':
                    date = now - num * 365

                break
        else:
            date = now
    
    return cmdr, colors, date
示例#16
0
    # Go through each card in the set...
    for card, conts in json.load(open(file_path)).items():
        # If it has names, it's a split card or fuse card or something
        if conts.has_key('names'):
            cardname = core.sanitize_cardname('/'.join(conts['names'])).lower()

            for name in conts['names']:
                r.hset('CARDS_JSON', core.sanitize_cardname(name), json.dumps(conts))

            r.hset('CARDS_JSON', core.sanitize_cardname(cardname), json.dumps(conts)) 
            r.hset('CARDS_JSON', core.sanitize_cardname(cardname.replace('/', ' // ')), json.dumps(conts)) 
            r.hset('CARDS_JSON', core.sanitize_cardname(cardname.replace('/', ' / ')), json.dumps(conts)) 

        else:
            cardname = core.sanitize_cardname(conts['name']).lower()

            r.hset('CARDS_JSON', core.sanitize_cardname(cardname), json.dumps(conts))      

        writecount += 1

    logging.debug('We just wrote ' + str(writecount) + ' card entries into Redis.')

load_cards_from_json('AllCards.json')

for deck in open('decks_sample.json').readlines():
    core.add_deck(json.loads(deck))

for cc in [ core.sanitize_cardname(c) for c in open('banlist.txt').read().strip().split('\n') ]:
    core.get_redis().sadd('BANNED', cc)
示例#17
0
    def cmdr(self, commander, nolog=False):
        commander = commander[:50]

        cherrypy.response.headers['Content-Type']= 'application/json'
        cherrypy.response.headers['Access-Control-Allow-Origin'] = "*"

        r = core.get_redis()

        commander = core.sanitize_cardname(commander)

        commander = closest_commander(commander)

        r = core.get_redis()

        if not cherrypy.session.has_key('id'):
            cherrypy.session['id'] = ''.join(random.choice('0123456789abcdefghijklmnopqrstuvwxyz') for i in range(8))

        if not nolog:
            r.sadd("SESSION_CMDRSEARCH_" +cherrypy.session['id'], commander)

        ckey = 'CACHE_COMMANDER_' + commander.replace(' ', '_')
        if r.exists(ckey):
            return r.get(ckey)

        colors = core.color_identity(commander)

        decks = [ deck for deck in core.get_decks(colors) if deck['commander'] == commander]

        if len(decks) < 3:
            return json.dumps({'error_code' : 'NOT_ENOUGH_DATA', 'message' : 'There are not enough decks in my database to generate recommendations for %s' % commander})

        out = {}
        out['numdecks'] = len(decks)

        cards = {}
        for deck in decks:
            for card in deck['cards']:

                try:
                    cards[card] = {'count' : 0, 'card_info' : {'name' : core.lookup_card(card)['name'], \
                                                               'types' : core.lookup_card(card)['types'], \
                                                               'colors' : core.lookup_card(card).get('colors', []), \
                                                               'cmc' : core.lookup_card(card).get('cmc', 0) \
                                       } }
                except TypeError:
                    logging.warn("for some reason card %s could not be looked up, ignoring." % card)
                    continue
 

        for deck in decks:
            for card in deck['cards']:
                if card == commander: continue
                if card in ['swamp', 'island', 'mountain', 'forest', 'plains']: continue

                try:
                    cards[card]['count'] += 1
                except KeyError:
                    continue

        #out['recs'] = [ pp for pp in sorted(cards.values(), key = (lambda x: -1 * x['count'])) if pp['count'] > 1 and pp['count'] > .1 * len(decks) ]
        out['recs'] = [ pp for pp in sorted(cards.values(), key = (lambda x: -1 * x['count'])) if pp['count'] > 1 ][:125]

        out['commander'] = core.cap_cardname(commander)

        out['stats'] = deckstats.get_commander_stats(commander)

        # kmeans output for subtopics
        if len(decks) > 15:
            out['archetypes'] = kmeans.kmeans(commander)

        r.set(ckey, json.dumps(out), ex=60*60*24*2) # 2 day cache

        return json.dumps(out)
示例#18
0
文件: api.py 项目: kmg1434/edhrec
    def cmdrdeck(self, commander):
        commander = commander[:50]
        cherrypy.response.headers['Access-Control-Allow-Origin'] = "*"
        cherrypy.response.headers['Content-Type'] = 'application/json'

        try:
            cmdr_out = json.loads(self.cmdr(commander))
        except ZeroDivisionError:
            return "Unfortunately, there are not enough decks in my database for '%s', so I can't generate a list" % commander

        colors = [
            clr for clr, cnt in cmdr_out['stats']['colors'].items() if cnt > 0
        ]

        lands = cmdr_out['stats']['lands']
        nonlands = cmdr_out['stats']['nonlands']
        outdeck = []

        landmap = {
            'Red': 'Mountain',
            'Blue': 'Island',
            'Black': 'Swamp',
            'White': 'Plains',
            'Green': 'Forest'
        }
        lands -= len(colors)

        for rec_dict in cmdr_out['recs']:
            if 'Land' in rec_dict['card_info']['types'] and lands > 0:
                outdeck.append(rec_dict)
                lands -= 1
                continue

            if (not 'Land' in rec_dict['card_info']['types']) and nonlands > 0:
                outdeck.append(rec_dict)
                nonlands -= 1
                continue

        # fill out the lands
        total = sum(cnt for clr, cnt in cmdr_out['stats']['colors'].items())
        old_lands = lands
        basics = dict(zip(colors, [0] * len(colors)))

        for color, count in cmdr_out['stats']['colors'].items():
            if count == 0: continue
            num = int(float(count) / total * old_lands) + 1
            lands -= num
            basics[color] += num

        # tack on a card if we are at 99
        if lands + nonlands > 2:
            logging.warn(
                'deck built had less than 98 cards... thats weird... %d' %
                len(outdeck))

        while lands + nonlands > 0:
            basics[random.choice(colors)] += 1
            lands -= 1

        out = {}
        out['cards'] = outdeck
        out['basics'] = [(landmap[color], count)
                         for color, count in basics.items()]
        out['commander'] = cmdr_out['commander']

        out['stats'] = deckstats.tally([{
            'cards': [
                core.sanitize_cardname(c['card_info']['name'])
                for c in out['cards']
            ]
        }])

        return json.dumps(out)
示例#19
0
文件: api.py 项目: kmg1434/edhrec
    def cmdr(self, commander, nolog=False):
        commander = commander[:50]

        cherrypy.response.headers['Content-Type'] = 'application/json'
        cherrypy.response.headers['Access-Control-Allow-Origin'] = "*"

        r = core.get_redis()

        commander = core.sanitize_cardname(commander)

        commander = closest_commander(commander)

        r = core.get_redis()

        if not cherrypy.session.has_key('id'):
            cherrypy.session['id'] = ''.join(
                random.choice('0123456789abcdefghijklmnopqrstuvwxyz')
                for i in range(8))

        if not nolog:
            r.sadd("SESSION_CMDRSEARCH_" + cherrypy.session['id'], commander)

        ckey = 'CACHE_COMMANDER_' + commander.replace(' ', '_')
        if r.exists(ckey):
            return r.get(ckey)

        colors = core.color_identity(commander)

        decks = [
            deck for deck in core.get_decks(colors)
            if deck['commander'] == commander
        ]

        if len(decks) < 3:
            return json.dumps({
                'error_code':
                'NOT_ENOUGH_DATA',
                'message':
                'There are not enough decks in my database to generate recommendations for %s'
                % commander
            })

        out = {}
        out['numdecks'] = len(decks)

        cards = {}
        for deck in decks:
            for card in deck['cards']:

                try:
                    cards[card] = {'count' : 0, 'card_info' : {'name' : core.lookup_card(card)['name'], \
                                                               'types' : core.lookup_card(card)['types'], \
                                                               'colors' : core.lookup_card(card).get('colors', []), \
                                                               'cmc' : core.lookup_card(card).get('cmc', 0) \
                                       } }
                except TypeError:
                    logging.warn(
                        "for some reason card %s could not be looked up, ignoring."
                        % card)
                    continue

        for deck in decks:
            for card in deck['cards']:
                if card == commander: continue
                if card in ['swamp', 'island', 'mountain', 'forest', 'plains']:
                    continue

                try:
                    cards[card]['count'] += 1
                except KeyError:
                    continue

        #out['recs'] = [ pp for pp in sorted(cards.values(), key = (lambda x: -1 * x['count'])) if pp['count'] > 1 and pp['count'] > .1 * len(decks) ]
        out['recs'] = [
            pp
            for pp in sorted(cards.values(), key=(lambda x: -1 * x['count']))
            if pp['count'] > 1
        ][:125]

        out['commander'] = core.cap_cardname(commander)

        out['stats'] = deckstats.get_commander_stats(commander)

        # kmeans output for subtopics
        if len(decks) > 15:
            out['archetypes'] = kmeans.kmeans(commander)

        r.set(ckey, json.dumps(out), ex=60 * 60 * 24 * 2)  # 2 day cache

        return json.dumps(out)
示例#20
0
文件: api.py 项目: kmg1434/edhrec
import cherrypy
import json
import core
import tappedout
import datetime
import logging
import deckstats
import random
import kmeans
import mtgsalvation
import deckstatscom
import gracefulstats

COMMANDERS = sorted(
    core.sanitize_cardname(cn.decode('utf-8').strip().lower())
    for cn in open('commanders.txt').readlines())


def closest_commander(partial_name):
    pn = core.sanitize_cardname(partial_name)

    for cn in COMMANDERS:
        if pn == cn:
            return cn

    for cn in COMMANDERS:
        if cn.startswith(pn):
            return cn

    for cn in COMMANDERS:
示例#21
0
import cherrypy
import json
import core
import tappedout
import datetime
import logging

logging.basicConfig(filename='api.log')

COMMANDERS = sorted( core.sanitize_cardname(cn.decode('utf-8').strip().lower()) for cn in open('commanders.txt').readlines() )

def closest_commander(partial_name):
    pn = core.sanitize_cardname(partial_name)

    for cn in COMMANDERS:
        if pn == cn:
            return cn

    for cn in COMMANDERS:
        if cn.startswith(pn):
            return cn

    for cn in COMMANDERS:
        if pn in cn:
            return cn


class API(object):
    _cp_config = {'tools.staticdir.on' : True,
                  'tools.staticdir.dir' : '/home/ubuntu/edhrec-site',
                  'tools.staticdir.index' : 'index.html',
示例#22
0
文件: kmeans.py 项目: kmg1434/edhrec
def kmeans(cmdr, k=4):

#    random.seed(52485)

    cmdr = core.sanitize_cardname(cmdr)

    card_to_idx = {}
    idx_to_card = {}
    dims = None

    decks = []

    i = 0
    for deck in core.get_all_decks():
        if deck['commander'] != cmdr:
            continue

        for card in deck['cards']:
            if card in ['island', 'swamp', 'mountain', 'forest', 'plains', cmdr]:
                continue

            lo = core.lookup_card(card)
            if lo is None or 'Land' in lo['types']: continue

            if card_to_idx.has_key(card): continue
           
            card_to_idx[card] = i
            idx_to_card[i] = card
            i += 1

        ll = numpy.zeros(i, dtype=int)

        idxs = []
        for card in deck['cards']:
            try:
                idxs.append(card_to_idx[card])
            except KeyError:
                continue

        for idx in idxs:
            ll[idx] = 1

        decks.append(ll)

    for idx, deck in enumerate(decks):
        decks[idx].resize(i, refcheck=False)

    decks = numpy.array(decks, dtype=int)

    kmc = sklearn.cluster.KMeans(n_clusters=k, init='k-means++', n_init=25, max_iter=300, tol=0.000001, precompute_distances=True, verbose=0, random_state=None, n_jobs=1)

    kmc.fit(decks)

    clusters = [ [] for i in range(k) ]

    out = []

    for idx, deck in enumerate(decks):
        clusters[kmc.labels_[idx]].append([idx_to_card[idx] for idx, v in enumerate(deck) if v == 1])

    for idx, cluster in enumerate(kmc.cluster_centers_):
        outc = {}

        sumdiff = sum([ cluster - other for other in kmc.cluster_centers_ ])
        defining = sorted( enumerate(sumdiff), key=lambda x: x[1], reverse=True)[:12]
        defining = [ {'score' : val, 'card_info' : {'name' : core.lookup_card(idx_to_card[jdx])['name'], \
                                                               'types' : core.lookup_card(idx_to_card[jdx])['types'], \
                                                               'colors' : core.lookup_card(idx_to_card[jdx]).get('colors', []), \
                                                               'cmc' : core.lookup_card(idx_to_card[jdx]).get('cmc', 0) } } for jdx, val in defining ]

        topc = sorted( [(val, idx_to_card[jdx] ) for jdx, val in enumerate(cluster)], reverse=True)[:125]
        topc = [ {'score' : val, 'card_info' : {'name' : core.lookup_card(card)['name'], \
                                                               'types' : core.lookup_card(card)['types'], \
                                                               'colors' : core.lookup_card(card).get('colors', []), \
                                                               'cmc' : core.lookup_card(card).get('cmc', 0) } } for val, card in topc ]

        outc['defining'] = defining
        outc['recs'] = topc

        outc['numdecks'] = len(clusters[idx])
        outc['percentdecks'] = int( len(clusters[idx]) / float(len(decks)) * 100 )
        outc['commander'] = cmdr
        outc['stats'] = deckstats.tally([ {'cards' : d } for d in clusters[idx] ])
        out.append(outc)

    return sorted(out, key=lambda x: x['percentdecks'], reverse=True)
示例#23
0
def get_tappedout_info(url, assume_now = True):

    con = core.urlopen(url).splitlines()

    # GET COMMANDER
    cmdr = None
    for line in con:
        # First, we need to find the commander we're talking about here.
        if line.strip().startswith('<a href="/mtg-card/'):
            cmdr_url = 'http://tappedout.net' + line.split('"')[1]

            # Unfortunately, it's not easy to grab the name from the commander immage
            # We have to go deeper... (the commander's specific page)
            ccon = core.urlopen(cmdr_url).splitlines()

            # Look for the title because that contains the commander name
            for cline in ccon:
                if "<title>" in cline:
                    cmdr = core.sanitize_cardname(cline.split('>')[1].split('(')[0])
                    break

            break
    
    # GET COLORS
    # GET COLORS from the pie on the right
    colors = set([])         
    for line in con:
        if line.strip().startswith('buildColorChart'):
            if 'Green' in line: colors.add('GREEN')
            if 'Red'   in line: colors.add('RED')
            if 'Blue'  in line: colors.add('BLUE')
            if 'Black' in line: colors.add('BLACK')
            if 'White' in line: colors.add('WHITE')

            break

    colors = sorted(list(colors))

    # override pie colors if we have a good commander
    if cmdr is not None:
        try:
            colors = core.color_identity(cmdr)
        except ValueError:
            logging.warn('I have a commander that I don\'t think should exist')
            cmdr = None
            # this will happen if the commander is one that tappedout sees but is not in allcards.json (i.e. if it is new)
            pass

    # GET DATE
    if assume_now: 
        date = datetime.datetime.now().toordinal()  # Note: we use ordinal dates to represent the day.
    else:
        # Go fetch the time. Tappedout makes it hard because they say "3 days ago" or "2 years ago"
        #  and it's got so many options. So, this scraping is pretty fickle but seems to work fine.
        for line in con:
            line = line.lower()
            if '<td>' in line and len(line) < 21 and ('day' in line or 'hour' in line or 'month' in line or 'year' in line):
                num, unit = line.strip()[4:].split('<')[0].split()
                num = int(num)
                unit = unit.strip('s')

                now = datetime.datetime.now().toordinal()

                if unit == 'hour':
                    date = now
                elif unit == 'day':
                    date = now - num
                elif unit == 'month':
                    date = int(now - num * (365. / 12.))
                elif unit == 'year':
                    date = now - num * 365

                break
        else:
            date = now
    
    return cmdr, colors, date
示例#24
0
                   json.dumps(conts))
            r.hset('CARDS_JSON',
                   core.sanitize_cardname(cardname.replace('/', ' // ')),
                   json.dumps(conts))
            r.hset('CARDS_JSON',
                   core.sanitize_cardname(cardname.replace('/', ' / ')),
                   json.dumps(conts))

        else:
            cardname = core.sanitize_cardname(conts['name']).lower()

            r.hset('CARDS_JSON', core.sanitize_cardname(cardname),
                   json.dumps(conts))

        writecount += 1

    logging.debug('We just wrote ' + str(writecount) +
                  ' card entries into Redis.')


load_cards_from_json('AllCards.json')

for deck in open('decks_sample.json').readlines():
    core.add_deck(json.loads(deck))

for cc in [
        core.sanitize_cardname(c)
        for c in open('banlist.txt').read().strip().split('\n')
]:
    core.get_redis().sadd('BANNED', cc)