add pypi package, cli parser ...

- use optional stackoverflow tags to do queries
master
Chakib (spike) Benziane 11 years ago
parent 720e74d1f6
commit ce02d7cfb6

1
.gitignore vendored

@ -33,3 +33,4 @@ nosetests.xml
.mr.developer.cfg
.project
.pydevproject
.ropeproject

@ -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()