deadman36g
9/26/2017 - 2:02 AM

vinecredits_0.0.7_(python2)

Fetches credits from Comic Vine in a friendly format.

#!/usr/bin/env python
# coding=utf-8
"""
Fetches credits from Comic Vine in a friendly format.
Version 0.0.7 (Python2)
Before running, change the API variable below (line 13)
"""
import datetime, json, re, sys
import requests # Use pip
#import pprint # Just for debugging


API ="" # Paste your API between the quotes

def fetchVine(urlStart,query):
    "Grabs info from ComicVine."
    # Download the JSON data.
    url = urlStart + query
    response = requests.get(url)
    response.raise_for_status()
    # Load JSON data into a Python variable.
    jsonData = json.loads(response.text)
    return jsonData

def flatName(the_name):
    "Regularize the_name for matching purposes"
    the_name = the_name.lower()

    # strip articles
    if the_name.startswith('the '):
        the_name = the_name[4:]
    elif the_name.startswith('a '):
        the_name = the_name[2:]

    # drop ambiguous characters
    return_name = ''
    for theChar in the_name:
        if theChar not in u'-–—:,!?\'\"':
            return_name += theChar

    l = return_name.split('  ') # in case we've left two spaces behind
    return_name = ' '.join(l)
    return return_name

def issueLoop(vol_ID,the_pub):
    "Continually prompts for issue numbers and returns info"
    global API
    while True:
        print('** Enter an issue number (or Return to start over): ') # input next line
        issue_no = raw_input() # makes the division b/t multiple issues easier to spot
        if issue_no:
            url_start ='http://www.comicvine.com/api/issues/'
            query = '?api_key=%s' % API
            query += '&format=json&field_list=api_detail_url'
            query += '&filter=volume:%s,issue_number:%s' % (vol_ID,issue_no)
            the_data = fetchVine(url_start,query)

            if the_data['error'] != 'OK':
                print('CV Returned Error: ' + the_data['error'])
                return

            if not the_data['results']:
                print('No results for issue %s!' % issue_no)
                continue

            iss_result = the_data['results']
            url_start = iss_result[0]['api_detail_url']
            query = '?api_key=%s' % API
            query += '&format=json&field_list=character_credits,team_credits'
            query += ',person_credits,description,store_date,name'
            query += '&filter=id:%s,issue_number:%s' % (vol_ID,issue_no)
            the_data = fetchVine(url_start,query)

            if the_data['error'] != 'OK':
                print('CV Returned Error: ' + the_data['error'])
                return

            if the_data['number_of_total_results'] != 1: # Haven't actually seen this
                print('Error: Too many results from CV!')
                return
            
            print('>> Issue %s PUBLISHER:\n%s' % (issue_no,the_pub)) # print pub.
            the_results = the_data['results']
            characters = the_results['character_credits'] # print characters
            characterOutput(issue_no,characters)
            teams = the_results['team_credits'] # print teams
            teamOutput(issue_no,teams)
            people = the_results['person_credits'] # print creator credits
            peopleOutput(issue_no,people)
            description = the_results['description'] # print description
            descriptionOutput(issue_no,description,people)
            date = the_results['store_date'] # Get date
            dateOutput(issue_no,date)
        else:
            return

def characterOutput(issue_no,characters):
    "Gets and outputs list of characters"
    character_list = []
    for character in characters:
        character_name = character['name']
        character_list.append(character_name)
    print('>> Issue %s CHARACTERS:' % issue_no)
    if not character_list:
        print('None found!')
    else:
        character_print = ", ".join(character_list)
        print(character_print)
    return

def teamOutput(issue_no,teams):
    "Gets and outputs list of teams"
    team_list = []
    for team in teams:
        team_name = team['name']
        team_list.append(team_name)
    print('>> Issue %s TEAMS:' % issue_no)
    if not team_list:
        print('None found!')
    else:
        team_print = ", ".join(team_list)
        print(team_print)
    return

