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