LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_private-get-statistics-report-transactions.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 1.5 % 206 3
Test Date: 2026-01-01 16:44:56 Functions: 12.5 % 8 1

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   (C) 2025 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU Affero General Public License as published by the Free Software
       7              :   Foundation; either version 3, or (at your option) any later version.
       8              : 
       9              :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10              :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11              :   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12              : 
      13              :   You should have received a copy of the GNU General Public License along with
      14              :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15              : */
      16              : /**
      17              :  * @file taler-merchant-httpd_private-get-statistics-report-transactions.c
      18              :  * @brief implement GET /statistics-report/transactions
      19              :  * @author Christian Grothoff
      20              :  */
      21              : #include "platform.h"
      22              : #include "taler-merchant-httpd_private-get-statistics-report-transactions.h"
      23              : #include <gnunet/gnunet_json_lib.h>
      24              : #include <taler/taler_json_lib.h>
      25              : #include <taler/taler_mhd_lib.h>
      26              : 
      27              : 
      28              : /**
      29              :  * Closure for the detail_cb().
      30              :  */
      31              : struct ResponseContext
      32              : {
      33              :   /**
      34              :    * Format of the response we are to generate.
      35              :    */
      36              :   enum
      37              :   {
      38              :     RCF_JSON,
      39              :     RCF_PDF
      40              :   } format;
      41              : 
      42              :   /**
      43              :    * Stored in a DLL while suspended.
      44              :    */
      45              :   struct ResponseContext *next;
      46              : 
      47              :   /**
      48              :    * Stored in a DLL while suspended.
      49              :    */
      50              :   struct ResponseContext *prev;
      51              : 
      52              :   /**
      53              :    * Context for this request.
      54              :    */
      55              :   struct TMH_HandlerContext *hc;
      56              : 
      57              :   /**
      58              :    * Async context used to run Typst.
      59              :    */
      60              :   struct TALER_MHD_TypstContext *tc;
      61              : 
      62              :   /**
      63              :    * Response to return.
      64              :    */
      65              :   struct MHD_Response *response;
      66              : 
      67              :   /**
      68              :    * Time when we started processing the request.
      69              :    */
      70              :   struct GNUNET_TIME_Timestamp now;
      71              : 
      72              :   /**
      73              :    * Period of each bucket.
      74              :    */
      75              :   struct GNUNET_TIME_Relative period;
      76              : 
      77              :   /**
      78              :    * Granularity of the buckets. Matches @e period.
      79              :    */
      80              :   const char *granularity;
      81              : 
      82              :   /**
      83              :    * Number of buckets to return.
      84              :    */
      85              :   uint64_t count;
      86              : 
      87              :   /**
      88              :    * HTTP status to use with @e response.
      89              :    */
      90              :   unsigned int http_status;
      91              : 
      92              :   /**
      93              :    * Length of the @e labels array.
      94              :    */
      95              :   unsigned int labels_cnt;
      96              : 
      97              :   /**
      98              :    * Array of labels for the chart.
      99              :    */
     100              :   char **labels;
     101              : 
     102              :   /**
     103              :    * Data groups for the chart.
     104              :    */
     105              :   json_t *data_groups;
     106              : 
     107              : };
     108              : 
     109              : 
     110              : /**
     111              :  * DLL of requests awaiting Typst.
     112              :  */
     113              : static struct ResponseContext *rctx_head;
     114              : 
     115              : /**
     116              :  * DLL of requests awaiting Typst.
     117              :  */
     118              : static struct ResponseContext *rctx_tail;
     119              : 
     120              : 
     121              : void
     122           15 : TMH_handler_statistic_report_transactions_cleanup ()
     123              : {
     124              :   struct ResponseContext *rctx;
     125              : 
     126           15 :   while (NULL != (rctx = rctx_head))
     127              :   {
     128            0 :     GNUNET_CONTAINER_DLL_remove (rctx_head,
     129              :                                  rctx_tail,
     130              :                                  rctx);
     131            0 :     MHD_resume_connection (rctx->hc->connection);
     132              :   }
     133           15 : }
     134              : 
     135              : 
     136              : /**
     137              :  * Free resources from @a ctx
     138              :  *
     139              :  * @param[in] ctx the `struct ResponseContext` to clean up
     140              :  */
     141              : static void
     142            0 : free_rc (void *ctx)
     143              : {
     144            0 :   struct ResponseContext *rctx = ctx;
     145              : 
     146            0 :   if (NULL != rctx->tc)
     147              :   {
     148            0 :     TALER_MHD_typst_cancel (rctx->tc);
     149            0 :     rctx->tc = NULL;
     150              :   }
     151            0 :   if (NULL != rctx->response)
     152              :   {
     153            0 :     MHD_destroy_response (rctx->response);
     154            0 :     rctx->response = NULL;
     155              :   }
     156            0 :   for (unsigned int i = 0; i<rctx->labels_cnt; i++)
     157            0 :     GNUNET_free (rctx->labels[i]);
     158            0 :   GNUNET_array_grow (rctx->labels,
     159              :                      rctx->labels_cnt,
     160              :                      0);
     161            0 :   json_decref (rctx->data_groups);
     162            0 :   GNUNET_free (rctx);
     163            0 : }
     164              : 
     165              : 
     166              : /**
     167              :  * Function called with the result of a #TALER_MHD_typst() operation.
     168              :  *
     169              :  * @param cls closure
     170              :  * @param tr result of the operation
     171              :  */
     172              : static void
     173            0 : pdf_cb (void *cls,
     174              :         const struct TALER_MHD_TypstResponse *tr)
     175              : {
     176            0 :   struct ResponseContext *rctx = cls;
     177              : 
     178            0 :   rctx->tc = NULL;
     179            0 :   GNUNET_CONTAINER_DLL_remove (rctx_head,
     180              :                                rctx_tail,
     181              :                                rctx);
     182            0 :   MHD_resume_connection (rctx->hc->connection);
     183            0 :   TALER_MHD_daemon_trigger ();
     184            0 :   if (TALER_EC_NONE != tr->ec)
     185              :   {
     186              :     rctx->http_status
     187            0 :       = TALER_ErrorCode_get_http_status (tr->ec);
     188              :     rctx->response
     189            0 :       = TALER_MHD_make_error (tr->ec,
     190            0 :                               tr->details.hint);
     191            0 :     return;
     192              :   }
     193              :   rctx->http_status
     194            0 :     = MHD_HTTP_OK;
     195              :   rctx->response
     196            0 :     = TALER_MHD_response_from_pdf_file (tr->details.filename);
     197              : }
     198              : 
     199              : 
     200              : /**
     201              :  * Typically called by `lookup_statistics_amount_by_bucket2`.
     202              :  *
     203              :  * @param[in,out] cls our `struct ResponseContext` to update
     204              :  * @param bucket_start start time of the bucket
     205              :  * @param amounts_len the length of @a amounts array
     206              :  * @param amounts the cumulative amounts in the bucket
     207              :  */
     208              : static void
     209            0 : amount_by_bucket (void *cls,
     210              :                   struct GNUNET_TIME_Timestamp bucket_start,
     211              :                   unsigned int amounts_len,
     212              :                   const struct TALER_Amount amounts[static amounts_len])
     213            0 : {
     214            0 :   struct ResponseContext *rctx = cls;
     215              :   json_t *values;
     216              : 
     217            0 :   for (unsigned int i = 0; i<amounts_len; i++)
     218              :   {
     219            0 :     bool found = false;
     220              : 
     221            0 :     for (unsigned int j = 0; j<rctx->labels_cnt; j++)
     222              :     {
     223            0 :       if (0 == strcmp (amounts[i].currency,
     224            0 :                        rctx->labels[j]))
     225              :       {
     226            0 :         found = true;
     227            0 :         break;
     228              :       }
     229              :     }
     230            0 :     if (! found)
     231              :     {
     232            0 :       GNUNET_array_append (rctx->labels,
     233              :                            rctx->labels_cnt,
     234              :                            GNUNET_strdup (amounts[i].currency));
     235              :     }
     236              :   }
     237              : 
     238            0 :   values = json_array ();
     239            0 :   GNUNET_assert (NULL != values);
     240            0 :   for (unsigned int i = 0; i<rctx->labels_cnt; i++)
     241              :   {
     242            0 :     const char *label = rctx->labels[i];
     243            0 :     double d = 0.0;
     244              : 
     245            0 :     for (unsigned int j = 0; j<amounts_len; j++)
     246              :     {
     247            0 :       const struct TALER_Amount *a = &amounts[j];
     248              : 
     249            0 :       if (0 != strcmp (amounts[j].currency,
     250              :                        label))
     251            0 :         continue;
     252            0 :       d = a->value * 1.0
     253            0 :           + (a->fraction * 1.0 / TALER_AMOUNT_FRAC_BASE);
     254            0 :       break;
     255              :     } /* for all amounts */
     256            0 :     GNUNET_assert (0 ==
     257              :                    json_array_append_new (values,
     258              :                                           json_real (d)));
     259              :   } /* for all labels */
     260              : 
     261              :   {
     262              :     json_t *dg;
     263              : 
     264            0 :     dg = GNUNET_JSON_PACK (
     265              :       GNUNET_JSON_pack_timestamp ("start_date",
     266              :                                   bucket_start),
     267              :       GNUNET_JSON_pack_array_steal ("values",
     268              :                                     values));
     269            0 :     GNUNET_assert (0 ==
     270              :                    json_array_append_new (rctx->data_groups,
     271              :                                           dg));
     272              : 
     273              :   }
     274            0 : }
     275              : 
     276              : 
     277              : /**
     278              :  * Create the transaction volume report.
     279              :  *
     280              :  * @param[in,out] rctx request context to use
     281              :  * @param[in,out] charts JSON chart array to expand
     282              :  * @return #GNUNET_OK on success,
     283              :  *         #GNUNET_NO to end with #MHD_YES,
     284              :  *         #GNUNET_NO to end with #MHD_NO.
     285              :  */
     286              : static enum GNUNET_GenericReturnValue
     287            0 : make_transaction_volume_report (struct ResponseContext *rctx,
     288              :                                 json_t *charts)
     289              : {
     290              :   // FIXME: this is from example-statistics, we probably want to hard-wire the stats from this endpoint!
     291            0 :   const char *bucket_name = "sales (before refunds)";
     292              :   enum GNUNET_DB_QueryStatus qs;
     293              :   json_t *chart;
     294              :   json_t *labels;
     295              : 
     296            0 :   rctx->data_groups = json_array ();
     297            0 :   GNUNET_assert (NULL != rctx->data_groups);
     298            0 :   qs = TMH_db->lookup_statistics_amount_by_bucket2 (
     299            0 :     TMH_db->cls,
     300            0 :     rctx->hc->instance->settings.id,
     301              :     bucket_name,
     302              :     rctx->granularity,
     303              :     rctx->count,
     304              :     &amount_by_bucket,
     305              :     rctx);
     306            0 :   if (0 > qs)
     307              :   {
     308            0 :     GNUNET_break (0);
     309              :     return (MHD_YES ==
     310            0 :             TALER_MHD_reply_with_error (
     311            0 :               rctx->hc->connection,
     312              :               MHD_HTTP_INTERNAL_SERVER_ERROR,
     313              :               TALER_EC_GENERIC_DB_FETCH_FAILED,
     314              :               "lookup_statistics_amount_by_bucket"))
     315            0 :         ? GNUNET_NO : GNUNET_SYSERR;
     316              :   }
     317              : 
     318            0 :   labels = json_array ();
     319            0 :   GNUNET_assert (NULL != labels);
     320            0 :   for (unsigned int i=0; i<rctx->labels_cnt; i++)
     321              :   {
     322            0 :     GNUNET_assert (0 ==
     323              :                    json_array_append_new (labels,
     324              :                                           json_string (rctx->labels[i])));
     325            0 :     GNUNET_free (rctx->labels[i]);
     326              :   }
     327            0 :   GNUNET_array_grow (rctx->labels,
     328              :                      rctx->labels_cnt,
     329              :                      0);
     330            0 :   chart = GNUNET_JSON_PACK (
     331              :     GNUNET_JSON_pack_string ("chart_name",
     332              :                              "Sales volume"),
     333              :     GNUNET_JSON_pack_string ("y_label",
     334              :                              "Sales"),
     335              :     GNUNET_JSON_pack_array_steal ("data_groups",
     336              :                                   rctx->data_groups),
     337              :     GNUNET_JSON_pack_array_steal ("labels",
     338              :                                   labels),
     339              :     GNUNET_JSON_pack_bool ("cumulative",
     340              :                            false));
     341            0 :   rctx->data_groups = NULL;
     342            0 :   GNUNET_assert (0 ==
     343              :                  json_array_append_new (charts,
     344              :                                         chart));
     345            0 :   return GNUNET_OK;
     346              : }
     347              : 
     348              : 
     349              : /**
     350              :  * Typically called by `lookup_statistics_counter_by_bucket2`.
     351              :  *
     352              :  * @param[in,out] cls our `struct ResponseContext` to update
     353              :  * @param bucket_start start time of the bucket
     354              :  * @param counters_len the length of @a cumulative_amounts
     355              :  * @param descriptions description for the counter in the bucket
     356              :  * @param counters the counters in the bucket
     357              :  */
     358              : static void
     359            0 : count_by_bucket (void *cls,
     360              :                  struct GNUNET_TIME_Timestamp bucket_start,
     361              :                  unsigned int counters_len,
     362              :                  const char *descriptions[static counters_len],
     363              :                  uint64_t counters[static counters_len])
     364            0 : {
     365            0 :   struct ResponseContext *rctx = cls;
     366              :   json_t *values;
     367              : 
     368            0 :   for (unsigned int i = 0; i<counters_len; i++)
     369              :   {
     370            0 :     bool found = false;
     371              : 
     372            0 :     for (unsigned int j = 0; j<rctx->labels_cnt; j++)
     373              :     {
     374            0 :       if (0 == strcmp (descriptions[i],
     375            0 :                        rctx->labels[j]))
     376              :       {
     377            0 :         found = true;
     378            0 :         break;
     379              :       }
     380              :     }
     381            0 :     if (! found)
     382              :     {
     383            0 :       GNUNET_array_append (rctx->labels,
     384              :                            rctx->labels_cnt,
     385              :                            GNUNET_strdup (descriptions[i]));
     386              :     }
     387              :   }
     388              : 
     389            0 :   values = json_array ();
     390            0 :   GNUNET_assert (NULL != values);
     391            0 :   for (unsigned int i = 0; i<rctx->labels_cnt; i++)
     392              :   {
     393            0 :     const char *label = rctx->labels[i];
     394            0 :     uint64_t v = 0;
     395              : 
     396            0 :     for (unsigned int j = 0; j<counters_len; j++)
     397              :     {
     398            0 :       if (0 != strcmp (descriptions[j],
     399              :                        label))
     400            0 :         continue;
     401            0 :       v = counters[j];
     402            0 :       break;
     403              :     } /* for all amounts */
     404            0 :     GNUNET_assert (0 ==
     405              :                    json_array_append_new (values,
     406              :                                           json_integer (v)));
     407              :   } /* for all labels */
     408              : 
     409              :   {
     410              :     json_t *dg;
     411              : 
     412            0 :     dg = GNUNET_JSON_PACK (
     413              :       GNUNET_JSON_pack_timestamp ("start_date",
     414              :                                   bucket_start),
     415              :       GNUNET_JSON_pack_array_steal ("values",
     416              :                                     values));
     417            0 :     GNUNET_assert (0 ==
     418              :                    json_array_append_new (rctx->data_groups,
     419              :                                           dg));
     420              : 
     421              :   }
     422            0 : }
     423              : 
     424              : 
     425              : /**
     426              :  * Create the transaction count report.
     427              :  *
     428              :  * @param[in,out] rctx request context to use
     429              :  * @param[in,out] charts JSON chart array to expand
     430              :  * @return #GNUNET_OK on success,
     431              :  *         #GNUNET_NO to end with #MHD_YES,
     432              :  *         #GNUNET_NO to end with #MHD_NO.
     433              :  */
     434              : static enum GNUNET_GenericReturnValue
     435            0 : make_transaction_count_report (struct ResponseContext *rctx,
     436              :                                json_t *charts)
     437              : {
     438            0 :   const char *prefix = "transaction-state-";
     439              :   enum GNUNET_DB_QueryStatus qs;
     440              :   json_t *chart;
     441              :   json_t *labels;
     442              : 
     443            0 :   rctx->data_groups = json_array ();
     444            0 :   GNUNET_assert (NULL != rctx->data_groups);
     445            0 :   qs = TMH_db->lookup_statistics_counter_by_bucket2 (
     446            0 :     TMH_db->cls,
     447            0 :     rctx->hc->instance->settings.id,
     448              :     prefix,   /* prefix to match against bucket name */
     449              :     rctx->granularity,
     450              :     rctx->count,
     451              :     &count_by_bucket,
     452              :     rctx);
     453            0 :   if (0 > qs)
     454              :   {
     455            0 :     GNUNET_break (0);
     456              :     return (MHD_YES ==
     457            0 :             TALER_MHD_reply_with_error (
     458            0 :               rctx->hc->connection,
     459              :               MHD_HTTP_INTERNAL_SERVER_ERROR,
     460              :               TALER_EC_GENERIC_DB_FETCH_FAILED,
     461              :               "lookup_statistics_XXX"))
     462            0 :         ? GNUNET_NO : GNUNET_SYSERR;
     463              :   }
     464              : 
     465            0 :   labels = json_array ();
     466            0 :   GNUNET_assert (NULL != labels);
     467            0 :   for (unsigned int i=0; i<rctx->labels_cnt; i++)
     468              :   {
     469            0 :     const char *label = rctx->labels[i];
     470              : 
     471              :     /* This condition should always hold. */
     472            0 :     if (0 ==
     473            0 :         strncmp (prefix,
     474              :                  label,
     475              :                  strlen (prefix)))
     476            0 :       label += strlen (prefix);
     477            0 :     GNUNET_assert (0 ==
     478              :                    json_array_append_new (labels,
     479              :                                           json_string (label)));
     480            0 :     GNUNET_free (rctx->labels[i]);
     481              :   }
     482            0 :   GNUNET_array_grow (rctx->labels,
     483              :                      rctx->labels_cnt,
     484              :                      0);
     485            0 :   chart = GNUNET_JSON_PACK (
     486              :     GNUNET_JSON_pack_string ("chart_name",
     487              :                              "Transaction counts"),
     488              :     GNUNET_JSON_pack_string ("y_label",
     489              :                              "Number of transactions"),
     490              :     GNUNET_JSON_pack_array_steal ("data_groups",
     491              :                                   rctx->data_groups),
     492              :     GNUNET_JSON_pack_array_steal ("labels",
     493              :                                   labels),
     494              :     GNUNET_JSON_pack_bool ("cumulative",
     495              :                            false));
     496            0 :   rctx->data_groups = NULL;
     497            0 :   GNUNET_assert (0 ==
     498              :                  json_array_append_new (charts,
     499              :                                         chart));
     500            0 :   return GNUNET_OK;
     501              : }
     502              : 
     503              : 
     504              : /**
     505              :  * Handle a GET "/private/statistics-report/transactions" request.
     506              :  *
     507              :  * @param rh context of the handler
     508              :  * @param connection the MHD connection to handle
     509              :  * @param[in,out] hc context with further information about the request
     510              :  * @return MHD result code
     511              :  */
     512              : MHD_RESULT
     513            0 : TMH_private_get_statistics_report_transactions (
     514              :   const struct TMH_RequestHandler *rh,
     515              :   struct MHD_Connection *connection,
     516              :   struct TMH_HandlerContext *hc)
     517              : {
     518            0 :   struct ResponseContext *rctx = hc->ctx;
     519            0 :   struct TMH_MerchantInstance *mi = hc->instance;
     520              :   json_t *charts;
     521              : 
     522            0 :   if (NULL != rctx)
     523              :   {
     524            0 :     if (NULL == rctx->response)
     525              :     {
     526            0 :       GNUNET_break (0);
     527            0 :       return MHD_NO;
     528              :     }
     529            0 :     return MHD_queue_response (connection,
     530              :                                rctx->http_status,
     531              :                                rctx->response);
     532              :   }
     533            0 :   rctx = GNUNET_new (struct ResponseContext);
     534            0 :   rctx->hc = hc;
     535            0 :   rctx->now = GNUNET_TIME_timestamp_get ();
     536            0 :   hc->ctx = rctx;
     537            0 :   hc->cc = &free_rc;
     538            0 :   GNUNET_assert (NULL != mi);
     539              : 
     540            0 :   rctx->granularity = MHD_lookup_connection_value (connection,
     541              :                                                    MHD_GET_ARGUMENT_KIND,
     542              :                                                    "granularity");
     543            0 :   if (NULL == rctx->granularity)
     544              :   {
     545            0 :     rctx->granularity = "day";
     546            0 :     rctx->period = GNUNET_TIME_UNIT_DAYS;
     547            0 :     rctx->count = 95;
     548              :   }
     549              :   else
     550              :   {
     551              :     const struct
     552              :     {
     553              :       const char *name;
     554              :       struct GNUNET_TIME_Relative period;
     555              :       uint64_t default_counter;
     556            0 :     } map[] = {
     557              :       {
     558              :         .name = "second",
     559            0 :         .period = GNUNET_TIME_UNIT_SECONDS,
     560              :         .default_counter = 120,
     561              :       },
     562              :       {
     563              :         .name = "minute",
     564            0 :         .period = GNUNET_TIME_UNIT_MINUTES,
     565              :         .default_counter = 120,
     566              :       },
     567              :       {
     568              :         .name = "hour",
     569            0 :         .period = GNUNET_TIME_UNIT_HOURS,
     570              :         .default_counter = 48,
     571              :       },
     572              :       {
     573              :         .name = "day",
     574            0 :         .period = GNUNET_TIME_UNIT_DAYS,
     575              :         .default_counter = 95,
     576              :       },
     577              :       {
     578              :         .name = "month",
     579            0 :         .period = GNUNET_TIME_UNIT_MONTHS,
     580              :         .default_counter = 36,
     581              :       },
     582              :       {
     583              :         .name = "quarter",
     584            0 :         .period = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MONTHS,
     585              :                                                  3),
     586              :         .default_counter = 40,
     587              :       },
     588              :       {
     589              :         .name = "year",
     590            0 :         .period = GNUNET_TIME_UNIT_YEARS,
     591              :         .default_counter = 10
     592              :       },
     593              :       {
     594              :         .name = NULL
     595              :       }
     596              :     };
     597              : 
     598            0 :     rctx->count = 0;
     599            0 :     for (unsigned int i = 0; map[i].name != NULL; i++)
     600              :     {
     601            0 :       if (0 == strcasecmp (map[i].name,
     602              :                            rctx->granularity))
     603              :       {
     604            0 :         rctx->count = map[i].default_counter;
     605            0 :         rctx->period = map[i].period;
     606            0 :         break;
     607              :       }
     608              :     }
     609            0 :     if (0 == rctx->count)
     610              :     {
     611            0 :       GNUNET_break_op (0);
     612            0 :       return TALER_MHD_reply_with_error (
     613              :         connection,
     614              :         MHD_HTTP_BAD_REQUEST,
     615              :         TALER_EC_GENERIC_PARAMETER_MALFORMED,
     616              :         "granularity");
     617              :     }
     618              :   } /* end handling granularity */
     619              : 
     620              :   /* Figure out desired output format */
     621              :   {
     622              :     const char *mime;
     623              : 
     624            0 :     mime = MHD_lookup_connection_value (connection,
     625              :                                         MHD_HEADER_KIND,
     626              :                                         MHD_HTTP_HEADER_ACCEPT);
     627            0 :     if (NULL == mime)
     628            0 :       mime = "application/json";
     629            0 :     if (0 == strcmp (mime,
     630              :                      "application/json"))
     631              :     {
     632            0 :       rctx->format = RCF_JSON;
     633              :     }
     634            0 :     else if (0 == strcmp (mime,
     635              :                           "application/pdf"))
     636              :     {
     637              : 
     638            0 :       rctx->format = RCF_PDF;
     639              :     }
     640              :     else
     641              :     {
     642            0 :       GNUNET_break_op (0);
     643            0 :       return TALER_MHD_REPLY_JSON_PACK (
     644              :         connection,
     645              :         MHD_HTTP_NOT_ACCEPTABLE,
     646              :         GNUNET_JSON_pack_string ("hint",
     647              :                                  mime));
     648              :     }
     649              :   } /* end of determine output format */
     650              : 
     651            0 :   TALER_MHD_parse_request_number (connection,
     652              :                                   "count",
     653              :                                   &rctx->count);
     654              : 
     655              :   /* create charts */
     656            0 :   charts = json_array ();
     657            0 :   GNUNET_assert (NULL != charts);
     658              :   {
     659              :     enum GNUNET_GenericReturnValue ret;
     660              : 
     661            0 :     ret = make_transaction_volume_report (rctx,
     662              :                                           charts);
     663            0 :     if (GNUNET_OK != ret)
     664            0 :       return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
     665            0 :     ret = make_transaction_count_report (rctx,
     666              :                                          charts);
     667            0 :     if (GNUNET_OK != ret)
     668            0 :       return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
     669              :   }
     670              : 
     671              :   /* generate response */
     672              :   {
     673              :     struct GNUNET_TIME_Timestamp start_date;
     674              :     struct GNUNET_TIME_Timestamp end_date;
     675              :     json_t *root;
     676              : 
     677            0 :     end_date = rctx->now;
     678              :     start_date
     679            0 :       = GNUNET_TIME_absolute_to_timestamp (
     680              :           GNUNET_TIME_absolute_subtract (
     681              :             end_date.abs_time,
     682              :             GNUNET_TIME_relative_multiply (rctx->period,
     683            0 :                                            rctx->count)));
     684            0 :     root = GNUNET_JSON_PACK (
     685              :       GNUNET_JSON_pack_string ("business_name",
     686              :                                mi->settings.name),
     687              :       GNUNET_JSON_pack_timestamp ("start_date",
     688              :                                   start_date),
     689              :       GNUNET_JSON_pack_timestamp ("end_date",
     690              :                                   end_date),
     691              :       GNUNET_JSON_pack_time_rel ("bucket_period",
     692              :                                  rctx->period),
     693              :       GNUNET_JSON_pack_array_steal ("charts",
     694              :                                     charts));
     695              : 
     696            0 :     switch (rctx->format)
     697              :     {
     698            0 :     case RCF_JSON:
     699            0 :       return TALER_MHD_reply_json (connection,
     700              :                                    root,
     701              :                                    MHD_HTTP_OK);
     702            0 :     case RCF_PDF:
     703              :       {
     704            0 :         struct TALER_MHD_TypstDocument doc = {
     705              :           .form_name = "transactions",
     706              :           .data = root
     707              :         };
     708              : 
     709            0 :         GNUNET_CONTAINER_DLL_insert (rctx_head,
     710              :                                      rctx_tail,
     711              :                                      rctx);
     712            0 :         MHD_suspend_connection (connection);
     713            0 :         rctx->tc = TALER_MHD_typst (TMH_cfg,
     714              :                                     false, /* remove on exit */
     715              :                                     "merchant",
     716              :                                     1, /* one document, length of "array"! */
     717              :                                     &doc,
     718              :                                     &pdf_cb,
     719              :                                     rctx);
     720            0 :         json_decref (root);
     721            0 :         if (NULL == rctx->tc)
     722              :         {
     723            0 :           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     724              :                       "Client requested PDF, but Typst is unavailable\n");
     725            0 :           return TALER_MHD_reply_with_error (
     726              :             connection,
     727              :             MHD_HTTP_NOT_IMPLEMENTED,
     728              :             TALER_EC_EXCHANGE_GENERIC_NO_TYPST_OR_PDFTK,
     729              :             NULL);
     730              :         }
     731            0 :         return MHD_YES;
     732              :       }
     733              :     } /* end switch */
     734              :   }
     735            0 :   GNUNET_assert (0);
     736              :   return MHD_NO;
     737              : }
     738              : 
     739              : 
     740              : /* end of taler-merchant-httpd_private-get-statistics-report-transactions.c */
        

Generated by: LCOV version 2.0-1