read pkg version template from file
[ppastats.git] / src / html.c
1 /*
2  * Copyright (C) 2011-2012 jeanfi@gmail.com
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301 USA
18  */
19
20 #include <libintl.h>
21 #define _(String) gettext(String)
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/time.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29
30 #include <json/json.h>
31
32 #include "html.h"
33 #include "io.h"
34 #include "log.h"
35 #include "lp.h"
36 #include "lp_ws.h"
37 #include "ppastats.h"
38
39 static const char *footer;
40 static const char *ppa_body;
41 static const char *pkg_body;
42 static const char *pkg_version_body;
43 static const char *header;
44
45 static char *path_new(const char *dir, const char *file, const char *suffixe)
46 {
47         char *path;
48
49         /* [dir]/[file][suffixe] */
50         path = malloc(strlen(dir)+1+
51                       strlen(file)+
52                       (suffixe ? strlen(suffixe) : 0) +
53                       1);
54
55         strcpy(path, dir);
56         strcat(path, "/");
57         strcat(path, file);
58         strcat(path, suffixe);
59
60         return path;
61 }
62
63 static const char *get_header()
64 {
65         const char *path;
66
67         if (!header) {
68                 path = DEFAULT_WWW_DIR"/header.tpl";
69                 header = file_get_content(path);
70
71                 if (!header)
72                         log_err("Failed to read header template: %s", path);
73         }
74
75         return header;
76 }
77
78 static const char *get_footer()
79 {
80         const char *path;
81
82         if (!footer) {
83                 path = DEFAULT_WWW_DIR"/footer.tpl";
84                 footer = file_get_content(path);
85
86                 if (!footer)
87                         log_err("Failed to read footer template: %s", path);
88         }
89
90         return footer;
91 }
92
93 static const char *get_pkg_version_body()
94 {
95         const char *path;
96
97         if (!pkg_version_body) {
98                 path = DEFAULT_WWW_DIR"/pkg_version.tpl";
99                 pkg_version_body = file_get_content(path);
100
101                 if (!pkg_version_body)
102                         log_err("Failed to read package version template: %s",
103                                 path);
104         }
105
106         return pkg_version_body;
107 }
108 static const char *get_ppa_body()
109 {
110         const char *path;
111
112         if (!ppa_body) {
113                 path = DEFAULT_WWW_DIR"/ppa.tpl";
114                 ppa_body = file_get_content(path);
115
116                 if (!ppa_body)
117                         log_err("Failed to read PPA template: %s", path);
118         }
119
120         return ppa_body;
121 }
122
123 static const char *get_pkg_body()
124 {
125         const char *path;
126
127         if (!pkg_body) {
128                 path = DEFAULT_WWW_DIR"/pkg.tpl";
129                 pkg_body = file_get_content(path);
130
131                 if (!pkg_body)
132                         log_err("Failed to read package template: %s", path);
133         }
134
135         return pkg_body;
136 }
137
138 static struct json_object *date_to_json(struct tm *tm)
139 {
140         json_object *json;
141
142         json = json_object_new_array();
143         json_object_array_add(json, json_object_new_int(tm->tm_year+1900));
144         json_object_array_add(json, json_object_new_int(tm->tm_mon+1));
145         json_object_array_add(json, json_object_new_int(tm->tm_mday));
146
147         return json;
148 }
149
150 static void json_add_ddts(json_object *json,
151                           struct daily_download_total **ddts)
152 {
153         json_object *json_ddt, *json_ddts;
154         struct daily_download_total *ddt;
155
156         json_ddts = json_object_new_array();
157         json_object_object_add(json, "ddts", json_ddts);
158
159         if (!ddts)
160                 return ;
161
162         while (*ddts) {
163                 ddt = *ddts;
164
165                 json_ddt = json_object_new_object();
166                 json_object_object_add(json_ddt,
167                                        "value",
168                                        json_object_new_int(ddt->count));
169                 json_object_object_add(json_ddt,
170                                        "time",
171                                        date_to_json(&ddt->date));
172
173                 json_object_array_add(json_ddts, json_ddt);
174
175                 ddts++;
176         }
177 }
178
179 static json_object *distro_to_json(struct distro_stats *d)
180 {
181         json_object *json;
182
183         json = json_object_new_object();
184
185         json_object_object_add(json,
186                                "name",
187                                json_object_new_string(d->name));
188
189         json_object_object_add(json,
190                                "count",
191                                json_object_new_int(d->download_count));
192
193         json_add_ddts(json, d->ddts);
194
195         return json;
196 }
197
198 static json_object *
199 pkg_to_json(struct ppa_stats *ppa, struct package_stats *pkg)
200 {
201         json_object *json, *json_versions, *json_distros, *json_distro;
202         struct version_stats **versions;
203         struct distro_stats **distros, *d;
204
205         json = json_object_new_object();
206
207         json_object_object_add(json,
208                                "ppa_name", json_object_new_string(ppa->name));
209         json_object_object_add(json,
210                                "ppa_owner",
211                                json_object_new_string(ppa->owner));
212
213         json_object_object_add(json,
214                                "name", json_object_new_string(pkg->name));
215
216         json_versions = json_object_new_array();
217         json_object_object_add(json, "versions", json_versions);
218         versions = pkg->versions;
219         while (*versions) {
220                 json_object_array_add
221                         (json_versions,
222                          json_object_new_string((*versions)->version));
223
224                 versions++;
225         }
226
227         distros = pkg->distros;
228         if (distros) {
229                 json_distros = json_object_new_array();
230                 json_object_object_add(json, "distros", json_distros);
231
232                 while (*distros) {
233                         d = *distros;
234
235                         if (d->download_count) {
236                                 json_distro = distro_to_json(d);
237
238                                 json_object_array_add(json_distros,
239                                                       json_distro);
240                         }
241
242                         distros++;
243                 }
244         }
245
246         json_add_ddts(json, pkg->daily_download_totals);
247
248         return json;
249 }
250
251 static char *version_to_json(struct ppa_stats *ppa,
252                              struct package_stats *pkg,
253                              struct version_stats *ver)
254 {
255         char *ret;
256         struct distro_stats **distros, *distro;
257         json_object *json, *json_distros, *json_distro, *json_archs, *json_arch;
258         struct arch_stats **archs;
259
260         json = json_object_new_object();
261
262         json_object_object_add(json,
263                                "ppa_name", json_object_new_string(ppa->name));
264         json_object_object_add(json,
265                                "ppa_owner",
266                                json_object_new_string(ppa->owner));
267
268         json_object_object_add(json,
269                                "pkg_name", json_object_new_string(pkg->name));
270
271         json_object_object_add(json,
272                                "name", json_object_new_string(ver->version));
273
274         json_add_ddts(json, ver->daily_download_totals);
275
276         distros = ver->distros;
277         json_distros = json_object_new_array();
278         json_object_object_add(json, "distros", json_distros);
279         while (*distros) {
280                 distro = *distros;
281                 json_distro = json_object_new_object();
282
283                 json_object_array_add(json_distros, json_distro);
284
285                 json_object_object_add(json_distro,
286                                        "name",
287                                        json_object_new_string(distro->name));
288
289                 archs = distro->archs;
290                 json_archs = json_object_new_array();
291                 json_object_object_add(json_distro, "archs", json_archs);
292                 while (*archs) {
293                         json_arch = json_object_new_object();
294
295                         json_object_object_add
296                                 (json_arch,
297                                  "name",
298                                  json_object_new_string((*archs)->name));
299
300                         json_object_object_add
301                                 (json_arch,
302                                  "count",
303                                  json_object_new_int((*archs)->download_count));
304
305                         json_object_array_add(json_archs, json_arch);
306                         archs++;
307                 }
308
309                 distros++;
310         }
311
312         ret = strdup(json_object_to_json_string(json));
313
314         json_object_put(json);
315
316         return ret;
317 }
318
319 static json_object *ppa_to_json(struct ppa_stats *ppa)
320 {
321         json_object *json, *json_pkgs, *json_pkg;
322         struct package_stats **pkgs;
323
324         json = json_object_new_object();
325
326         json_object_object_add(json,
327                                "ppa_name", json_object_new_string(ppa->name));
328         json_object_object_add(json,
329                                "ppa_owner",
330                                json_object_new_string(ppa->owner));
331
332         json_add_ddts(json, ppa->daily_download_totals);
333
334         pkgs = ppa->packages;
335         json_pkgs = json_object_new_array();
336         json_object_object_add(json, "packages", json_pkgs);
337         while (*pkgs) {
338                 json_pkg = json_object_new_object();
339                 json_object_array_add(json_pkgs, json_pkg);
340
341                 json_object_object_add(json_pkg, "name",
342                                        json_object_new_string((*pkgs)->name));
343
344                 json_object_object_add
345                         (json_pkg, "count",
346                          json_object_new_int((*pkgs)->download_count));
347
348                 pkgs++;
349         }
350
351         return json;
352 }
353
354 static void
355 version_to_html(struct ppa_stats *ppa,
356                 struct package_stats *pkg,
357                 struct version_stats *version,
358                 const char *dir)
359 {
360         char *f_name, *path;
361         FILE *f;
362         const char *footer;
363         const char *pkg_ver;
364
365         pkg_ver = get_pkg_version_body();
366         if (!pkg_ver) {
367                 log_err("Failed to get package version template");
368                 return ;
369         }
370
371         f_name = malloc(strlen(pkg->name)+1+strlen(version->version)+1);
372         sprintf(f_name, "%s_%s", pkg->name, version->version);
373
374         path = path_new(dir, f_name, ".html");
375         f = fopen(path, "w");
376
377         if (!f) {
378                 log_err(_("failed to open: %s"), path);
379                 return ;
380         }
381
382         fprintf(f, pkg_ver, version_to_json(ppa, pkg, version));
383
384         footer = get_footer();
385         if (footer)
386                 fputs(footer, f);
387
388         fclose(f);
389
390         free(path);
391         free(f_name);
392 }
393
394 static void
395 create_html(const char *path,
396             const char *title,
397             const char *body_template,
398             const char *script)
399 {
400         FILE *f;
401         const char *footer;
402         const char *header;
403
404         header = get_header();
405         if (!header) {
406                 log_err(_("Failed to get the header template"));
407                 return ;
408         }
409
410         f = fopen(path, "w");
411
412         if (!f) {
413                 log_err(_("Failed to open: %s"), path);
414                 return ;
415         }
416
417         fprintf(f, header, title, script);
418         fputs(body_template, f);
419
420         footer = get_footer();
421         if (footer)
422                 fputs(footer, f);
423
424         fclose(f);
425 }
426
427 static char *ppa_display_name(const struct ppa_stats *ppa)
428 {
429         char *ret;
430
431         ret = malloc(4+strlen(ppa->name)+1+strlen(ppa->owner)+1);
432
433         sprintf(ret, "ppa:%s/%s", ppa->owner, ppa->name);
434
435         return ret;
436 }
437
438 static void
439 index_to_html(struct ppa_stats *ppa, const char *dir)
440 {
441         char *path, *json_path, *dname;
442         json_object *json;
443         const char *body;
444
445         body = get_ppa_body();
446         if (!body) {
447                 log_err("Failed to create PPA page");
448                 return ;
449         }
450
451         json = ppa_to_json(ppa);
452         json_path = path_new(dir, "index", ".json");
453
454         log_debug(_("generating %s"), json_path);
455         json_object_to_file(json_path, json);
456         json_object_put(json);
457         free(json_path);
458
459         path = path_new(dir, "index", ".html");
460         dname = ppa_display_name(ppa);
461         create_html(path, dname, body, "ppastats_ppa();");
462         free(path);
463         free(dname);
464 }
465
466 static void
467 pkg_to_html(struct ppa_stats *ppa, struct package_stats *pkg, const char *dir)
468 {
469         char *path, *json_path, *script;
470         json_object *json;
471         const char *body;
472
473         body = get_pkg_body();
474         if (!body) {
475                 log_err("Failed to create package page: %s", pkg->name);
476                 return ;
477         }
478
479         json_path = path_new(dir, pkg->name, ".json");
480         json = pkg_to_json(ppa, pkg);
481         log_debug(_("Generating %s"), json_path);
482
483         json_object_to_file(json_path, json);
484         json_object_put(json);
485         free(json_path);
486
487         path = path_new(dir, pkg->name, ".html");
488         script = malloc(strlen("ppastats_pkg(\"\");")+
489                         strlen(pkg->name)+
490                         strlen(".json")+
491                         1);
492         sprintf(script, "ppastats_pkg(\"%s%s\");", pkg->name, ".json");
493
494         log_debug(_("Generating %s"), path);
495
496         create_html(path, pkg->name, body, script);
497         free(path);
498         free(script);
499 }
500
501 static void
502 pkgs_to_html(struct ppa_stats *ppa,
503              struct package_stats **pkgs,
504              const char *dir)
505 {
506         struct version_stats **versions;
507
508         while (*pkgs) {
509                 pkg_to_html(ppa, *pkgs, dir);
510
511                 versions = (*pkgs)->versions;
512                 while (*versions) {
513                         version_to_html(ppa, *pkgs, *versions, dir);
514
515                         versions++;
516                 }
517
518                 pkgs++;
519         }
520 }
521
522 void
523 ppa_to_html(const char *owner,
524             const char *ppa,
525             const char *package_status,
526             const char *output_dir,
527             const int install_static_files)
528 {
529         struct ppa_stats *ppastats;
530         char *path, *f_dst;
531         char *css_dir, *js_dir;
532         int i;
533         static char *www_files[]
534                 = { DEFAULT_WWW_DIR"/jquery.min.js", "js/jquery.min.js",
535                     DEFAULT_WWW_DIR"/ppastats.js", "js/ppastats.js",
536                     DEFAULT_WWW_DIR"/jqplot.dateAxisRenderer.min.js",
537                     "js/jqplot.dateAxisRenderer.min.js",
538                     DEFAULT_WWW_DIR"/jquery.jqplot.min.js",
539                     "js/jquery.jqplot.min.js",
540                     DEFAULT_WWW_DIR"/excanvas.js", "js/excanvas.js",
541                     DEFAULT_WWW_DIR"/ppastats.css", "css/ppastats.css",
542                     DEFAULT_WWW_DIR"/jquery.jqplot.min.css",
543                     "css/jquery.jqplot.min.css" };
544
545         mkdirs(output_dir, 0777);
546
547         if (install_static_files) {
548                 css_dir = path_append(output_dir, "css");
549                 js_dir = path_append(output_dir, "js");
550
551                 mkdir(css_dir, 0777);
552                 mkdir(js_dir, 0777);
553
554                 for (i = 0; i < 7; i++) {
555                         f_dst = path_append(output_dir, www_files[2*i+1]);
556
557                         log_debug(_("Copying %s %s"), www_files[2*i], f_dst);
558                         fcopy(www_files[2*i], f_dst);
559
560                         free(f_dst);
561                 }
562                 free(css_dir);
563                 free(js_dir);
564         }
565
566         ppastats = create_ppa_stats(owner, ppa, package_status);
567
568         path = path_new(output_dir, "ppa", ".html");
569
570         pkgs_to_html(ppastats, ppastats->packages, output_dir);
571
572         index_to_html(ppastats, output_dir);
573
574         ppa_stats_free(ppastats);
575
576         free(path);
577 }