├── requirements_windows.txt ├── requirements.txt ├── .flake8 ├── setup.cfg ├── MANIFEST.in ├── .coveragerc ├── Makefile ├── .gitignore ├── codecov.yml ├── Examples ├── shell-search.py └── loklak-tweets-search.py ├── .travis.yml ├── AUTHORS ├── setup.py ├── CODE_OF_CONDUCT.md ├── sample.py ├── bin └── loklak ├── tests └── test.py ├── README.rst └── loklak.py /requirements_windows.txt: -------------------------------------------------------------------------------- 1 | gooey==0.9.5 -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.16.0 2 | xmljson==0.1.9 3 | lxml==4.1.1 4 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = E226, E501 3 | exclude = docs/*, build/* 4 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal=1 3 | 4 | [metadata] 5 | description-file = README.md -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include requirements.txt 2 | include requirements_windows.txt 3 | include AUTHORS 4 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [report] 2 | omit = 3 | */python?.?/* 4 | */site-packages/nose/* 5 | */tests/* 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | @echo "make clean" 3 | 4 | clean: 5 | rm -rf build dist *.egg-info */__pycache__ */*.pyc -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | *.egg-info/ 4 | *.egg 5 | *.py[cod] 6 | __pycache__/ 7 | *.so 8 | *~ 9 | venv/* 10 | *.coverage 11 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | require_ci_to_pass: yes 4 | 5 | coverage: 6 | precision: 2 7 | round: down 8 | range: "70...100" 9 | 10 | status: 11 | project: yes 12 | patch: yes 13 | changes: no 14 | 15 | comment: 16 | layout: "reach, diff, flags, files, footer" 17 | behavior: default 18 | require_changes: no 19 | -------------------------------------------------------------------------------- /Examples/shell-search.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """This module contains an example of retrieving tweets using search() function and print them in the console.""" 4 | import sys 5 | from loklak import Loklak 6 | 7 | query = sys.argv[1] 8 | l = Loklak() 9 | print("Retrieving Tweets...") 10 | tweets = l.search(query) 11 | 12 | for tweet in tweets['statuses']: 13 | print('@'+str(tweet['screen_name'])+': '+str(tweet['text'])) -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | - "3.5" 5 | install: 6 | - pip install -r requirements.txt 7 | - pip install pydocstyle 8 | - pip install pyflakes 9 | - pip install python-coveralls 10 | - pip install coverage 11 | - pip install nose 12 | script: 13 | - python setup.py install 14 | - pyflakes setup.py 15 | - nosetests tests/test.py -v --with-coverage 16 | - pydocstyle 17 | after_success: 18 | - coveralls 19 | - bash <(curl -s https://codecov.io/bash) 20 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Sudheesh Singanamalla 2 | Gopal Singh Meena 3 | Pol Baladas Luna 4 | Seva Zhidkov 5 | Sopan Khosla 6 | Damini Satya 7 | M.Yasoob Ullah Khalid 8 | Gunvir Ranu 9 | DengYiping 10 | Joy 11 | Sampriti Panda 12 | Dhruv Ramani 13 | -------------------------------------------------------------------------------- /Examples/loklak-tweets-search.py: -------------------------------------------------------------------------------- 1 | """This module contains an example for searching the tweets using search() function in loklak.py.""" 2 | from loklak import Loklak 3 | 4 | myTweets = Loklak() 5 | 6 | movieName = raw_input("What movie would you like to \ 7 | search recent tweets for? ") #asks for what user would like to search for 8 | 9 | index = 1 10 | for tweet in myTweets.search(movieName)["statuses"]: #searches movie and finds "statuses" where "text" is under 11 | print("%d%s", index, '.') #prints the tweet number 12 | print("%s\n", tweet["text"]) #prints the text of the tweet and makes a new line 13 | index += 1 #increments index 14 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """The module that setup loklak python API.""" 2 | try: 3 | from setuptools import setup 4 | except ImportError: 5 | from distutils.core import setup 6 | 7 | description = "Python API for loklak, Anonymous distributed P2P Systems." 8 | try: 9 | long_description = open("README.rst").read() 10 | except IOError: 11 | long_description = description 12 | with open('requirements.txt') as f: 13 | requirements = f.read().splitlines() 14 | setup(name='python-loklak-api', 15 | version='1.7', 16 | description=description, 17 | long_description=long_description, 18 | author='Sudheesh Singanamalla', 19 | author_email='sudheesh95@gmail.com', 20 | url='https://github.com/loklak/loklak_python_api', 21 | license='', 22 | include_package_data=True, 23 | platforms='Linux/Mac', 24 | py_modules=['loklak'], 25 | python_requires='>=2.7.0', 26 | classifiers=[ 27 | 'Development Status :: 3 - Alpha', 28 | 'Intended Audience :: Developers', 29 | 'Programming Language :: Python', 30 | 'Programming Language :: Python :: 2', 31 | 'Programming Language :: Python :: 3', 32 | 'Topic :: Internet', 33 | ], 34 | keywords="Twitter Loklak Anonymous API", 35 | install_requires=requirements, 36 | zip_safe=False, 37 | download_url='https://github.com/loklak/loklak_python_api/archive/v1.7.tar.gz', 38 | scripts=['bin/loklak'] 39 | ) 40 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 4 | 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. 6 | 7 | Examples of unacceptable behavior by participants include: 8 | 9 | * The use of sexualized language or imagery 10 | * Personal attacks 11 | * Trolling or insulting/derogatory comments 12 | * Public or private harassment 13 | * Publishing other's private information, such as physical or electronic addresses, without explicit permission 14 | * Other unethical or unprofessional conduct. 15 | 16 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. 17 | 18 | This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. 19 | 20 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 21 | 22 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) 23 | -------------------------------------------------------------------------------- /sample.py: -------------------------------------------------------------------------------- 1 | """This module contains an example on using every function in loklak.py and print the result in the console.""" 2 | import tempfile 3 | from loklak import Loklak 4 | from pprint import pprint 5 | 6 | # Create a loklak object 7 | l = Loklak() 8 | 9 | ### API Usage 10 | 11 | ####################################### 12 | # Usage and Testings # 13 | ####################################### 14 | # Loklak settings API 15 | settings = l.settings() 16 | pprint(settings) 17 | 18 | # Loklak Status API 19 | 20 | status = l.status() 21 | pprint(status) 22 | 23 | # Loklak Hello API 24 | 25 | hello = l.hello() 26 | pprint(hello) 27 | 28 | # Loklak Peers API 29 | 30 | peers = l.peers() 31 | pprint(peers) 32 | 33 | # Loklak Geocode API 34 | #Single place : 35 | geocode = l.geocode(['Barcelona']) 36 | pprint(geocode) 37 | 38 | # Multiple place names : 39 | geocode = l.geocode(['New York', 'Singapore']) 40 | pprint(geocode) 41 | 42 | # Loklak Geographic Map API 43 | png_data = l.get_map(17.582729, 79.118320, 512, 512, 12, 'Hello') 44 | path = tempfile.mkstemp(suffix=".png")[1] 45 | open(path, 'wb').write(png_data) 46 | 47 | ####################################### 48 | # Users API 49 | 50 | # User Query 51 | user = l.user('sudheesh001') 52 | pprint(user) 53 | # User Query with followers and following list 54 | user = l.user('sudheesh001', '1000', '1000') 55 | pprint(user) 56 | # User Query with followers only 57 | user = l.user('sudheesh001', '1000') 58 | pprint(user) 59 | # User Query with following only 60 | user = l.user('sudheesh001', None, '1000') 61 | pprint(user) 62 | 63 | user = l.user('') 64 | pprint(user) 65 | 66 | ####################################### 67 | 68 | # Search API Usage 69 | 70 | ### Aggregations 71 | 72 | searchAggregation1 = l.aggregations('sudheesh001',None,None,['mentions','hashtags'],10) 73 | pprint(searchAggregation1) 74 | 75 | searchAggregation2 = l.aggregations('sudheesh001','2015-01-10','2015-10-21',['mentions','hashtags'],10) 76 | pprint(searchAggregation2) 77 | 78 | searchAggregation3 = l.aggregations('sudheesh001',None,'2015-10-21',['mentions','hashtags'],10) 79 | pprint(searchAggregation3) 80 | 81 | searchAggregation4 = l.aggregations('sudheesh001','2015-10-21',None,['mentions','hashtags'],10) 82 | pprint(searchAggregation4) 83 | 84 | searchAggregation5 = l.aggregations('sudheesh001',None,None,['mentions','hashtags']) 85 | pprint(searchAggregation5) 86 | 87 | ### Search 88 | 89 | search1 = l.search('sudheesh001') 90 | pprint(search1) 91 | 92 | search2 = l.search('sudheesh001','2015-01-10') 93 | pprint(search2) 94 | 95 | search3 = l.search('sudheesh001', None, '2015-01-10') 96 | pprint(search3) 97 | 98 | search4 = l.search('sudheesh001', '2015-01-10', None) 99 | pprint(search4) 100 | 101 | search5 = l.search('sudheesh001', '2015-01-10', '2015-01-21') 102 | pprint(search5) 103 | 104 | search6 = l.search('sudheesh001', '2015-01-10', '2015-01-21','sudheesh001') 105 | pprint(search6) 106 | 107 | search7 = l.search('gunvirranu', None, None, None, 6) 108 | pprint(search7) 109 | 110 | ###loklak Suggestion API 111 | suggestion1 = l.suggest() 112 | pprint(suggestion1) 113 | 114 | suggestion2 = l.suggest(count=20, order='desc') 115 | pprint(suggestion2) 116 | 117 | 118 | suggestion3 = l.suggest(count=10, order='asc') 119 | pprint(suggestion3) 120 | 121 | ### account 122 | account1 = l.account('name') 123 | pprint(account1) 124 | 125 | account2 = l.account('name','update','type') 126 | pprint(account2) 127 | 128 | # Try call function without name 129 | account3 = l.account(action='update', data='type') 130 | -------------------------------------------------------------------------------- /bin/loklak: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | import argparse 4 | from loklak import Loklak 5 | from pprint import pprint 6 | import os 7 | def main(): 8 | parser = argparse.ArgumentParser() 9 | 10 | subparsers = parser.add_subparsers(help='Python wrapper around the loklak API', dest='command') 11 | 12 | search_parser = subparsers.add_parser('search', help='Search API Wrapper which helps to query loklak for JSON results.') 13 | search_parser.add_argument('query', action='store', help='Query term') 14 | search_parser.add_argument('--since', action='store', help='Only messages after the date (including the date) =yyyy-MM-dd or yyyy-MM-dd_HH:mm') 15 | search_parser.add_argument('--until', action='store', help='Only messages before the date (excluding the date) =yyyy-MM-dd or yyyy-MM-dd_HH:mm') 16 | search_parser.add_argument('--from_user', action='store', help='Only messages published by the user') 17 | search_parser.add_argument('--count', action='count', help='Result count') 18 | 19 | status_parser = subparsers.add_parser('status', help='Status API Wrapper for the loklak status check.') 20 | status_parser.add_argument('--get_status', action='store_true', default="true", help='Get complete status') 21 | 22 | suggest_parser = subparsers.add_parser('suggest', help='Suggestions API Wrapper , Works better with local loklak instance.') 23 | suggest_parser.add_argument('query', action='store', help='Query term') 24 | suggest_parser.add_argument('--since', action='store', help='Only messages after the date (including the date) =yyyy-MM-dd or yyyy-MM-dd_HH:mm') 25 | suggest_parser.add_argument('--until', action='store', help='Only messages before the date (excluding the date) =yyyy-MM-dd or yyyy-MM-dd_HH:mm') 26 | suggest_parser.add_argument('--order', choices=['asc', 'desc'], default='asc', help='Result count') 27 | suggest_parser.add_argument('--orderby', choices=['retrieval_next', 'query_count'], help='A field name of the query index schema') 28 | suggest_parser.add_argument('--count', action='count', help='Result count') 29 | 30 | crawler_parser = subparsers.add_parser('crawler', help='Crawler API Wrapper on Loklak to crawl for tweets for a particular crawl depth.') 31 | crawler_parser.add_argument('--crawl', action='store_true', default="true", help='Get status of loklak API') 32 | 33 | hello_parser = subparsers.add_parser('hello', help='Loklak status check API') 34 | hello_parser.add_argument('--get_hello_status', action='store_true', default="true", help='Get Loklak API health status') 35 | 36 | geocode_parser = subparsers.add_parser('geocode', help='Geocode API for geolocation based information.') 37 | geocode_parser.add_argument('--places', action='store', help=', a list of place names') 38 | 39 | peer_parser = subparsers.add_parser('peers', help='Loklak API for peers connected on the distributed network.') 40 | peer_parser.add_argument('--get_peers', action='store_true', default="true", help='Get Loklak peers connected') 41 | 42 | hello_parser = subparsers.add_parser('pushgeojson', help='Public API to push geojson objects to the loklak server') 43 | hello_parser.add_argument('--get_pushgeojson', action='store_true', default="true", help='Push objects') 44 | 45 | user_parser = subparsers.add_parser('user', help='User API to show twitter user information.') 46 | user_parser.add_argument('screen_name', action='store', help='Twitter screen name of the user') 47 | user_parser.add_argument('--followers', action='store', help='Followers of the user') 48 | user_parser.add_argument('--following', action='store', help='Accounts the user is following') 49 | 50 | map_parser = subparsers.add_parser('map', help='Map Visualization render using Loklak service.') 51 | map_parser.add_argument('latitude', action='store', help='Latitude value') 52 | map_parser.add_argument('longitude', action='store', help='Longitude value') 53 | map_parser.add_argument('--width', action='store', default='500', help='Width') 54 | map_parser.add_argument('--height', action='store', default='500', help='Height') 55 | map_parser.add_argument('--zoom', action='count', default='8', help='Zoom value') 56 | map_parser.add_argument('text', action='store', help='Value of text like Hello') 57 | 58 | markdown_parser = subparsers.add_parser('markdown', help='Markdown conversion API to render markdown as image using Loklak.') 59 | markdown_parser.add_argument('text', action='store', help='Text to be printed, markdown possible') 60 | markdown_parser.add_argument('--color_text', action='store', default="000000", help='6-character hex code for the color') 61 | markdown_parser.add_argument('--color_background', action='store', default="ffffff", help='6-character hex code for the color') 62 | markdown_parser.add_argument('--padding', action='count', default="10", help='Space around text') 63 | markdown_parser.add_argument('--uppercase', action='store_true', default="true", help='If true the text is printed UPPERCASE') 64 | 65 | args = parser.parse_args() 66 | loklak = Loklak() 67 | if args.command == "search": 68 | pprint(loklak.search(args.query, args.since, args.until, args.from_user, args.count)) 69 | elif args.command == "status": 70 | pprint(loklak.status()) 71 | elif args.command == "suggest": 72 | pprint(loklak.suggest(args.query, args.count, args.order, args.orderby, args.since, args.until)) 73 | elif args.command == "crawler": 74 | pass 75 | elif args.command == "hello": 76 | pprint(loklak.hello()) 77 | elif args.command == "geocode": 78 | pprint(loklak.geocode(args.places.split(', '))) 79 | elif args.command == "peers": 80 | pprint(loklak.peers()) 81 | elif args.command == "pushgeojson": 82 | pass 83 | elif args.command == "user": 84 | pprint(loklak.search(args.screen_name.split(), args.followers, args.following)) 85 | elif args.command == "map": 86 | data = loklak.get_map(args.latitude, args.longitude, args.width, args.height, args.zoom, args.text) 87 | with open(os.path.join(os.getcwd(), 'map.png'), 'wb') as f: 88 | f.write(data) 89 | elif args.command == "markdown": 90 | data = loklak.get_markdown(args.text, args.color_text, args.color_background, args.padding, args.uppercase) 91 | with open(os.path.join(os.getcwd(), 'markdown.png'), 'wb') as f: 92 | f.write(data) 93 | else: 94 | print('Choose API method.') 95 | 96 | if __name__ == '__main__': 97 | main() 98 | -------------------------------------------------------------------------------- /tests/test.py: -------------------------------------------------------------------------------- 1 | """This module contains unit tests for every method in loklak.py.""" 2 | from __future__ import print_function 3 | import unittest 4 | from loklak import Loklak 5 | import os 6 | import sys 7 | 8 | class TestLoklak(unittest.TestCase): 9 | """Test class.""" 10 | 11 | baseUrl = 'http://api.loklak.org/' 12 | 13 | def setUp(self): 14 | """Test proper setup.""" 15 | self.loklak = Loklak(self.baseUrl) 16 | 17 | def test_status(self): 18 | """Test status.""" 19 | result = self.loklak.status() 20 | 21 | self.assertTrue('index' in result) 22 | result_properties = [ 23 | 'messages', 'mps', 'users', 'queries', 24 | 'accounts', 'user', 'followers', 'following' 25 | ] 26 | for prop in result_properties: 27 | self.assertTrue( 28 | prop in result['index'], 29 | msg='{} not found in index'.format(prop) 30 | ) 31 | 32 | result = self.loklak.status("nonexisturl") 33 | self.assertEqual(result, "{}") 34 | 35 | def test_hello(self): 36 | """Test hello instance.""" 37 | result = self.loklak.hello() 38 | self.assertEqual(result['status'], u'ok') 39 | 40 | def test_xmlToJson(self): 41 | """Test xmlToJson method.""" 42 | xmlData = "\nCoders\nFOSSASIA\nFOSSASIA \ 43 | \nLet's code!!\n" 44 | expectedJSONData = '{"note": {"to": {"$": "Coders"}, "from": {"$": "FOSSASIA"}, "heading": {"$": "FOSSASIA"}, "body": {"$": "Let\'s code!!"}}}' 45 | result = self.loklak.xmlToJson(xmlData) 46 | self.assertEqual(result, expectedJSONData) 47 | result = self.loklak.xmlToJson() 48 | self.assertEqual(result, "[]") 49 | 50 | def test_geocode(self): 51 | """Test geological features.""" 52 | result = self.loklak.geocode() 53 | self.assertEqual(result, '{}') 54 | 55 | result = self.loklak.geocode(places=['Moscow']) 56 | self.assertTrue('locations' in result) 57 | self.assertTrue('Moscow' in result['locations']) 58 | self.assertEqual( 59 | 'Russian Federation', 60 | result['locations']['Moscow']['country'] 61 | ) 62 | self.assertEqual( 63 | 'Russian Federation', 64 | result['locations']['Moscow']['country'] 65 | ) 66 | self.assertTrue( 67 | isinstance(result['locations']['Moscow']['place'], list) 68 | ) 69 | 70 | def test_get_map(self): 71 | """Test the get_map method.""" 72 | map_file = os.path.join(os.getcwd(), 'markdown.png') 73 | data = self.loklak.get_map(17.582729, 79.118320) 74 | print(data) 75 | self.assertFalse(data[:8] == b'\211PNG\r\n\032\n' and 76 | data[12:16] == b'IHDR') 77 | with open(map_file, 'w+') as file_handle: 78 | file_handle.write(data) 79 | with open(map_file, 'rb') as file_handle: 80 | file_contents = file_handle.read() 81 | self.assertTrue(os.path.exists(map_file)) 82 | # self.assertEqual(data, file_contents) 83 | try: 84 | os.remove(map_file) 85 | except OSError as error: 86 | print(error) 87 | 88 | def test_peers(self): 89 | """Test finding peers.""" 90 | result = self.loklak.peers() 91 | if (len(result['peers']) == 0): 92 | pass 93 | else: 94 | self.assertTrue('peers' in result) 95 | self.assertTrue(isinstance(result['peers'], list)) 96 | self.assertTrue(len(result['peers']) >= 1) 97 | self.assertEqual(len(result['peers']), result['count']) 98 | 99 | def test_push(self): 100 | """Test for push data to index.""" 101 | data={ "statuses": [ { "id_str": "yourmessageid_1234", "screen_name": "testuser", "created_at": "2016-07-22T07:53:24.000Z", "text": "The rain is spain stays always in the plain", "source_type": "GENERIC", "place_name": "Georgia, USA", "location_point": [3.058579854228782,50.63296878274201], "location_radius": 0, "user": { "user_id": "youruserid_5678", "name": "Mr. Bob", } } ] } 102 | result = self.loklak.push(data) 103 | self.assertTrue('status' in result) 104 | 105 | def test_user(self): 106 | """Test user.""" 107 | result = self.loklak.user('dhruvRamani98') 108 | self.assertTrue('user' in result) 109 | self.assertTrue('name' in result['user']) 110 | self.assertTrue('screen_name' in result['user']) 111 | result = self.loklak.user("fossasia", 1500, 330) 112 | self.assertTrue('user' in result) 113 | self.assertTrue('name' in result['user']) 114 | result = self.loklak.user() 115 | self.assertTrue('error' in self.loklak.user()) 116 | self.assertEqual(result, '{"error": "No user name given to query. Please check and try again"}') 117 | 118 | def test_search(self): 119 | """Test search result.""" 120 | result = self.loklak.search('doctor who', count=18) 121 | self.assertTrue('statuses' in result) 122 | self.assertTrue(isinstance(result['statuses'], list)) 123 | self.assertTrue(len(result['statuses']) >= 1) 124 | self.assertEqual(len(result['statuses']), 125 | int(result['search_metadata']['maximumRecords'])) 126 | self.assertEqual(int(result['search_metadata']['maximumRecords']), 18) 127 | 128 | result = self.loklak.search('FOSSASIA', since='2000-01-01', until='2017-12-31_12:24', from_user='fossasia', count=20) 129 | self.assertTrue('statuses' in result) 130 | 131 | result = self.loklak.search() 132 | self.assertEqual(result, '{"error": "No Query string has been given to query for an account"}') 133 | 134 | def test_aggregations(self): 135 | """Test aggregations.""" 136 | result = self.loklak.aggregations('fossasia', '2017-01-10', 137 | '2018-01-10', 10) 138 | data = result.json() 139 | self.assertEqual(result.status_code, 200) 140 | self.assertTrue('aggregations' in data) 141 | 142 | result = self.loklak.aggregations() 143 | self.assertEqual(result, '{"error": "No Query string has been given to run query for aggregations"}') 144 | # self.assertTrue('hashtags' in data['aggregations']) 145 | # self.assertTrue('mentions' in data['aggregations']) 146 | 147 | if __name__ == '__main__': 148 | if len(sys.argv) > 1: 149 | TestLoklak.baseUrl = sys.argv.pop() 150 | unittest.main() 151 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Python Loklak API 2 | ----------------- 3 | 4 | |PyPI version| |Build Status| |Codecov branch| |Coverage Status| |Code 5 | Health| |Dependency Status| 6 | 7 | -------------- 8 | 9 | If you want to create an alternative twitter search portal, the only way 10 | would be to use the official twitter API to retrieve Tweets. But that 11 | interface needs an OAuth account and it makes your search portal 12 | completely dependent on Twitters goodwill. The alternative is, to scrape 13 | the tweets from the twitter html search result pages, but Twitter may 14 | still lock you out on your IP address. To circumvent this, you need many 15 | clients accessing twitter to scrape search results. This makes it 16 | neccessary to create a distributed peer-to-peer network of twitter 17 | scrapers which can all organize, store and index tweets. This solution 18 | was created with loklak. 19 | 20 | What is Loklak ? 21 | ^^^^^^^^^^^^^^^^ 22 | 23 | It is a server application which is able to collect messages from 24 | various sources, including twitter. The server contains a search index 25 | and a peer-to-peer index sharing interface. 26 | 27 | -------------- 28 | 29 | Why should I use this ? 30 | ^^^^^^^^^^^^^^^^^^^^^^^ 31 | 32 | If you like to be anonymous when searching things, want to archive 33 | tweets or messages about specific topics and if you are looking for a 34 | tool to create statistics about tweet topics, then you may consider 35 | loklak. With loklak you can do: - collect and store a very, very large 36 | amount of tweets and similar messages - create your own search engine 37 | for tweets - omit authentication enforcment for API requests on the 38 | twitter plattform - share tweets and tweet archives with other loklak 39 | users - search anonymously on your own search portal - create your own 40 | tweet search portal or statistical evaluations - use Kibana to analyze 41 | large amounts of tweets as source for statistical data. 42 | 43 | -------------- 44 | 45 | Documentation of the API and Usage Examples 46 | =========================================== 47 | 48 | To use the loklak app, first an object of the loklak type needs to be 49 | created. Do the following to install the ``pip`` module and add it to 50 | your ``requirements`` for the application. 51 | Currently our pip package is supported for python2.7 and lower venv versions. 52 | Will be supported in python3 version soon! 53 | 54 | ``pip install python-loklak-api`` 55 | 56 | To use the GUI client for API, install ``wxPython`` from 57 | `here `__ and then install 58 | `Gooey `__ ``pip`` module. 59 | 60 | ``pip install Gooey`` 61 | 62 | Then add 63 | 64 | :: 65 | 66 | from gooey import Gooey 67 | @Gooey 68 | 69 | before ``def main():`` in ``/bin/loklak`` 70 | 71 | Loklak once installed, can be used in the application as 72 | 73 | ``from loklak import Loklak`` 74 | 75 | To create a loklak object you can assign the ``Loklak()`` object to a 76 | variable. ``variable = Loklak()`` 77 | 78 | eg. ``l = Loklak()`` This creates an objects whose backend loklak server 79 | is ``http://loklak.org/`` 80 | 81 | | If you want to set this API to use your own server, you can now define 82 | it by doing 83 | | ``l = Loklak('http://192.168.192.5:9000/')`` for example or pass a URL 84 | to it as 85 | | ``l = Loklak('http://loklak-super-cluster.mybluemix.net/')`` 86 | 87 | Note the trailing ``/`` is important and so is ``http://`` 88 | 89 | API Documentation 90 | ~~~~~~~~~~~~~~~~~ 91 | 92 | Status of the Loklak server 93 | ''''''''''''''''''''''''''' 94 | 95 | Using the object created above, ``l.status()`` returns a json of the 96 | status as follows 97 | 98 | .. code:: json 99 | 100 | { 101 | "system": { 102 | "assigned_memory": 2051014656, 103 | "used_memory": 1374976920, 104 | "available_memory": 676037736, 105 | "cores": 8, 106 | "threads": 97, 107 | "runtime": 734949, 108 | "time_to_restart": 85665051, 109 | "load_system_average": 18.19, 110 | "load_system_cpu": 0.24344589731081373, 111 | "load_process_cpu": 0.018707976134026073, 112 | "server_threads": 68 113 | }, 114 | "index": { 115 | "mps": 176, 116 | "messages": { 117 | "size": 1195277012, 118 | "size_local": 1195277012, 119 | "size_backend": 0, 120 | "stats": { 121 | "name": "messages", 122 | "object_cache": { 123 | "update": 51188, 124 | "hit": 1796, 125 | "miss": 139470, 126 | "size": 10001, 127 | "maxsize": 10000 128 | }, 129 | "exist_cache": { 130 | "update": 68419, 131 | "hit": 2450, 132 | "miss": 137020, 133 | "size": 68313, 134 | "maxsize": 3000000 135 | }, 136 | "index": { 137 | "exist": 68634, 138 | "get": 0, 139 | "write": 51016 140 | } 141 | }, 142 | "queue": { 143 | "size": 100000, 144 | "maxSize": 100000, 145 | "clients": 72 146 | } 147 | }, 148 | "users": { 149 | "size": 65915082, 150 | "size_local": 65915082, 151 | "size_backend": 0, 152 | "stats": { 153 | "name": "users", 154 | "object_cache": { 155 | "update": 51827, 156 | "hit": 3756, 157 | "miss": 639, 158 | "size": 10000, 159 | "maxsize": 10000 160 | }, 161 | "exist_cache": { 162 | "update": 56222, 163 | "hit": 0, 164 | "miss": 0, 165 | "size": 15933, 166 | "maxsize": 3000000 167 | }, 168 | "index": { 169 | "exist": 0, 170 | "get": 639, 171 | "write": 51016 172 | } 173 | } 174 | }, 175 | "queries": { 176 | "size": 4251, 177 | "stats": { 178 | "name": "queries", 179 | "object_cache": { 180 | "update": 452, 181 | "hit": 132, 182 | "miss": 3297, 183 | "size": 160, 184 | "maxsize": 10000 185 | }, 186 | "exist_cache": { 187 | "update": 3703, 188 | "hit": 162, 189 | "miss": 2959, 190 | "size": 3002, 191 | "maxsize": 3000000 192 | }, 193 | "index": { 194 | "exist": 2959, 195 | "get": 176, 196 | "write": 292 197 | } 198 | } 199 | }, 200 | "accounts": {"size": 96}, 201 | "user": {"size": 790137}, 202 | "followers": {"size": 146}, 203 | "following": {"size": 135} 204 | }, 205 | "client_info": { 206 | "RemoteHost": "103.43.112.99", 207 | "IsLocalhost": "false", 208 | "request_header": { 209 | "Cookie": "__utma=156806566.949140694.1455798901.1455798901.1455798901.1; __utmc=156806566; __utmz=156806566.1455798901.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)", 210 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 211 | "Upgrade-Insecure-Requests": "1", 212 | "X-Forwarded-Proto": "http", 213 | "Connection": "close", 214 | "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.103 Safari/537.36", 215 | "X-Forwarded-For": "103.43.112.99", 216 | "Host": "loklak.org", 217 | "Accept-Encoding": "gzip, deflate, sdch", 218 | "Accept-Language": "en-US,en;q=0.8", 219 | "X-Real-IP": "103.43.112.99" 220 | } 221 | } 222 | } 223 | 224 | Settings of the loklak server (strictly only for localhost clients) 225 | ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 226 | 227 | Using the class method ``settings()`` to returns a json of the settings 228 | being used by the loklak server 229 | 230 | Hello test - Check if the server is responding properly and is online 231 | ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 232 | 233 | Using the object created above ``l.hello()`` returns a json response of 234 | the server status 235 | 236 | When the server is online, the json should read 237 | 238 | .. code:: json 239 | 240 | {"status": "ok"} 241 | 242 | Peers - API To find out the loklak peers 243 | '''''''''''''''''''''''''''''''''''''''' 244 | 245 | Finding the list of loklak peers, use the object created above 246 | ``l.peers()`` which returns a json response containing all the peers 247 | connected to ``loklak.org`` 248 | 249 | Users API 250 | ''''''''' 251 | 252 | What this can do ? 253 | 254 | - Fetch the details of one user 255 | - Fetch the details of the user along with number of their followes and 256 | following 257 | - Fetch only the followers / following of a particular user 258 | 259 | Query Structure: 260 | ``l.user(, , )`` 261 | 262 | | ```` is a string, e.g. ``'loklak_app'`` 263 | | ```` and ```` is a numeric or a 264 | string or ``None`` 265 | 266 | | e.g. 267 | | 1. ``l.user('loklak_app')`` 268 | | 2. ``l.user('loklak_app', 1000)`` - 1000 followers of ``loklak_app`` 269 | | 3. ``l.user('loklak_app', 1000, 1000)`` - 1000 followers and following 270 | of ``loklak_app`` 271 | | 4. ``l.user('loklak_app', None, 1000)`` - 1000 following of 272 | ``loklak_app`` 273 | 274 | Accounts API 275 | '''''''''''' 276 | 277 | LOCALHOST ONLY, Loklak server running on port ``localhost:9000`` 278 | 279 | To query the user account details of the data within the loklak server, 280 | use ``l.account('name')`` where ``'name'`` is the screen\_name of the 281 | user whose information is required. 282 | 283 | To update the user details within the server, package a ``json`` object 284 | with the following parameters and other parameters which needs to be 285 | pushed to the server and use the ``action=update`` where ``action`` is 286 | the 2nd parameter of the ``account()`` api 287 | 288 | ``l.account('name', 'update', '{ json object }')`` 289 | 290 | Search API 291 | '''''''''' 292 | 293 | Public search API for the scraped tweets from Twitter. 294 | 295 | Query structure: 296 | ``search('querycontent', 'since date', 'until date', 'from a specific user', '# of tweets')`` 297 | 298 | e.g. ``l.search('doctor who')`` 299 | 300 | A search result in json looks as follows. 301 | 302 | .. code:: json 303 | 304 | { 305 | "search_metadata" : { 306 | "itemsPerPage" : "100", 307 | "count" : "100", 308 | "count_twitter_all" : 0, 309 | "count_twitter_new" : 100, 310 | "count_backend" : 0, 311 | "count_cache" : 97969, 312 | "hits" : 97969, 313 | "period" : 18422, 314 | "query" : "doctor who", 315 | "client" : "103.43.112.99", 316 | "time" : 4834, 317 | "servicereduction" : "false", 318 | "scraperInfo" : "http://kaskelix.de:9000,local" 319 | }, 320 | "statuses" : [ { 321 | "created_at" : "2015-03-03T19:30:43.000Z", 322 | "screen_name" : "exanonym77s", 323 | "text" : "check #DoctorWho forums #TheDayOfTheDoctor #TheMaster @0rb1t3r http://www.thedoctorwhoforum.com/ https://pic.twitter.com/FvW6J9WMCw", 324 | "link" : "https://twitter.com/ronakpw/status/572841550834737152", 325 | "id_str" : "572841550834737152", 326 | "source_type" : "TWITTER", 327 | "provider_type" : "SCRAPED", 328 | "retweet_count" : 0, 329 | "favourites_count" : 0, 330 | "hosts" : [ "www.thedoctorwhoforum.com", "pic.twitter.com" ], 331 | "hosts_count" : 2, 332 | "links" : [ "http://www.thedoctorwhoforum.com/", "https://pic.twitter.com/FvW6J9WMCw" ], 333 | "links_count" : 2, 334 | "mentions" : [ "@0rb1t3r" ], 335 | "mentions_count" : 1, 336 | "hashtags" : [ "DoctorWho", "TheDayOfTheDoctor", "TheMaster" ], 337 | "hashtags_count" : 3, 338 | "without_l_len" : 62, 339 | "without_lu_len" : 62, 340 | "without_luh_len" : 21, 341 | "user" : { 342 | "name" : "Example User Anyone", 343 | "screen_name" : "exanonym77s", 344 | "profile_image_url_https" : "https://pbs.twimg.com/profile_images/567071565473267713/4hiyjKkF_bigger.jpeg", 345 | "appearance_first" : "2015-03-03T19:31:30.269Z", 346 | "appearance_latest" : "2015-03-03T19:31:30.269Z" 347 | } 348 | }, ... 349 | ] 350 | } 351 | 352 | Mentioning the Since and Until dates 353 | 354 | e.g. ``l.search('sudheesh001', '2015-01-10', '2015-01-21')`` 355 | 356 | Which results in a json as follows 357 | 358 | .. code:: json 359 | 360 | { 361 | "search_metadata" : { 362 | "itemsPerPage" : "100", 363 | "count" : "100", 364 | "count_twitter_all" : 0, 365 | "count_twitter_new" : 100, 366 | "count_backend" : 0, 367 | "count_cache" : 97969, 368 | "hits" : 97969, 369 | "period" : 18422, 370 | "query" : "doctor who", 371 | "client" : "103.43.112.99", 372 | "time" : 4834, 373 | "servicereduction" : "false", 374 | "scraperInfo" : "http://kaskelix.de:9000,local" 375 | }, 376 | "statuses" : [ { 377 | "timestamp" : "2016-05-11T16:53:46.615Z", 378 | "created_at" : "2016-05-11T16:52:59.000Z", 379 | "screen_name" : "BelleRinger1", 380 | "text" : "I would love to see http://www.cultbox.co.uk/?p=53662", 381 | "link" : "https://twitter.com/BelleRinger1/status/730440578031190016", 382 | "id_str" : "730440578031190016", 383 | "source_type" : "TWITTER", 384 | "provider_type" : "SCRAPED", 385 | "retweet_count" : 0, 386 | "favourites_count" : 0, 387 | "images" : [ ], 388 | "images_count" : 0, 389 | "audio" : [ ], 390 | "audio_count" : 0, 391 | "videos" : [ ], 392 | "videos_count" : 0, 393 | "place_name" : "", 394 | "place_id" : "", 395 | "place_context" : "ABOUT", 396 | "hosts" : [ "www.cultbox.co.uk" ], 397 | "hosts_count" : 1, 398 | "links" : [ "http://www.cultbox.co.uk/?p=53662" ], 399 | "links_count" : 1, 400 | "mentions" : [ ], 401 | "mentions_count" : 0, 402 | "hashtags" : [ ], 403 | "hashtags_count" : 0, 404 | "classifier_language" : "english", 405 | "classifier_language_probability" : 6.95489E-8, 406 | "without_l_len" : 19, 407 | "without_lu_len" : 19, 408 | "without_luh_len" : 19, 409 | "user" : { 410 | "screen_name" : "BelleRinger1", 411 | "user_id" : "2497345790", 412 | "name" : "Belle Gaudreau", 413 | "profile_image_url_https" : "https://pbs.twimg.com/profile_images/723262970805907456/RbMnyEqs_bigger.jpg", 414 | "appearance_first" : "2016-05-11T16:53:46.615Z", 415 | "appearance_latest" : "2016-05-11T16:53:46.615Z" 416 | } 417 | }, ... 418 | ] 419 | } 420 | 421 | Valid parameters for ``since`` and ``until`` can also be ``None`` or any 422 | ``YMD`` date format. Looking towards the future releases to resolve this 423 | to any date format. 424 | 425 | The ``from a specific user`` parameter makes sure that the results 426 | obtained for the given query are only from a specific user. 427 | 428 | e.g. ``l.search('doctor who', '2015-01-10', '2015-01-21','0rb1t3r')`` 429 | 430 | The ``# of tweets`` parameter is how many tweets will be returned. 431 | 432 | e.g. ``l.search('avengers', None, None, 'Iron_Man', 3)`` 433 | 434 | Aggregations API 435 | '''''''''''''''' 436 | 437 | GeoLocation API 438 | ''''''''''''''' 439 | 440 | Loklak allows you to fetch required information about a country or city. 441 | 442 | e.g. ``l.geocode(['Barcelona'])``, ``l.geocode(['place1', 'place2'])`` 443 | 444 | .. |PyPI version| image:: https://badge.fury.io/py/python-loklak-api.svg 445 | :target: https://badge.fury.io/py/python-loklak-api 446 | .. |Build Status| image:: https://travis-ci.org/loklak/loklak_python_api.svg?branch=master 447 | :target: https://travis-ci.org/loklak/loklak_python_api 448 | .. |Codecov branch| image:: https://img.shields.io/codecov/c/github/loklak/loklak_python_api/master.svg?style=flat-square&label=Codecov+Coverage 449 | :target: https://codecov.io/gh/loklak/loklak_python_api 450 | .. |Coverage Status| image:: https://coveralls.io/repos/github/loklak/loklak_python_api/badge.svg?branch=master 451 | :target: https://coveralls.io/github/loklak/loklak_python_api?branch=master 452 | .. |Code Health| image:: https://landscape.io/github/loklak/loklak_python_api/master/landscape.svg?style=flat 453 | :target: https://landscape.io/github/loklak/loklak_python_api/master 454 | .. |Dependency Status| image:: https://gemnasium.com/badges/github.com/loklak/loklak_python_api.svg 455 | :target: https://gemnasium.com/github.com/loklak/loklak_python_api 456 | -------------------------------------------------------------------------------- /loklak.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | """The module that handles the main interface of loklak.""" 4 | from __future__ import (absolute_import, division, 5 | print_function, unicode_literals) 6 | 7 | import json 8 | import re 9 | import requests 10 | from xmljson import badgerfish as bf 11 | import lxml.etree as et 12 | from json import dumps 13 | import csv 14 | 15 | 16 | class Loklak(object): 17 | """The fields for the Loklak object. 18 | 19 | Additionaly, it contains methods that can be used for accessing 20 | the Loklak API services like geocode, markdown, etc. 21 | 22 | """ 23 | 24 | baseUrl = 'http://api.loklak.org/' 25 | baseUrlSusi = 'https://api.susi.ai/' 26 | name = None 27 | followers = None 28 | following = None 29 | query = None 30 | since = None 31 | until = None 32 | source = None 33 | count = None 34 | fields = None 35 | from_user = None 36 | fields = None 37 | limit = None 38 | action = None 39 | data = {} 40 | 41 | def __init__(self, baseUrl='http://api.loklak.org/'): 42 | """Loklak class constructor. 43 | 44 | Args: 45 | baseUrl (str): Base URL for accessing the APIs (default=http://api.loklak.org/). 46 | 47 | """ 48 | baseUrl = re.findall('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', baseUrl) 49 | try: 50 | if baseUrl[0]: 51 | if baseUrl[0] != 'http://api.loklak.org/': 52 | url_test = self.hello() 53 | if url_test['status'] == 'ok': 54 | self.baseUrl = baseUrl[0] 55 | else: 56 | self.baseUrl = baseUrl[0] 57 | except IndexError: 58 | pass 59 | 60 | def getBaseUrl(self): 61 | """Return the string value of baseUrl.""" 62 | return self.baseUrl 63 | 64 | def status(self, status_application='api/status.json'): 65 | """Retrieve a json response about the status of the server.""" 66 | url_to_give = self.baseUrl + status_application 67 | return_to_user = requests.get(url_to_give) 68 | if return_to_user.status_code == 200: 69 | return return_to_user.json() 70 | else: 71 | return_to_user = {} 72 | return json.dumps(return_to_user) 73 | 74 | def xmlToJson(self, xmlData = None): 75 | """Convert XML to JSON as the service.""" 76 | jsonData = '[]' 77 | if xmlData: 78 | jsonData = dumps(bf.data(et.fromstring(xmlData))) 79 | return jsonData 80 | 81 | def csvToJson(self, csvData = None, fieldnamesList = None): 82 | """Convert CSV to JSON as the service.""" 83 | jsonData = '' 84 | if csvData: 85 | data = csv.DictReader( csvData, fieldnames = fieldnamesList) 86 | jsonData = json.dumps( [ row for row in data ] ) 87 | return out 88 | 89 | def hello(self): 90 | """Retrieve a json response about the basic status of the server.""" 91 | hello_application = 'api/hello.json' 92 | url_to_give = self.baseUrl+hello_application 93 | return_to_user = requests.get(url_to_give) 94 | if return_to_user.status_code == 200: 95 | return return_to_user.json() 96 | else: 97 | return_to_user = {} 98 | return json.dumps(return_to_user) 99 | 100 | def geocode(self, places=None): 101 | """Provide geocoding of place names to location coordinates. 102 | 103 | Args: 104 | places (list): A list of place names. 105 | 106 | Examples: 107 | >>> l.geocode(['Barcelona']) 108 | {u'locations': {u'Barcelona': {u'country': u'Spain', 109 | u'country_code': u'ES', 110 | u'location':[2.15898974899153, 111 | 41.38879005861875], 112 | ... 113 | 114 | Returns: 115 | json: The geocoding results based on the given place(s) name. 116 | 117 | """ 118 | geo_application = 'api/geocode.json' 119 | url_to_give = self.baseUrl+geo_application 120 | params = {} 121 | params['places'] = places 122 | return_to_user = requests.get(url_to_give, params=params) 123 | if return_to_user.status_code == 200: 124 | return return_to_user.json() 125 | else: 126 | return_to_user = {} 127 | return json.dumps(return_to_user) 128 | 129 | def get_map(self, latitude, longitude, width=500, height=500, 130 | zoom=8, text=""): 131 | """Visualize map using Loklak service. 132 | 133 | Args: 134 | latitude (float): Latitude value. 135 | longtitude (float): Longitude value. 136 | width (int): Width (default=500). 137 | height (int): Height (default=500). 138 | zoom (int): Zoom value (default=8). 139 | text (str): Value of text like Hello. 140 | 141 | Returns: 142 | bytes: An encoded map image. 143 | 144 | """ 145 | map_application = 'vis/map.png' 146 | params = {'text': text, 'mlat': latitude, 'mlon': longitude, 147 | 'width': width, 'height': height, 'zoom': zoom} 148 | return_to_user = requests.get(self.baseUrl + map_application, 149 | params=params, stream=True) 150 | if return_to_user.status_code == 200: 151 | return return_to_user.raw.read() 152 | else: 153 | return '' 154 | 155 | def get_markdown(self, text, color_text="000000", color_bg="ffffff", 156 | padding="10", uppercase="true"): 157 | """Provide an image with text on it. 158 | 159 | Args: 160 | text (str): Text to be printed, markdown possible. 161 | color_text: 6-character hex code for the color. 162 | color_bg: 6-character hex code for the color. 163 | padding: Space around text. 164 | uppercase: by default true. If true the text is printed UPPERCASE. 165 | 166 | Returns: 167 | bytes: An encoded image. 168 | 169 | """ 170 | map_application = 'vis/markdown.png' 171 | params = {'text': text, 'color_text': color_text, 'color_background': color_bg, 172 | 'padding': padding, 'uppercase': uppercase} 173 | return_to_user = requests.get(self.baseUrl + map_application, 174 | params=params, stream=True) 175 | if return_to_user.status_code == 200: 176 | return return_to_user.raw.read() 177 | else: 178 | return '' 179 | 180 | def peers(self): 181 | """Retrieve the peers of a user.""" 182 | peers_application = 'api/peers.json' 183 | url_to_give = self.baseUrl+peers_application 184 | return_to_user = requests.get(url_to_give) 185 | if return_to_user.status_code == 200: 186 | return return_to_user.json() 187 | else: 188 | return_to_user = {} 189 | return json.dumps(return_to_user) 190 | 191 | def push(self, data=None): 192 | """Push servlet for twitter like messages. 193 | 194 | Note: 195 | The API of this function has a restrictions 196 | which only localhost clients are granted. 197 | 198 | Args: 199 | data: A search result object. 200 | 201 | Returns: 202 | json: Status about the message is pushed or not. 203 | 204 | """ 205 | push_application = 'api/push.json' 206 | url_to_give = self.baseUrl + push_application 207 | headers = { 208 | 'User-Agent': ('Mozilla/5.0 (Android 4.4; Mobile; rv:41.0)' 209 | ' Gecko/41.0 Firefox/41.0'), 210 | 'From': 'info@loklak.org' 211 | } 212 | if data: 213 | self.data = data 214 | params = {} 215 | params['data'] = json.dumps(self.data) 216 | return_to_user = requests.post(url_to_give, data=params) 217 | if return_to_user: 218 | return return_to_user.json() 219 | else: 220 | return_to_user = {} 221 | return_to_user['error'] = ('Something went wrong,' 222 | ' looks like the query is wrong.') 223 | return json.dumps(return_to_user) 224 | else: 225 | return_to_user = {} 226 | return_to_user['error'] = ('Something went wrong,' 227 | ' looks like the data is not correct.') 228 | return json.dumps(return_to_user) 229 | 230 | 231 | def user(self, name=None, followers=None, following=None): 232 | """Retrieve Twitter user information. 233 | 234 | Args: 235 | name (str): Twitter screen name of the user. 236 | followers (int): Followers of the user. 237 | following (int): Accounts the user is following. 238 | 239 | Returns: 240 | json: User information, including who they are following, and who follows them. 241 | 242 | """ 243 | user_application = 'api/user.json' 244 | url_to_give = self.baseUrl+user_application 245 | self.name = name 246 | self.followers = followers 247 | self.following = following 248 | if name: 249 | params = {} 250 | params['screen_name'] = self.name 251 | if followers is not None: 252 | params['followers'] = self.followers 253 | if following is not None: 254 | params['following'] = self.following 255 | 256 | return_to_user = requests.get(url_to_give, params=params) 257 | if return_to_user.status_code == 200: 258 | return return_to_user.json() 259 | else: 260 | return_to_user = {} 261 | return json.dumps(return_to_user) 262 | else: 263 | return_to_user = {} 264 | return_to_user['error'] = ('No user name given to query. Please' 265 | ' check and try again') 266 | return json.dumps(return_to_user) 267 | 268 | def settings(self): 269 | """Retrieve the settings of the application. 270 | 271 | Note: 272 | The API of this function has a restrictions 273 | which only localhost clients are granted. 274 | 275 | Returns: 276 | json: The settings of the application. 277 | 278 | """ 279 | settings_application = 'api/settings.json' 280 | url_to_give = self.baseUrl + settings_application 281 | return_to_user = requests.get(url_to_give) 282 | if return_to_user.status_code == 200: 283 | return return_to_user.json() 284 | else: 285 | return_to_user = {} 286 | return_to_user['error'] = ('This API has access restrictions:' 287 | ' only localhost clients are granted.') 288 | return json.dumps(return_to_user) 289 | 290 | def search(self, query=None, since=None, until=None, from_user=None, count=None): 291 | """Handle the searching. 292 | 293 | Args: 294 | query (str): Query term. 295 | since (str): Only messages after the date (including the date), =yyyy-MM-dd or yyyy-MM-dd_HH:mm. 296 | until (str): Only messages before the date (excluding the date), =yyyy-MM-dd or yyyy-MM-dd_HH:mm. 297 | from_user (str): Only messages published by the user. 298 | count (int): The wanted number of results. 299 | 300 | Returns: 301 | json: Search results from API. 302 | 303 | """ 304 | search_application = 'api/search.json' 305 | url_to_give = self.baseUrl+search_application 306 | self.query = query 307 | self.since = since 308 | self.until = until 309 | self.from_user = from_user 310 | self.count = count 311 | if query: 312 | params = {} 313 | params['query'] = self.query 314 | if since: 315 | params['query'] = params['query'] + ' since:'+self.since 316 | if until: 317 | params['query'] = params['query'] + ' until:'+self.until 318 | if from_user: 319 | params['query'] = params['query'] + ' from:'+self.from_user 320 | if count: 321 | params['count'] = self.count 322 | return_to_user = requests.get(url_to_give, params=params) 323 | if return_to_user.status_code == 200: 324 | return return_to_user.json() 325 | else: 326 | return_to_user = {} 327 | return_to_user['error'] = ('Something went wrong, looks like' 328 | ' the server is down.') 329 | return json.dumps(return_to_user) 330 | else: 331 | return_to_user = {} 332 | return_to_user['error'] = ('No Query string has been' 333 | ' given to query for an account') 334 | return json.dumps(return_to_user) 335 | 336 | def suggest(self, query=None, count=None, order=None, orderby=None,since=None, until=None): 337 | """Retrieve a list of queries based on the given criteria. 338 | 339 | Args: 340 | query (str): To get a list of queries which match; to get all latest: leave query empty. 341 | count (int): Number of queries. 342 | order (str): Possible values: desc, asc; default: desc. 343 | orderby (str): A field name of the query index schema, i.e. retrieval_next or query_count. 344 | since (str): Left bound for a query time. 345 | until (str): Right bound for a query time. 346 | 347 | Returns: 348 | json: A list of queries in the given order. 349 | 350 | """ 351 | suggest_application = 'api/suggest.json' 352 | url_to_give = self.baseUrl+suggest_application 353 | params = {} 354 | if query: 355 | params['query'] = query 356 | if count: 357 | params['count'] = count 358 | if order: 359 | params['order'] = order 360 | if since: 361 | params['since'] = since 362 | if until: 363 | params['until'] = until 364 | print(params) 365 | return_to_user = requests.get(url_to_give, params=params) 366 | print(return_to_user.url) 367 | if return_to_user.status_code == 200: 368 | return return_to_user.json() 369 | else : 370 | return_to_user = {} 371 | return_to_user['error'] = ('Something went wrong,' 372 | ' looks like the server is down.') 373 | return json.dumps(return_to_user) 374 | 375 | def aggregations(self, query=None, since=None, until=None, 376 | fields=None, limit=6, count=0): 377 | """Give the aggregations of the application. 378 | 379 | Args: 380 | query (str): Query term. 381 | since (str): Only messages after the date (including the date), =yyyy-MM-dd or yyyy-MM-dd_HH:mm. 382 | until (str): Only messages before the date (excluding the date), =yyyy-MM-dd or yyyy-MM-dd_HH:mm. 383 | fields (str): Aggregation fields for search facets, like "created_at,mentions". 384 | limit (int): A limitation of number of facets for each aggregation. 385 | count (int): The wanted number of results. 386 | 387 | Returns: 388 | json: Aggregations of the application. 389 | 390 | """ 391 | aggregations_application = 'api/search.json' 392 | url_to_give = self.baseUrl+aggregations_application 393 | self.query = query 394 | self.since = since 395 | self.until = until 396 | self.fields = fields 397 | self.limit = limit 398 | self.count = count 399 | if query: 400 | params = {} 401 | params['query'] = self.query 402 | if since: 403 | params['query'] = params['query']+' since:'+self.since 404 | if until: 405 | params['query'] = params['query']+' until:'+self.until 406 | if fields: 407 | if isinstance(fields, list): 408 | params['fields'] = ','.join(self.fields) 409 | else: 410 | params['fields'] = self.fields 411 | 412 | params['count'] = self.count 413 | params['source'] = 'cache' 414 | return_to_user = requests.get(url_to_give, params=params) 415 | if return_to_user.status_code == 200: 416 | return return_to_user 417 | else: 418 | return_to_user = {} 419 | return_to_user['error'] = ('Something went wrong,' 420 | ' looks like the server is down.') 421 | return json.dumps(return_to_user) 422 | else: 423 | return_to_user = {} 424 | return_to_user['error'] = ('No Query string has been given to run ' 425 | 'query for aggregations') 426 | return json.dumps(return_to_user) 427 | 428 | def account(self, name=None, action=None, data=None): 429 | """Provide the storage and retrieval of the user account data. 430 | 431 | Note: 432 | The API of this function has a restrictions which only localhost 433 | clients are granted. If you want to retrieve the user account 434 | information, just fill the 'name' argument, and do not fill any 435 | args. 436 | 437 | If you want to store one or more account objects, fill the 438 | 'action' argument with "update" then submit that object 439 | (these objects) inside the 'data' argument. 440 | 441 | The data object must have this following form: 442 | { 443 | "screen_name" : "test", // primary key for the user 444 | "source_type" : "TWITTER", // the application which created the token, by default "TWITTER" 445 | "oauth_token" : "abc", // the oauth token 446 | "oauth_token_secret" : "def", // the oauth token_secret 447 | "authentication_first" : "2015-06-07T09:39:22.341Z", // optional 448 | "authentication_latest" : "2015-06-07T09:39:22.341Z", // optional 449 | "apps" : {"wall" : {"type" : "horizontal"}} // any json 450 | } 451 | 452 | Args: 453 | name (str): Twitter screen name of the user. 454 | action (str): Proper values are either or 'update'. 455 | data (str): An object to store (if you set action=update, you must submit this data object). 456 | 457 | Returns: 458 | json: Information about user's account if the 'action' argument is empty (retrieving). 459 | 460 | """ 461 | account_application = 'account.json' 462 | url_to_give = 'http://localhost:9000/api/'+account_application 463 | self.name = name 464 | self.data = data 465 | self.action = action 466 | # Simple GET Query 467 | headers = { 468 | 'User-Agent': ('Mozilla/5.0 (Android 4.4; Mobile; rv:41.0)' 469 | ' Gecko/41.0 Firefox/41.0'), 470 | 'From': 'info@loklak.org' 471 | } 472 | if name: 473 | params = {} 474 | params['screen_name'] = self.name 475 | return_to_user = requests.get(url_to_give, params=params, 476 | headers=headers) 477 | if return_to_user.status_code == 200: 478 | return return_to_user.json() 479 | else: 480 | return_to_user = {} 481 | return_to_user['error'] = ('Something went wrong,' 482 | ' looks like the query is wrong.') 483 | return json.dumps(return_to_user) 484 | # if action = update and data is provided, then make request 485 | elif self.action == 'update' and data: 486 | params = {} 487 | params['action'] = self.action 488 | params['data'] = self.data 489 | return_to_user = requests.post(url_to_give, 490 | params=params, headers=headers) 491 | if return_to_user.status_code == 200: 492 | return return_to_user.json() 493 | else: 494 | return_to_user = {} 495 | return_to_user['error'] = ('Something went wrong,' 496 | ' looks like the query is wrong.') 497 | return json.dumps(return_to_user) 498 | else: 499 | return_to_user = {} 500 | return_to_user['error'] = ('No Query string has been given' 501 | ' given to query for an account') 502 | return json.dumps(return_to_user) 503 | --------------------------------------------------------------------------------