ITI0011RUS:Tweets

Allikas: Kursused
Redaktsioon seisuga 26. märts 2015, kell 07:37 kasutajalt Aleksandr (arutelu | kaastöö)
Mine navigeerimisribale Mine otsikasti

Вернуться на страницу предмета

Задание дополняется!

Срок сдачи 6. - 7. апреля.

Описание задания

Программа должна делать запрос в социальную сеть Twitter и получать оттуда список твитов. Запрос делается на недавно добавленные твиты по заданному месторасположению и их список показывается пользователю.

Функционал:

  • Считвается место, которое задается пользователем (например, Tallinn)
  • Программа узнает географические координаты введенного места (широта, долгота)
  • полученные географические координаты используются, для задания точки и радиуса, где следует искать твиты
  • Используя Twitter API посылается запрос, в него передаются координаты точки центра и радиус.
  • Ответ Twitteri API принимается, оттуда считываются необходимые данные и выводятся на экран.
  • Программе можно задать место также в виде параметра при запуске.

Дополнительные части:

  • Буферизация мест в файл (чтобы не нужно было каждый раз искать, например, координаты Таллинна, которые не меняются).
  • Сортировка твитов
  • Интерактивное управление программой
  • Тестируемый код (составление программы в соответствии с шаблоном)

Дополнительная информация

Координаты места

Используем сервис OpenStreetMap Nominatim (Nominatim wiki leht). Следует отослать сервису запрос, в котором следует указать желаемое месторасположение, и в ответе получите координаты искомого места (шитору и долготу) и координаты квадрата, окружающего эту центральную точку (bounding box).

Пример запроса: http://nominatim.openstreetmap.org/search?q=Tallinn&format=xml

Отослав такой запрос получите результат (в целях компактности показана лишь самая важная часть ответа):

<searchresults timestamp="Sat, 13 Sep 14 21:47:21 +0000" 
attribution="Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright" 
querystring="Tallinn" polygon="false" 
exclude_place_ids="98174326,11438224,6000303521,6919504,6893196,86869124,15103978,5983246058" 
more_url="http://nominatim.openstreetmap.org/search?format=xml&
exclude_place_ids=98174326,11438224,6000303521,6919504,6893196,86869124,15103978,5983246058&accept-language=en-
US,en;q=0.5&q=Tallinn">
<place place_id="98174326" osm_type="relation" osm_id="2164745" place_rank="16" 
boundingbox="59.351806640625,59.5915794372559,24.5501689910889,24.9262847900391" lat="59.4372155" 
lon="24.7453688" display_name="Tallinn, Harju maakond, Estonia" class="place" type="city" 
importance="0.7819722223575" icon="http://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png"/>
...
</searchresults>

В результате получите несколько различных мест с именем Tallinn (или в названии которых содержится это слово). Самая первая запись для нас подойдет. Таким образом, вы считываете первую запись под элементом "place". Поскольку для отсылки запроса в Twitter нам понадобятся координаты центра и радиус (поскольку ищем результаты с большой площади), то интересующие нас атрибуты "lat" и "lon" (широта и долгота), которые обозначают координаты центральной точки, а также атрибут "boundingbox", который задает линию, ограничивающие окрестности данной точки. В данном примере lat="59.4372155", lon="24.7453688", boundingbox="59.351806640625,59.5915794372559,24.5501689910889,24.9262847900391".

Область поиска в Twitter-е задается точкой центра и радиусом. В качестве центральной точки можно использовать полученные значения "lat" и "lon" напрямую. Для вычисления радиуса следует использовать значение "boundingbox". Поскольку радиус должен быть представлен в километрах, то следует то параметрам boundingbox высчитать радиус. Внимание, в различных областях земного шара длина широты может различаться. В рамках данного задания точно высчитать радиус не требуется. Будет достаточно приблизительного значения. Здесь в описании задания никакой конкретной формулы или примера не приведено - это вам следует придумать самим. Важно, чтобы города, чьи размеры заметно отличаются, давали бы разный радиус (New York > > Tallinn > Haapsalu).

Для вас важно, что вы можете требуемые данные прочесть и преобразовать к точке центра и радиусу.

Примечание. Для решения основной части достаточно и того, если вы будете пользоваться любым другим API (например, Google Maps).

Twitter API