def peopleOutput(issue_no,people):
    "Gets and outputs artists and writers"
    writer_list, artist_list = [], []
    for person in people:
        person_role, person_name = person['role'], person['name']
        if ('writer' in person_role):
            writer_list.append(person_name)
        if ('artist' in person_role):
            artist_list.append(person_name)
        elif ('penciler' in person_role):
            artist_list.append(person_name)
        elif ('inker' in person_role):
            artist_list.append(person_name)
        elif ('colorist' in person_role):
            artist_list.append(person_name)
        elif ('cover' in person_role): # TODO make this optional?
            artist_list.append(person_name)
    print('>> Issue %s WRITERS:' % issue_no)
    if not writer_list:
        print('None found!')
    else:
        writer_print = ", ".join(writer_list)
        print(writer_print)
    print('>> Issue %s ARTISTS:' % issue_no)
    if not artist_list:
        print('None found!')
    else:
        artist_print = ", ".join(artist_list)
        print(artist_print)
    return

def peopleOutput(issue_no,people):
    "Gets and outputs artists and writers"
    writer_list, artist_list = [], []
    for person in people:
        person_role, person_name = person['role'], person['name']
        if ('writer' in person_role):
            writer_list.append(person_name)
        # could be writer/artist
        if ('artist' in person_role):
            artist_list.append(person_name)
        elif ('penciler' in person_role):
            artist_list.append(person_name)
        elif ('inker' in person_role):
            artist_list.append(person_name)
        elif ('colorist' in person_role):
            artist_list.append(person_name)
        elif ('cover' in person_role): # TODO make this optional?
            artist_list.append(person_name)
    print('>> Issue %s WRITERS:' % issue_no)
    if not writer_list:
        print('None found!')
    else:
        writer_print = ", ".join(writer_list)
        print(writer_print)
    print('>> Issue %s ARTISTS:' % issue_no)
    if not artist_list:
        print('None found!')
    else:
        artist_print = ", ".join(artist_list)
        print(artist_print)
    return

def descriptionOutput(issue_no,description,people):
    "Gets and outputs description"
    regular_CA = ''
    if description:
        description_list = description.split('<h4>List of covers and their creators:</h4>')
        description = description_list[0]
        
        # try to ID regular cover artist
        if len(description_list) > 1:
            dl_1 = description_list[1]
            dl_1_s = dl_1.split('<tbody>') # should be in the first line of table body
            table_body = dl_1_s[1]
            table_body = table_body.lstrip('<trd>')
            table_body = table_body.rstrip('</tdrbodyale>')
            table_list = table_body.split('</td></tr><tr><td>')
            reg_line_list = table_list[0].split('</td><td>')
            regular_CA = reg_line_list[2]

        # get description - Solicit description usually in italics
        italics = re.compile('<em>(.*)</em>')
        mo = italics.search(description)
        if (mo and mo.group(1)):
            description = mo.group(1)
            description = re.sub('<[^<]+?>', '', description) # Hack to remove HTML
            description = '[quote]%s[/quote]' % description # quotes for solicit
        else: # use what we got
            description = re.sub('<[^<]+?>', '', description) # Hack to remove HTML
    else:
        description = 'None found!'

    print('>> Issue %s DESCRIPTION:' % issue_no)
    print(description)

    if people:
        creator_list = []
        for person in people:
            person_role, person_name = person['role'], person['name']
            if person_name in regular_CA:
                person_role = re.sub('cover','regular cover',person_role)
            if ('writer' in person_role):
                creator_list.append('%s: %s' % (person_name,person_role))
            elif ('artist' in person_role):
                creator_list.append('%s: %s' % (person_name,person_role))
            elif ('penciler' in person_role):
                creator_list.append('%s: %s' % (person_name,person_role))
            elif ('inker' in person_role):
                creator_list.append('%s: %s' % (person_name,person_role))
            elif ('colorist' in person_role):
                creator_list.append('%s: %s' % (person_name,person_role))
            elif ('cover' in person_role): # TODO make optional?
                creator_list.append('%s: %s' % (person_name,person_role))
        if creator_list:
            creator_list.sort()
            creators = '\n[*]'.join(creator_list)
            print('Creators: \n[*]%s' % creators)

def dateOutput(issue_no,date):
    "Gets, formats, and outputs date"
    if not date:
        date = 'None found!'
    else:
        do = datetime.datetime.strptime(date, '%Y-%m-%d').date()
        date = do.strftime('%d %b %Y')
    print('>> Issue %s store date:' % issue_no)
    print(date)
    return

