module analytics.api; import handy_http_primitives; import handy_http_handlers.path_handler : PathMapping, GetMapping; import handy_http_data : writeJsonBody; import std.datetime; import profile.data; import profile.service; import profile.api : PROFILE_PATH; import analytics.util; import util.money; import util.data; @PathMapping(HttpMethod.GET, PROFILE_PATH ~ "/analytics/category-spend-time-series") void handleGetCategorySpendTimeSeries(ref ServerHttpRequest request, ref ServerHttpResponse response) { auto ds = getProfileDataSource(request); serveJsonFromProperty(response, ds, "analytics.categorySpendTimeSeries"); } @GetMapping(PROFILE_PATH ~ "/analytics/balance-time-series") void handleGetBalanceTimeSeries(ref ServerHttpRequest request, ref ServerHttpResponse response) { import analytics.modules.balances; auto ds = getProfileDataSource(request); Currency currency = Currency.ofCode(request.getParamAs!string("currency", Currencies.USD.code)); auto data = computeBalanceTimeSeries(ds, currency, getTimeRange(request)); writeJsonBody(response, data); } @GetMapping(PROFILE_PATH ~ "/analytics/category-spend") void handleGetCategorySpend(ref ServerHttpRequest request, ref ServerHttpResponse response) { import analytics.modules.category_spend; auto ds = getProfileDataSource(request); Currency currency = Currency.ofCode(request.getParamAs!string("currency", Currencies.USD.code)); long categoryParentId = request.getParamAs!long("parentId", -1); Optional!ulong categoryParentIdOpt = categoryParentId == -1 ? Optional!(ulong).empty() : Optional!(ulong).of(categoryParentId); auto data = computeCategorySpend(ds, currency, getTimeRange(request), categoryParentIdOpt); writeJsonBody(response, data); } /** * Helper method to serve JSON analytics data to a client by fetching it * directly from the user's profile properties table and writing it to the * response. * Params: * response = The response to write to. * ds = The datasource to serve data from. * key = The name of the analytics property to read data from. */ private void serveJsonFromProperty(ref ServerHttpResponse response, ref ProfileDataSource ds, string key) { PropertiesRepository propsRepo = ds.getPropertiesRepository(); string jsonStr = propsRepo.findProperty(key).orElse(null); if (jsonStr is null || jsonStr.length == 0) { response.status = HttpStatus.NOT_FOUND; response.writeBodyString("No data found for " ~ key); } else { response.writeBodyString(jsonStr, ContentTypes.APPLICATION_JSON); } } private TimeRange getTimeRange(in ServerHttpRequest request) { long fromTimestampMs = request.getParamAs!long("from", -1); long toTimestampMs = request.getParamAs!long("to", -1); return TimeRange( fromTimestampMs == -1 ? Optional!(SysTime).empty() : Optional!(SysTime).of(toSysTime(fromTimestampMs)), toTimestampMs == -1 ? Optional!(SysTime).empty() : Optional!(SysTime).of(toSysTime(toTimestampMs)) ); }