add pypi package, cli parser ...
- use optional stackoverflow tags to do queriesmaster
parent
720e74d1f6
commit
ce02d7cfb6
@ -0,0 +1,8 @@
|
||||
v0.1
|
||||
----
|
||||
|
||||
Initial version:
|
||||
|
||||
- query using simple questions
|
||||
|
||||
- query with optional stackoverflow tags
|
@ -0,0 +1,20 @@
|
||||
Copyright (C) 2012 Chakib (sp4ke) Benziane (chakib.benz@gmail.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -1,4 +0,0 @@
|
||||
howto
|
||||
=====
|
||||
|
||||
quick code answers from stackoverflow
|
@ -0,0 +1,11 @@
|
||||
HowTo - StackOverflow Code Search Tool
|
||||
=====================================
|
||||
|
||||
Quick code answers from StackOverflow API, Just ask a question with an optional
|
||||
tag parameter to get quick code answers
|
||||
|
||||
USAGE:
|
||||
=====
|
||||
|
||||
::
|
||||
./howto 'filter dicts' -t python
|
@ -0,0 +1,6 @@
|
||||
cssselect==0.7.1
|
||||
ipython==0.13.1
|
||||
lxml==3.1beta1
|
||||
py-stackexchange==1.1-4
|
||||
pyquery==1.2.4
|
||||
wsgiref==0.1.2
|
@ -1,29 +0,0 @@
|
||||
from stackexchange import *
|
||||
from pyquery import PyQuery as pq
|
||||
import sys
|
||||
|
||||
if __name__ == '__main__':
|
||||
tag = sys.argv[2]
|
||||
query = sys.argv[1]
|
||||
so = StackOverflow()
|
||||
questions = so.search(order='desc', sort='votes', tagged=tag, intitle=query, filter='!-u2CTCMX')
|
||||
[q.fetch() for q in questions]
|
||||
|
||||
answers = [answer for sublist in
|
||||
[answer_list for answer_list in
|
||||
[filter(lambda x: x.accepted, q.answers) for q in questions]
|
||||
]
|
||||
for answer in sublist
|
||||
]
|
||||
|
||||
answers_wbody = so.answers([a.id for a in answers],order='desc',sort='votes', body='true', filter='!-u2CTCMX')
|
||||
answers_wbody = sorted(answers_wbody, key=lambda ans: ans.score, reverse=True)
|
||||
|
||||
|
||||
answers_wbody.reverse()
|
||||
for a in answers_wbody:
|
||||
html = pq(a.body)
|
||||
el = html('code')
|
||||
print el.text()
|
||||
print '================\n'
|
||||
|
@ -0,0 +1 @@
|
||||
__version__ = '0.1'
|
@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
|
||||
import argparse
|
||||
from stackexchange import *
|
||||
from pyquery import PyQuery as pq
|
||||
import sys
|
||||
|
||||
|
||||
class NoResult(Exception):
|
||||
pass
|
||||
|
||||
class SOSearch(Site):
|
||||
def __init__(self, qs, tags=None, **kw):
|
||||
self.qs = qs
|
||||
self.tags = '' if tags == None else tags
|
||||
self.domain = 'api.stackoverflow.com'
|
||||
super(SOSearch, self).__init__(self.domain, **kw)
|
||||
self.qs = self.search(order=DESC, sort='votes', tagged=self.tags,
|
||||
intitle=self.qs, filter='!-u2CTCMX')
|
||||
if self.qs.total == 0:
|
||||
raise NoResult
|
||||
|
||||
def first_q(self):
|
||||
self._populate_one_question(self.qs[0])
|
||||
return self.qs[0]
|
||||
|
||||
def has_code(self, answer):
|
||||
return '<code>' in answer.body
|
||||
|
||||
def _find_best_answer(self, question):
|
||||
if hasattr(question, 'is_accepted_id'):
|
||||
for a in question.answers:
|
||||
if not self.has_code(a):
|
||||
continue
|
||||
if a.id == question.is_accepted_id:
|
||||
question.best_answer = a
|
||||
else:
|
||||
for a in question.answers:
|
||||
if self.has_code(a):
|
||||
question.best_answer = question.answers[0]
|
||||
else:
|
||||
continue
|
||||
|
||||
|
||||
def _order_answers(self, qs):
|
||||
ordered = sorted(qs.answers, key=lambda a: a.score, reverse=True)
|
||||
qs.answers = ordered
|
||||
|
||||
def _populate_one_question(self, qs):
|
||||
qs.fetch()
|
||||
qs.answers = self.answers([a.id for a in qs.answers], order=DESC, body='true')
|
||||
for a in qs.answers:
|
||||
html = pq(a.body)
|
||||
el = html('code')
|
||||
a.code = el.text()
|
||||
self._order_answers(qs)
|
||||
self._find_best_answer(qs)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def main(args):
|
||||
"""docstring for main"""
|
||||
try:
|
||||
args.query = ' '.join(args.query).replace('?', '')
|
||||
so = SOSearch(args.query, args.tags)
|
||||
result = so.first_q().best_answer.code
|
||||
if result != None:
|
||||
print result
|
||||
else:
|
||||
print("Sorry I can't find your answer, try adding tags")
|
||||
except NoResult, e:
|
||||
print("Sorry I can't find your answer, try adding tags")
|
||||
|
||||
|
||||
def cli_run():
|
||||
"""docstring for argparse"""
|
||||
parser = argparse.ArgumentParser(description='Stupidly simple code answers from StackOverflow')
|
||||
parser.add_argument('query', help="What's the problem ?", type=str, nargs='+')
|
||||
parser.add_argument('-t','--tags', help='semicolon separated tags -> python;lambda')
|
||||
args = parser.parse_args()
|
||||
main(args)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cli_run()
|
@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
import howto
|
||||
import os
|
||||
|
||||
|
||||
def read(*names):
|
||||
values = dict()
|
||||
extensions = ['.txt', '.rst']
|
||||
for name in names:
|
||||
value = ''
|
||||
for extension in extensions:
|
||||
filename = name + extension
|
||||
if os.path.isfile(filename):
|
||||
value = open(name + extension).read()
|
||||
break
|
||||
values[name] = value
|
||||
return values
|
||||
|
||||
long_description = """
|
||||
%(README)s
|
||||
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
%(CHANGES)s
|
||||
|
||||
""" % read('README', 'CHANGES')
|
||||
|
||||
setup(
|
||||
name='howto',
|
||||
version=howto.__version__,
|
||||
description='stackoverflow code search tool',
|
||||
long_description=long_description,
|
||||
classifiers=[
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Environment :: Console",
|
||||
"Intended Audience :: Developers",
|
||||
"Programming Language :: Python :: 2",
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Topic :: Documentation",
|
||||
],
|
||||
keywords='howto help console code stackoverflow',
|
||||
author='Chakib Benziane',
|
||||
author_email='chakib.benz@gmail.com',
|
||||
maintainer='Chakib Benziane',
|
||||
maintainer_email='chakib.benz@gmail.com',
|
||||
url='https://github.com/sp4ke/howto',
|
||||
license='MIT',
|
||||
packages=find_packages(),
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'howto = howto.howto:cli_run',
|
||||
]
|
||||
},
|
||||
install_requires=[
|
||||
'pyquery',
|
||||
'py-stackexchange',
|
||||
],
|
||||
)
|
@ -0,0 +1,63 @@
|
||||
import unittest
|
||||
from stackexchange import StackOverflow
|
||||
import stackexchange
|
||||
from pyquery import PyQuery as pq
|
||||
from howto.howto import SOSearch
|
||||
from random import choice
|
||||
|
||||
|
||||
class SOSearchTests(unittest.TestCase):
|
||||
'''Test case for Stackoverflow searches'''
|
||||
|
||||
def setUp(self):
|
||||
self.so_search = SOSearch('singleton pattern', 'java')
|
||||
|
||||
def test_issubclass_of_stackexchangeSite(self):
|
||||
self.assertIsInstance(self.so_search, stackexchange.Site)
|
||||
|
||||
def test_init_values(self):
|
||||
with self.assertRaises(Exception):
|
||||
so = SOSearch()
|
||||
|
||||
def test_fist_question(self):
|
||||
first = self.so_search.first_q()
|
||||
self.assertEqual(first, self.so_search.qs[0])
|
||||
|
||||
def test_questions_ordered_by_votes(self):
|
||||
for (qs, next) in zip(self.so_search.qs[:-2], self.so_search.qs[1:]):
|
||||
self.assertGreaterEqual(qs.score,
|
||||
next.score)
|
||||
|
||||
def test_extracted_code(self):
|
||||
question = self.so_search.first_q()
|
||||
answer = choice(question.answers)
|
||||
html = pq(answer.body)
|
||||
el = html('code')
|
||||
code = el.text()
|
||||
self.assertEqual(code, answer.code)
|
||||
|
||||
def test_has_code(self):
|
||||
q = self.so_search.first_q()
|
||||
a = choice(q.answers)
|
||||
a.body = 'This is a test<code>Magic</code>'
|
||||
self.assertEqual(self.so_search.has_code(a), True)
|
||||
a.body = 'This is a test'
|
||||
self.assertEqual(self.so_search.has_code(a), False)
|
||||
|
||||
def test_best_answer(self):
|
||||
question = self.so_search.first_q()
|
||||
best = question.best_answer
|
||||
for answer in question.answers:
|
||||
self.assertGreaterEqual(best.score, answer.score)
|
||||
|
||||
|
||||
def test_get_next_answer(self):
|
||||
self.failUnless(False)
|
||||
|
||||
|
||||
def main():
|
||||
"""docstring for main"""
|
||||
unittest.main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Reference in New Issue