def getVolumes(which_volume):
    url_start ='http://www.comicvine.com/api/volumes/'
    query = '?api_key=' + API
    query += '&format=json&field_list=name,start_year,id,count_of_issues,publisher'
    query += '&filter=name:%s' % which_volume
    """
    #Experimenting with dictionary query
    #Format: 'http://www.comicvine.com/api/volumes/?field_list=name&field_list=start_year&field_list=id&field_list=count_of_issues&field_list=publisher&format=json&filter=name&filter=grayson&api_key=00000'
    f_list = ['name','start_year','id','count_of_issues','publisher'] # fields requested
    query = {'api_key': API,'format': 'json',{'field_list':f_list}, 'filter':('name:%s'% which_volume)}
    pprint.pprint(query)
    """
    print('Searching ComicVine...')
    the_data = fetchVine(url_start,query)

    if the_data['error'] != 'OK':
        print('CV Returned Error: %s' % the_data['error'])
        return

    num_results = the_data['number_of_total_results']
    if not num_results:
        print('No results returned!')
        return

    print('Found %s results.' % num_results)
    the_results = the_data['results']

    # get any additional results
    add_results = int(num_results) - 100
    if add_results > 0:
        offset = 100
        while add_results > 0:
            print('Fetching additional results (>%s)' % offset)
            query += "&offset=%s" % offset
            the_data = fetchVine(url_start,query)
            the_results.extend(the_data['results'])
            add_results -= 100
            offset += 100

    # make sure fields are populated
    for i in the_results:
        
        check_year = i.get('start_year')
        check_pub = i.get('publisher')
        if not check_pub:
            i['publisher'] = {}
            i['publisher']['name'] = 'Unknown'
        if not check_year:
            i['start_year'] = '????'

    the_results.sort(key=lambda k: k['start_year'])

    # check for close matches
    flat_volume = flatName(which_volume)
    match_results = []
    for i in the_results:
        if (flatName(i['name']) == flat_volume):
            match_results.append(i)

    # show matched (or all) results
    if not match_results:
        print('No matches for %s found.' % which_volume)
        print('Showing all %d results:' % len(the_results))
        for i in the_results:
            match_results.append(i)

        the_choice = showVolumeResults(match_results, None)

        if the_choice == 'go back':
            return

        the_choice = int(the_choice) - 1
        the_match = match_results[the_choice]
        vol_ID = str(the_match['id'])
        the_pub = the_match['publisher']['name']
        #print('Volume: %s PUBLISHER:\n%s' % (vol_ID,the_pub)) 
    else:
        print('Found %s matches:' % len(match_results))

        if (len(match_results) == len(the_results)):
            the_choice = showVolumeResults(match_results, None)
        else:
            the_choice = showVolumeResults(match_results,'Show all results')

        if the_choice == 'go back':
                return
        the_choice = int(the_choice) - 1

        if the_choice >= 0:
            the_match = match_results[the_choice]
            vol_ID = str(the_match['id'])
            the_pub = the_match['publisher']['name']
        else:
            print('Showing all %s results:' % len(the_results))
            the_choice = showVolumeResults(the_results,None)
            if the_choice == 'go back':
                return
            the_choice = int(the_choice) - 1
            the_match = the_results[the_choice]
            vol_ID = str(the_match['id'])
            the_pub = the_match['publisher']['name']

    return vol_ID, the_pub

def showVolumeResults(result_list, zero_option):
    "Shows a list (and optional option), returns choice as string"
    upper_range = len(result_list) + 1

    for i, m in enumerate(result_list):
        name, startYear, issueCount = m['name'], m['start_year'], m['count_of_issues']
        publisher = m['publisher']['name']
        print('%d : %s (%s) %s (~%s issues)' % (i+1, name, startYear, publisher, issueCount))

    if zero_option:
        print('%d : %s' % (0, zero_option))
        lower_range = 0
    else:
        lower_range = 1
    the_range = range(lower_range, upper_range)

    # Get volume selection
    the_input = ''
    while not the_input:
        the_input = raw_input('Enter the number before the correct Volume (or Return to start over): ')
        if the_input == '':
            return 'go back' # to distinguish from 0!
        if the_input.isdigit():
            if int(the_input) in the_range:
                break
        the_input = ''

    return the_input

def main():
    while True:
        which_volume = raw_input('Enter a Volume Title (or Return to quit): ')
        if not which_volume:
            sys.exit('Quitting...')
        else:
            vol_ID, the_pub = getVolumes(which_volume)
            if vol_ID:
                issueLoop(vol_ID, the_pub)


if __name__ == "__main__":
    if not API:
        # TODO ask for and save API
        print('No API! Open this script in an editor and follow the instructions on lines 5 & 6.')
    else:
        main()