Take the 2-minute tour ×
Stack Apps is a question and answer site for apps, scripts, and development with the Stack Exchange API. It's 100% free, no registration required.

This native application retrieves the closed questions from a site, excluding duplicates, migrations, deleted questions, and recently closed questions. The list is presented in a form that can be included in a meta post. For each question, the output presents the title linking to the post, the close reason abbreviated to 2–4 letters, the score and the number of answers if any.

Requirements: Python (tested with Python 2.6)

Example (result in this post):

./show_closed_questions cs

Code

#! /usr/bin/env python
import datetime, json, os, re, sys, urllib2

def open_page(site, **params):
    url = 'http://api.stackexchange.com/search/advanced?order=asc&sort=creation&closed=True&site=' + site
    for k in params:
        url += '&' + str(k) + '=' + str(params[k])
    return urllib2.urlopen(url)

def load_page(f, save=None):
    text = f.read()
    if save:
        o = open(save, 'w')
        o.write(text)
        o.close()
    return json.loads(text)

def download(site, save=None):
    page_number = 1
    data = load_page(open_page(site),
                     '%s-%03d.json' % (save, page_number) if save else None)
    questions = data['items']
    while data['has_more']:
        page_number += 1
        data = load_page(open_page(site, page=page_number),
                     '%s-%03d.json' % (save, page_number) if save else None)
        questions += data['items']
    return questions

def load_files(names):
    questions = []
    for name in names:
        data = load_page(open(name))
        questions += data['items']
    return questions

def filter_questions(questions,
                     min_closed_date = 0,
                     max_closed_date = 2**64-1):
    return [q for q in questions
            if q['closed_date'] >= min_closed_date
            and q['closed_date'] <= max_closed_date
            and not q.has_key('migrated_to')
            and q['closed_reason'] not in set(['exact duplicate', 'duplicate'])]

def escape_for_markdown(text):
    return re.sub(r'[<>\[\]_`]', r'\\\g<0>',
                  re.sub(r'(?:^|\n)[\t ]*(?:[#*\-]|[0-9]+\.)',
                         lambda s: s[:-1] + '\\' + s[-1:],
                         text))

def day_of_date(seconds):
    date = datetime.datetime.fromtimestamp(seconds)
    return date.strftime("%04Y-%02m-%02d")

def pretty_print(questions):
    for q in questions:
        title = escape_for_markdown(q['title'])
        title_bytes = title.encode('utf-8')
        close_reason = {'off topic': 'OT',
                        'not constructive': 'NC',
                        'not a real question': 'NaRQ',
                        'too localized': 'TL',
                        'off-topic': 'OT',
                        'unclear what you\'re asking': 'Un',
                        'too broad': 'TB',
                        'primarily opinion-based': 'POB',
                       }[q['closed_reason']]
        closure_date = day_of_date(q['closed_date'])
        include_closure_date = False
        print ('  * [%s](%s) (%s%s; %s%d%s)' % \
               (title, q['link'],
                close_reason,
                ' on ' + closure_date if include_closure_date else '',
                ('+' if q['score'] > 0 else ''), q['score'],
                ('' if q['answer_count'] == 0 else
                 '; 1 answer' if q['answer_count'] == 1 else
                 '; ' + str(q['answer_count']) + ' answers'))).encode('utf-8')

def usage_and_exit():
    print 'Usage: ' + sys.argv[0] + ''' SITENAME [DAYS]
Retrieve the list of closed questions on the Stack Exchange site SITENAME.
Filter out the questions that are closed as exact duplicates or that have
been migrated away.
Also filter out questions that were closed less than DAYS ago.
DAYS defaults to 15.
Pretty-print the remaining questions in markdown syntax.'''

def main(*args):
    if len(args) == 0 or args[0] == '--help':
        usage_and_exit()
    min_days_ago = 15
    if len(args) >= 2 and args != '': min_days_ago = int(args[1])
    max_closed_date = int((datetime.datetime.utcnow() -
                           datetime.timedelta(min_days_ago)).strftime("%s"))
    all_questions = download(args[0])
    questions = filter_questions(all_questions,
                                 max_closed_date=max_closed_date)
    pretty_print(questions)

if __name__ == '__main__':
    main(*sys.argv[1:])
share|improve this question
    
You could do the same for searching for closed:1. –  dimensio1n0 Sep 15 '13 at 12:22
    
@DImension10AbhimanyuPS That code does search for closed:1 and presents the results in a usable way. –  Gilles Sep 15 '13 at 12:24
add comment

You must log in to answer this question.

Browse other questions tagged .