{"action":"created","issue":{"url":"https://api.github.com/repos/facebook/ThreatExchange/issues/2","labels_url":"https://api.github.com/repos/facebook/ThreatExchange/issues/2/labels{/name}","comments_url":"https://api.github.com/repos/facebook/ThreatExchange/issues/2/comments","events_url":"https://api.github.com/repos/facebook/ThreatExchange/issues/2/events","html_url":"https://github.com/facebook/ThreatExchange/pull/2","id":66164664,"number":2,"title":"pytx (pie-tex) library","user":{"login":"mgoffin","id":260873,"avatar_url":"https://avatars.githubusercontent.com/u/260873?v=3","gravatar_id":"","url":"https://api.github.com/users/mgoffin","html_url":"https://github.com/mgoffin","followers_url":"https://api.github.com/users/mgoffin/followers","following_url":"https://api.github.com/users/mgoffin/following{/other_user}","gists_url":"https://api.github.com/users/mgoffin/gists{/gist_id}","starred_url":"https://api.github.com/users/mgoffin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mgoffin/subscriptions","organizations_url":"https://api.github.com/users/mgoffin/orgs","repos_url":"https://api.github.com/users/mgoffin/repos","events_url":"https://api.github.com/users/mgoffin/events{/privacy}","received_events_url":"https://api.github.com/users/mgoffin/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/facebook/ThreatExchange/labels/CLA%20Signed","name":"CLA Signed","color":"009900"}],"state":"open","locked":false,"assignee":{"login":"jessek","id":1842175,"avatar_url":"https://avatars.githubusercontent.com/u/1842175?v=3","gravatar_id":"","url":"https://api.github.com/users/jessek","html_url":"https://github.com/jessek","followers_url":"https://api.github.com/users/jessek/followers","following_url":"https://api.github.com/users/jessek/following{/other_user}","gists_url":"https://api.github.com/users/jessek/gists{/gist_id}","starred_url":"https://api.github.com/users/jessek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jessek/subscriptions","organizations_url":"https://api.github.com/users/jessek/orgs","repos_url":"https://api.github.com/users/jessek/repos","events_url":"https://api.github.com/users/jessek/events{/privacy}","received_events_url":"https://api.github.com/users/jessek/received_events","type":"User","site_admin":false},"milestone":null,"comments":6,"created_at":"2015-04-03T15:17:50Z","updated_at":"2015-04-18T00:36:07Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/facebook/ThreatExchange/pulls/2","html_url":"https://github.com/facebook/ThreatExchange/pull/2","diff_url":"https://github.com/facebook/ThreatExchange/pull/2.diff","patch_url":"https://github.com/facebook/ThreatExchange/pull/2.patch"},"body":"This PR adds the pytx library for interacting with the ThreatExchange API in Python.\r\n\r\nThe library allows you to initialize once with your APP-ID and APP-SECRET. From there all pytx objects as well as those created through the API will have access to those values for submitting requests.\r\n\r\nCurrent classes include:\r\n\r\n```python\r\nfrom pytx import (\r\n    ThreatIndicator\r\n    ThreatExchangeMember\r\n    Malware\r\n)\r\n```\r\n\r\nWhen using the classes to access content you have a few options for how to get results. For example:\r\n\r\n```python\r\nfrom pytx import init\r\nfrom pytx import ThreatIndicator\r\n\r\ninit('<app-id>', '<app-secret>')\r\n\r\n# Get results which will return a generator which handles\r\n# following the pager for you. Looping over the generator\r\n# will return instantiated ThreatIndicator objects.\r\nresults = ThreatIndicator.objects(text=\"www.facebook.com\")\r\n\r\n# Get results using __raw__ which allows me to specify my\r\n# GET parameters myself. Will still return a generator like the\r\n# previous example.\r\nresults = ThreatIndicator.objects(__raw__={'text': 'www.facebook.com'})\r\n\r\n# Get results but instead of using the generator I want the\r\n# response body returned so I can manually handle it\r\n# myself. This does not handle paging for you.\r\nresults = ThreatIndicator.objects(text=\"www.facebook.com\", full_response=True)\r\n\r\n# Get results and use the generator so we get looping \r\n# and paging, but instead of returning instantiated \r\n# ThreatIndicator objects, return each object as a dictionary.\r\nresults = ThreatIndicator.objects(text=\"www.facebook.com\", dict_generator=True)\r\n```\r\n\r\nThis should make things a bit more flexible for people who want more control or a more classful approach to interacting with the API. Also there is a request Broker which can be used directly for people who want full control over the URL and parameters when making requests as well as the full response. The Broker is used internally for the classes, but using it directly allows you to do things like the following:\r\n\r\n```python\r\nfrom pytx import init\r\nfrom pytx.request import Broker\r\n\r\ninit('<app-id>', '<app-secret>')\r\n\r\nresults = Broker.get(\"https://graph.facebook.com/threat_indicators/\",\r\n                     params={'text': 'www.facebook.com'})\r\n```\r\n\r\nWhen querying for details you can also cater to your situation by using an instantiated or uninstantiated class:\r\n\r\n```python\r\ni = ThreatIndicator()\r\ni.id = '1234'\r\ni.details()\r\n\r\ni = ThreatIndicator.details(id='1234')\r\n```\r\n\r\nDepending on the situation you are in it might be more useful to populate your already-instantiated object with the details, or if you don't have one it makes it very easy to get one and get the details in one step.\r\n\r\nThere is a vocabulary available for developers (and the library itself) to leverage for accessing ThreatExchange properly. It should be kept up-to-date over time as vocabulary changes. This makes it very easy for us to change field names, URLs, limits, etc. without disrupting existing code.\r\n\r\nIt comes with a `scripts` directory which contains scripts that replicate (as best as I can tell) all of the existing ThreatExchange Python scripts sans the POST script (I didn't want to submit random test data). When performing a `setup.py install` these scripts will also be installed so developers can use them as a reference if they wish.\r\n\r\nI've started a `docs` directory with the hopes of getting this on readthedocs, but it seems like readthedocs doesn't like building documentation from subfolders (or I haven't been able to figure out how). It begs the question of whether we should make pytx its own repo/project, or just move all of the documentation to the root of the ThreatExchange repo and use readthedocs from there?\r\n\r\nI haven't added this to PyPi as I figured we should get it in its final resting place before setting that up, so `pip install pytx` won't work yet.\r\n\r\n*NOTE*: This PR could definitely use a few more eyes and lots of testing to make sure it's working well. There are a coupe TODO's in the vocabulary as some of the API documentation has changed over time and I don't want to remove large chunks of the vocab until it's confirmed it can get nuked. As long as we are happy with the core setup of the library then I'm good with proceeding. I'd hate for it to get accepted and we have a fundamental shift in how we want the library to work after people have started using it :)"},"comment":{"url":"https://api.github.com/repos/facebook/ThreatExchange/issues/comments/94106305","html_url":"https://github.com/facebook/ThreatExchange/pull/2#issuecomment-94106305","issue_url":"https://api.github.com/repos/facebook/ThreatExchange/issues/2","id":94106305,"user":{"login":"mgoffin","id":260873,"avatar_url":"https://avatars.githubusercontent.com/u/260873?v=3","gravatar_id":"","url":"https://api.github.com/users/mgoffin","html_url":"https://github.com/mgoffin","followers_url":"https://api.github.com/users/mgoffin/followers","following_url":"https://api.github.com/users/mgoffin/following{/other_user}","gists_url":"https://api.github.com/users/mgoffin/gists{/gist_id}","starred_url":"https://api.github.com/users/mgoffin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mgoffin/subscriptions","organizations_url":"https://api.github.com/users/mgoffin/orgs","repos_url":"https://api.github.com/users/mgoffin/repos","events_url":"https://api.github.com/users/mgoffin/events{/privacy}","received_events_url":"https://api.github.com/users/mgoffin/received_events","type":"User","site_admin":false},"created_at":"2015-04-18T00:36:07Z","updated_at":"2015-04-18T00:36:07Z","body":"Looking over the python options, dateutil was the first thing that popped into my head. It also seems like the best options. It would add another dependency outside of `requests` if we choose to add it. If that's not a decent option we could just leave it and let people submit whatever they want and deal with error responses?"}}