diff options
author | Antony Kellermann | 2018-05-29 20:53:07 -0400 |
---|---|---|
committer | Antony Kellermann | 2018-05-29 20:53:07 -0400 |
commit | eedb9c51064050dc7021a97560d7ff8c2f4eb821 (patch) | |
tree | bdfa4add22f56da0d5b5802136e283de4a2ce1d0 | |
parent | 4638bd317573fa8a8cd9ecebb822196f84c871d0 (diff) | |
download | aur-eedb9c51064050dc7021a97560d7ff8c2f4eb821.tar.gz |
Added News struct to easily store news and moved news API call as a threadable function to api.c
-rw-r--r-- | api.c | 84 | ||||
-rw-r--r-- | api.h | 20 | ||||
-rw-r--r-- | info.c | 69 | ||||
-rw-r--r-- | info.h | 4 | ||||
-rw-r--r-- | main.c | 6 |
5 files changed, 119 insertions, 64 deletions
@@ -1,5 +1,11 @@ #include "api.h" +News* api_news_init(void) { + News* pNews = malloc(sizeof(News)); + pointer_alloc_check(pNews); + return pNews; +} + Info* api_info_init(void) { Info* pInfo = malloc(sizeof(Info)); pointer_alloc_check(pInfo); @@ -10,7 +16,7 @@ Info* api_info_init(void) { .gross_profit = EMPTY, .cash = EMPTY, .debt = EMPTY, .eps = {EMPTY, EMPTY, EMPTY, EMPTY}, .fiscal_period[0][0] = '\0', .fiscal_period[1][0] = '\0', .fiscal_period[2][0] = '\0', .fiscal_period[3][0] = '\0', .eps_year_ago = {EMPTY, EMPTY, EMPTY, EMPTY}, .change_1d = EMPTY, - .change_7d = EMPTY, .change_30d = EMPTY, .points = NULL + .change_7d = EMPTY, .change_30d = EMPTY, .points = NULL, .articles = NULL, .num_articles = EMPTY }; return pInfo; } @@ -197,6 +203,60 @@ void* iex_store_chart(void* vpInfo) { return NULL; } +void* iex_store_news(void* vpInfo) { + Info* symbol_info = vpInfo; + if (symbol_info->symbol[0] == '\0') + return NULL; + + if (symbol_info->num_articles == EMPTY) + symbol_info->num_articles = DEFAULT_NUM_ARTICLES; + + char iex_api_string[URL_MAX_LENGTH]; + sprintf(iex_api_string, "https://api.iextrading.com/1.0/stock/%s/news/last/%d", symbol_info->symbol, + symbol_info->num_articles); + String* pString = api_curl_data(iex_api_string); + if (pString == NULL) + return NULL; + + Json* jobj = json_tokener_parse(pString->data), * idx; + if (jobj == NULL) { // Invalid symbol + string_destroy(&pString); + return NULL; + } + size_t len = json_object_array_length(jobj); + if (len < (unsigned) symbol_info->num_articles) + symbol_info->num_articles = (int)len; + + symbol_info->articles = malloc(sizeof(News*) * symbol_info->num_articles); + pointer_alloc_check(symbol_info->articles); + + for (int i = 0; i < symbol_info->num_articles; i++) { + symbol_info->articles[i] = api_news_init(); + pointer_alloc_check(symbol_info->articles[i]); + idx = json_object_array_get_idx(jobj, (size_t) i); + strcpy(symbol_info->articles[i]->headline, json_object_get_string(json_object_object_get(idx, "headline"))); + strcpy(symbol_info->articles[i]->source, json_object_get_string(json_object_object_get(idx, "source"))); + strncpy(symbol_info->articles[i]->date, json_object_get_string(json_object_object_get(idx, "datetime")), 10); + symbol_info->articles[i]->date[10] = '\0'; + strcpy(symbol_info->articles[i]->summary, json_object_get_string(json_object_object_get(idx, "summary"))); + strip_tags(symbol_info->articles[i]->summary); // Summary will be html formatted, so must strip tags + strcpy(symbol_info->articles[i]->url, json_object_get_string(json_object_object_get(idx, "url"))); + strcpy(symbol_info->articles[i]->related, json_object_get_string(json_object_object_get(idx, "related"))); + int related_num = 0; + for (size_t j = 0; j < strlen(symbol_info->articles[i]->related); j++) { // List only first five related symbols + if (symbol_info->articles[i]->related[j] == ',') + related_num++; + if (related_num == 5) { + symbol_info->articles[i]->related[j] = '\0'; + break; + } + } + } + json_object_put(jobj); + string_destroy(&pString); + return NULL; +} + void* morningstar_store_info(void* vpInfo) { Info* symbol_info = vpInfo; char today_str[DATE_MAX_LENGTH], five_year_str[DATE_MAX_LENGTH], morningstar_api_string[URL_MAX_LENGTH]; @@ -273,15 +333,15 @@ void* coinmarketcap_store_info(void* vpInfo) { Info* iex_get_info(const char* symbol) { Info* symbol_info = api_info_init(); strcpy(symbol_info->symbol, symbol); - pthread_t threads[5]; - void* (*funcs[5]) (void*) = { - iex_store_company, iex_store_quote, iex_store_stats, iex_store_earnings, iex_store_chart + pthread_t threads[6]; + void* (*funcs[6]) (void*) = { + iex_store_company, iex_store_quote, iex_store_stats, iex_store_earnings, iex_store_chart, iex_store_news }; - for (int i = 0; i < 5; i++) + for (int i = 0; i < 6; i++) if (pthread_create(&threads[i], NULL, funcs[i], symbol_info)) EXIT_MSG("Error creating thread!"); - for (int i = 0; i < 5; i++) + for (int i = 0; i < 6; i++) if (pthread_join(threads[i], NULL)) EXIT_MSG("Error joining thread!"); @@ -357,8 +417,18 @@ Info* api_get_info(const char* symbol) { return coinmarketcap_get_info(symbol); } +void api_news_destroy(News** phNews) { + free(*phNews); + *phNews = NULL; +} + void api_info_destroy(Info** phInfo) { - free((*phInfo)->points); + Info* pInfo = *phInfo; + free(pInfo->points); + if (pInfo->articles != NULL) + for (int i = 0; i < pInfo->num_articles; i++) + api_news_destroy(&pInfo->articles[i]); + free(pInfo->articles); free(*phInfo); *phInfo = NULL; }
\ No newline at end of file @@ -14,6 +14,7 @@ #define URL_MAX_LENGTH 2048 #define INFO_TEXT_MAX 2048 #define EMPTY (-999) +#define DEFAULT_NUM_ARTICLES 3 #include <stddef.h> #include <curl/curl.h> @@ -21,6 +22,15 @@ #include <pthread.h> #include "string-tick.h" +typedef struct news_article { + char headline[INFO_TEXT_MAX]; + char source[INFO_TEXT_MAX]; + char date[DATE_MAX_LENGTH]; + char summary[INFO_TEXT_MAX]; + char url[URL_MAX_LENGTH]; + char related[INFO_TEXT_MAX]; +} News; + typedef struct info { /* Company */ char symbol[SYMBOL_MAX_LENGTH]; // ex. AAPL @@ -65,8 +75,14 @@ typedef struct info { double change_7d; // Percent change since 7 days ago double change_30d; // Percent change since 30 days ago double* points; // Array of one price per day, startings five years previously + + /* News */ + News** articles; // Array of News pointers + int num_articles; // Number of News pointers in array } Info; +News* api_news_init(void); + /** * Allocates an Info struct and returns a pointer to it. All numbers are set to EMPTY, all strings are NULL * terminated at length 0, and all pointers are set to NULL. @@ -137,6 +153,8 @@ void* iex_store_earnings(void* vpInfo); */ void* iex_store_chart(void* vpInfo); +void* iex_store_news(void* vpInfo); + /** * Designed for threading * @@ -204,6 +222,8 @@ Info* api_get_check_info(const char* symbol); */ Info* api_get_info(const char* symbol); +void api_news_destroy(News** phNews); + /** * Destroys Info object and frees memory. Sets the pointer to the Info to NULL * @param phInfo the Info to destroy @@ -20,7 +20,7 @@ void symbol_print_info(const char* symbol) { time_t time = symbol_info->intraday_time / 1000; // divide into second instead of milliseconds struct tm* ts = localtime(&time); strftime(time_str, 32, "%F %T", ts); - mvprintw(0, 13, "%s", time_str); + mvprintw(0, 18, "%s", time_str); } mvprintw(0, (int) (28 + strlen(symbol_info->name) + strlen(symbol_info->symbol)), "24H 7D "); if (symbol_info->change_30d != EMPTY) @@ -77,7 +77,7 @@ void symbol_print_info(const char* symbol) { else mvwprintw(company_win, getcury(company_win), getmaxx(company_win) / 2, "Volume unavailable.\n"); if (symbol_info->pe_ratio != EMPTY) - wprintw(company_win, "P/E Ratio: %ld", symbol_info->pe_ratio); + wprintw(company_win, "P/E Ratio: %lf", symbol_info->pe_ratio); else wprintw(company_win, "P/E Ratio unavailable."); if (symbol_info->div_yield != EMPTY) @@ -109,65 +109,30 @@ void symbol_print_info(const char* symbol) { WINDOW* graph_win = newwin(GRAPH_HEIGHT, GRAPH_WIDTH, GRAPH_Y, GRAPH_X); graph_printw(graph_win, symbol_info, NULL); } - while (getch() != 'q'); endwin(); api_info_destroy(&symbol_info); } -void symbol_print_news(const char* symbol, int num_articles) { - if (num_articles > 50) +void news_print(const char* symbol, int num_articles) { + if (num_articles > 50 || num_articles < 1) RET_MSG("You cannot request more than 50 articles."); - char iex_api_string[URL_MAX_LENGTH]; - sprintf(iex_api_string, "https://api.iextrading.com/1.0/stock/%s/news/last/%d", symbol, num_articles); - String* pString = api_curl_data(iex_api_string); - if (pString == NULL) - return; - - if (strcmp(pString->data, "Unknown symbol") == 0) { // Invalid symbol - string_destroy(&pString); - RET_MSG("Invalid symbol."); - } - - Json* jobj = json_tokener_parse(pString->data); - size_t len = json_object_array_length(jobj); - if (len == 0) { - json_object_put(jobj); - string_destroy(&pString); - RET_MSG("No articles available."); - } - - Json* idx; - const char* headline, * source, *url; - char date[DATE_MAX_LENGTH]; - for (size_t i = 0; i < len; i++) { - idx = json_object_array_get_idx(jobj, i); - headline = json_object_get_string(json_object_object_get(idx, "headline")); // Headline - source = json_object_get_string(json_object_object_get(idx, "source")); // Source - strncpy(date, json_object_get_string(json_object_object_get(idx, "datetime")), 10); // Date - date[10] = '\0'; // null terminate date before time - char summary[strlen(json_object_get_string(json_object_object_get(idx, "summary")))]; // Summary - strcpy(summary, json_object_get_string(json_object_object_get(idx, "summary"))); - strip_tags(summary); // Summary will be html formatted, so must strip tags - url = json_object_get_string(json_object_object_get(idx, "url")); // URL - char related[strlen(json_object_get_string(json_object_object_get(idx, "related")))]; // Related - strcpy(related, json_object_get_string(json_object_object_get(idx, "related"))); - int related_num = 0; - for (size_t j = 0; j < strlen(related); j++) { // List only first five related symbols - if (related[j] == ',') - related_num++; - if (related_num == 5) { - related[j] = '\0'; - break; - } - } - printf("%s | %s | %s\n%s\n%s | Related: %s\n\n", headline, source, date, summary, url, related); + Info* symbol_info = api_info_init(); + strcpy(symbol_info->symbol, symbol); + symbol_info->num_articles = num_articles; + iex_store_news(symbol_info); + if (symbol_info->articles == NULL) { + api_info_destroy(&symbol_info); + RET_MSG("Invalid symbol"); } - json_object_put(jobj); - string_destroy(&pString); + for (int i = 0; i < symbol_info->num_articles; i++) + printf("%s | %s | %s\n%s\n%s | Related: %s\n\n", + symbol_info->articles[i]->headline, symbol_info->articles[i]->source, symbol_info->articles[i]->date, + symbol_info->articles[i]->summary, symbol_info->articles[i]->url, symbol_info->articles[i]->related); + api_info_destroy(&symbol_info); } -void symbol_graph(const char* symbol, const char* symbol2) { +void graph_print(const char* symbol, const char* symbol2) { Info* symbol_info = api_get_check_info(symbol), *symbol_info2 = NULL; if (symbol_info == NULL) RET_MSG("Invalid symbol") @@ -47,14 +47,14 @@ void symbol_print_info(const char* symbol); * @param symbol stock/etf symbol * @param num_articles number of articles to print (max 50) */ -void symbol_print_news(const char* symbol, int num_articles); +void news_print(const char* symbol, int num_articles); /** * Graphs a security in stdscr. If symbol2 is not NULL, also graphs that security. * @param symbol security to graph * @param symbol2 optional second security */ -void symbol_graph(const char* symbol, const char* symbol2); +void graph_print(const char* symbol, const char* symbol2); /** * -- Main input loop for graphing -- @@ -32,7 +32,7 @@ int main(int argc, char* argv[]) { int num_articles = 3; // Default if (argc == 4) num_articles = (int) strtol(argv[3], NULL, 10); - symbol_print_news(sym, num_articles); + news_print(sym, num_articles); } //Encrypt/decrypt @@ -45,14 +45,14 @@ int main(int argc, char* argv[]) { // Graph else if (strcmp(cmd, "graph") == 0 && argc == 3) - symbol_graph(sym, NULL); + graph_print(sym, NULL); // Compare else if (strcmp(cmd, "cmp") == 0 && argc == 4) { char sym2[strlen(argv[3]) + 1]; strcpy(sym2, argv[3]); strtoupper(sym2); - symbol_graph(sym, sym2); + graph_print(sym, sym2); } // Check |