Twitter API (https://dev.twitter.com/docs/api/1.1) позволяет делать программе запросы в Twitter . Для решения данного задания не требуется, чтобы вы были активным пользователем Twitter.

Для использования Twitteri API вам все же необходимо создать аккаунт. После логина, можете увидеть свои приложения на этой странице: https://apps.twitter.com/ (эта ссылка также доступна по адресу dev.twitter.com внизу страницы в списке "TOOLS"). Там создайте свое приложение, где в качестве домашней страницы можете записать любой адрес URL (хоть страницу этого предмета).

После того, как приложение создано, вы увидите его в списке своих приложений. Если откроете созданное приложение, то в разделе "API keys" увидите настройки приложения. Нас интересуют данные "API key" и "API secret". Эти два значения нужны для отправления запросов к Twitter API.

Сам запрос, который слеует использовать, описан здесь: https://dev.twitter.com/rest/public/search ja https://dev.twitter.com/rest/reference/get/search/tweets

Например, запрос может быть таким: https://api.twitter.com/1.1/search/tweets.json?q=&geocode=59.437215524.7453688,1km&result_type=recent

Ссылка выше может не дать никакого результата, если вы не залогинены. Она должна давать твиты с месторасположением Tallinn в радиусе 1 км.

Немного информации о том, как авторизовать приложение для использования Twitteri API: https://dev.twitter.com/oauth .

Для того, чтобы весь процесс не был слишком сложным, при решении задания советуем пользоваться библиотекой, которая может провести аутентификацию и отослать запрос за вам. Одна такая библиотека, о которой ниже также будет пара примеров кода, нахоится здесь: http://twitter4j.org/ . При желании, можете воспользоваться любой другой.

Если вы используете twitter4j, вам нужно получить следующий файл: twitter4j-core-4.0.2.jar. Когда скачаете архив в zip-формате, этот файл будет располагаться в директории "lib". Во время написания этого руководства текущая венсия библиотеки была 4.0.2, она может в любой момент измениться - соответственно, изменятся и имена файлов. Этот jar-файд следует добавить в свой проект Eclipse: project properties > java build path > libraries > add external jars ... и найдите этот файл.

Сконфигурировать библиотеку можно следующим образом: <source lang="java"> ConfigurationBuilder cb = new ConfigurationBuilder(); cb.setDebugEnabled(true) .setApplicationOnlyAuthEnabled(true); cb.setOAuthConsumerKey(TWITTER_CUSTOMER_KEY) .setOAuthConsumerSecret(TWITTER_CUSTOMER_SECRET);

TwitterFactory tf = new TwitterFactory(cb.build()); twitter4j.Twitter twitter = tf.getInstance();

OAuth2Token token; try { token = twitter.getOAuth2Token(); } catch (TwitterException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } </source>

В примере выше значения констант TWITTER_CUSTOMER_KEY и TWITTER_CUSTOMER_SECRET взяты из настроек приложения с веб страницы (application key и application secret соответственно). Альтернативно, можете настроить библиотеку при помощи конфигурационного файла, или другими способами (больше об этом можете прочитать на веб странице библиотеки, которую вы используете).

То, как отправить запрос выбранной библиотекой, вы должны выяснить сами. Об этом на странице библиотеки должна быть пара примеров. Здесь также один пример: https://github.com/yusuke/twitter4j/blob/master/twitter4j-core/src/test/java/twitter4j/SearchAPITest.java (Он взят из директории тестов. Часто тесты содержат больше примеров кода, чем сами примеры.)


Шаблон

TwitterApplication.java: <source lang="java">


import java.util.List;

/**

* The core interface for your application.

*

* The setup of this class is the following: *

    *
  • in the constructor (not described here) * all the different service objects should be set. * E.g. setLocationSearch(), setTwitterSearch(), setCache() etc. *
  • the actual start of the application is done with: * application.run(args); *

*

* The constructor of the class which implements TwitterApplication * could be the following: *

 * public AwesomeTwitterApplication() {
 *     LocationSearch locSearch = new AwesomeLocationSearch();
 *     this.setLocationSearch(locSearch);
 *     Cache cache = new AwesomeCache();
 *     this.setCache(cache);
 *     TwitterSearch twitterSearch = new AwesomeTwitterSearch();
 *     this.setTwitterSearch(twitterSearch);
 * }
 * 
* 
* Constructor should not do anything else. The actual execution
* of the program starts with run(args).

*

* Setting all your implementations of the services in the constructor * gives the test system opportunity to test each one of those. * @author Ago * @see TwitterApplication#run(String[]) * */ public interface TwitterApplication { /** * The application will be run using arguments * from the command line (should be passed here * directly from the main method). * runWithArgs() method will be called. *

* If the arguments are empty, runInteractive() is run. * * @param args Arguments from command line (the same * which are passed to the main method). * @see TwitterApplication#runWithArgs(String[]) * @see TwitterApplication#runInteractive() */ default public void run(String[] args) { if (args.length == 0) { runInteractive(); } else { runWithArgs(args); } } /** * Tries to run the program with command line * arguments. *

* If command line arguments are not correct, * the program can exit with an error code. * @param args Arguments from command line. */ public void runWithArgs(String[] args); /** * The application will be run in an interactive mode * where the user can enter different statements * to control the program. */ public void runInteractive(); /** * Given a command as a String (in interactive mode), * this method should parse the input and return * a list of Action instances. * Usually one command should create one action, this * method gives the opportunity to have combined actions * for one command line ("query tallinn 30" - query + count). * If you don't use combined actions, just return a list * with one element in it - the Action instance to be * executed. * @param action Command string from interactive mode * @return List of actions to be executed */ public List<Action> getActionsFromInput(String action); /** * Given command line arguments this method parses * the arguments and returns a list of Action instances. * As the command line can accept several different actions * (for example query, sort and search), this method * return a list of all the actions. * @param args Command line arguments (from main method) * @return List of actions to be executed */ public List<Action> getActionsFromArguments(String[] args); /** * Given an instance of Action, it will be executed. * If you choose to implement Action.execute() * for every action, then the body of this method can just be: * action.execute() * @param action Action to be executed */ public void executeAction(Action action); /** * Executes all the actions given as a list. * The default implementation just iterates over * all the actions and calls executeAction. * @param actions A list of actions */ default public void executeActions(List<Action> actions) { for (Action action : actions) { executeAction(action); } } /** * Executes a location search using location search set with * setLocationSearch(). * Returns a query object which holds all the values for Twitter search. * Note that this method has a default implementation which * just executes a method from local location search and * returns its return value. * Use this default implementation if you don't have caching implemented. * If you need caching, you need to override this method. * @param location The location which is to be searched for * @return Query object which holds all the necessary information * about Twitter query * @see TwitterApplication#setLocationSearch(LocationSearch) */ default public TwitterQuery getQueryFromLocation(String location) { LocationSearch locationSearch = getLocationSearch(); return locationSearch.getQueryFromLocation(location); } /** * Executes a search of tweets on TwitterSearch object which * is stored via setTwitterSearch(). * Returns a list of received tweets. * @param query Query object which holds all the necessary values * @return List of Tweet objects received from Twitter search. * null if nothing received. * @see TwitterApplication#setTwitterSearch(TwitterSearch) */ default public List<Tweet> getTweets(TwitterQuery query) { TwitterSearch twitterSearch = getTwitterSearch(); return twitterSearch.getTweets(query); } /** * Stores location search object which will * be used to make queries to location search API. * This should be called from the constructor. * @param locationSearch Implementation of LocationSearch, which * can find information about location (city, country etc.). */ public void setLocationSearch(LocationSearch locationSearch); /** * Returns currently stored location search object. * @return Implementation of LocationSeach * which will be used for location search. */ public LocationSearch getLocationSearch(); /** * Stores Twitter search object which will be used * to query tweets from Twitter API. * Should be called from the constructor. * @param twitterSearch Implementation of TwitterSearch */ public void setTwitterSearch(TwitterSearch twitterSearch); /** * Returns currently stored Twitter search object. * @return Implementation of TwitterSearch * which will be used for queries. */ public TwitterSearch getTwitterSearch(); /** * Stores cache object which will be used * to cache locations in the file. * Should be called from the constructor. * @param cache Implementation of Cache */ public void setCache(Cache cache); /** * Returns currently stored cache object. * @return Implementation of Cache * which will be used for location caching. */ public Cache getCache(); /** * Stores the latest state of tweets list. * You should store your tweets using this * method after querying, sorting, searching. * @param tweets A list of tweets */ public void setTweets(List<Tweet> tweets); /** * Get the latest state of tweets list. * This method should be used for printing * and when applying sorting. * @return A list of tweets */ public List<Tweet> getTweets(); } </source>