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