Fossil SCM
Add the /artifact_stats page for use by administrators.
Commit
d96bee1c76276787a3b4b8760839776429c814f92cff39b66f54742f49887bde
Parent
561fa8a3b7f2a63…
1 file changed
+161
+161
| --- src/stat.c | ||
| +++ src/stat.c | ||
| @@ -95,10 +95,13 @@ | ||
| 95 | 95 | if( !brief ){ |
| 96 | 96 | @ <tr><th>Number Of Artifacts:</th><td> |
| 97 | 97 | n = db_int(0, "SELECT count(*) FROM blob"); |
| 98 | 98 | m = db_int(0, "SELECT count(*) FROM delta"); |
| 99 | 99 | @ %d(n) (%d(n-m) fulltext and %d(m) deltas) |
| 100 | + if( g.perm.Admin ){ | |
| 101 | + @ <a href='%R/artifact_stats'>Details</a> | |
| 102 | + } | |
| 100 | 103 | @ </td></tr> |
| 101 | 104 | if( n>0 ){ |
| 102 | 105 | int a, b; |
| 103 | 106 | Stmt q; |
| 104 | 107 | @ <tr><th>Uncompressed Artifact Size:</th><td> |
| @@ -569,5 +572,163 @@ | ||
| 569 | 572 | piechart_render(800,500,PIE_OTHER|PIE_PERCENT); |
| 570 | 573 | @ </svg></center> |
| 571 | 574 | } |
| 572 | 575 | style_footer(); |
| 573 | 576 | } |
| 577 | + | |
| 578 | +/* | |
| 579 | +** Gather statistics on artifact types, counts, and sizes. | |
| 580 | +*/ | |
| 581 | +static void gather_artifact_stats(void){ | |
| 582 | + static const char zSql[] = | |
| 583 | + @ CREATE TEMP TABLE artstat( | |
| 584 | + @ id INTEGER PRIMARY KEY, -- Corresponds to BLOB.RID | |
| 585 | + @ atype TEXT, -- 'data', 'manifest', 'tag', 'wiki', etc. | |
| 586 | + @ isDelta BOOLEAN, -- true if stored as a delta | |
| 587 | + @ szExp, -- expanded, uncompressed size | |
| 588 | + @ szCmpr -- size as stored on disk | |
| 589 | + @ ); | |
| 590 | + @ INSERT INTO artstat(id,atype,isDelta,szExp,szCmpr) | |
| 591 | + @ SELECT blob.rid, NULL, | |
| 592 | + @ EXISTS(SELECT 1 FROM delta WHERE delta.rid=blob.rid), | |
| 593 | + @ size, length(content) | |
| 594 | + @ FROM blob | |
| 595 | + @ WHERE content IS NOT NULL; | |
| 596 | + @ UPDATE artstat SET atype='file' | |
| 597 | + @ WHERE id IN (SELECT fid FROM mlink) | |
| 598 | + @ AND atype IS NULL; | |
| 599 | + @ UPDATE artstat SET atype='manifest' | |
| 600 | + @ WHERE id IN (SELECT objid FROM event WHERE type='ci') AND atype IS NULL; | |
| 601 | + @ UPDATE artstat SET atype='cluster' | |
| 602 | + @ WHERE atype IS NULL | |
| 603 | + @ AND id IN (SELECT rid FROM tagxref | |
| 604 | + @ WHERE tagid=(SELECT tagid FROM tag | |
| 605 | + @ WHERE tagname='cluster')); | |
| 606 | + @ UPDATE artstat SET atype='ticket' | |
| 607 | + @ WHERE atype IS NULL | |
| 608 | + @ AND id IN (SELECT rid FROM tagxref | |
| 609 | + @ WHERE tagid IN (SELECT tagid FROM tag | |
| 610 | + @ WHERE tagname GLOB 'tkt-*')); | |
| 611 | + @ UPDATE artstat SET atype='wiki' | |
| 612 | + @ WHERE atype IS NULL | |
| 613 | + @ AND id IN (SELECT rid FROM tagxref | |
| 614 | + @ WHERE tagid IN (SELECT tagid FROM tag | |
| 615 | + @ WHERE tagname GLOB 'wiki-*')); | |
| 616 | + @ UPDATE artstat SET atype='technote' | |
| 617 | + @ WHERE atype IS NULL | |
| 618 | + @ AND id IN (SELECT rid FROM tagxref | |
| 619 | + @ WHERE tagid IN (SELECT tagid FROM tag | |
| 620 | + @ WHERE tagname GLOB 'event-*')); | |
| 621 | + @ UPDATE artstat SET atype='attachment' | |
| 622 | + @ WHERE atype IS NULL | |
| 623 | + @ AND id IN (SELECT attachid FROM attachment UNION | |
| 624 | + @ SELECT blob.rid FROM attachment JOIN blob ON uuid=src); | |
| 625 | + @ UPDATE artstat SET atype='tag' | |
| 626 | + @ WHERE atype IS NULL | |
| 627 | + @ AND id IN (SELECT srcid FROM tagxref); | |
| 628 | + @ UPDATE artstat SET atype='tag' | |
| 629 | + @ WHERE atype IS NULL | |
| 630 | + @ AND id IN (SELECT objid FROM event WHERE type='g'); | |
| 631 | + @ UPDATE artstat SET atype='unknown' WHERE atype IS NULL; | |
| 632 | + ; | |
| 633 | + db_multi_exec("%s", zSql/*safe-for-%s*/); | |
| 634 | +} | |
| 635 | + | |
| 636 | +/* | |
| 637 | +** WEBPAGE: artifact_stats | |
| 638 | +** | |
| 639 | +** Show information about the sizes of artifacts in this repository | |
| 640 | +*/ | |
| 641 | +void artifact_stats_page(void){ | |
| 642 | + Stmt q; | |
| 643 | + login_check_credentials(); | |
| 644 | + if( !g.perm.Admin ){ login_needed(g.anon.Admin); return; } | |
| 645 | + style_header("Artifact Statistics"); | |
| 646 | + gather_artifact_stats(); | |
| 647 | + db_prepare(&q, | |
| 648 | + "SELECT atype, count(*), sum(isDelta), sum(szCmpr), sum(szExp)" | |
| 649 | + " FROM artstat GROUP BY 1" | |
| 650 | + " UNION ALL " | |
| 651 | + "SELECT 'ALL', count(*), sum(isDelta), sum(szCmpr), sum(szExp)" | |
| 652 | + " FROM artstat" | |
| 653 | + " ORDER BY 5;" | |
| 654 | + ); | |
| 655 | + @ <table class='sortable' border='1' \ | |
| 656 | + @ data-column-types='tkkkkk' data-init-sort='5'> | |
| 657 | + @ <thead><tr> | |
| 658 | + @ <th>Artifact Type</th> | |
| 659 | + @ <th>Count</th> | |
| 660 | + @ <th>Full-Text</th> | |
| 661 | + @ <th>Delta</th> | |
| 662 | + @ <th>Compressed Size</th> | |
| 663 | + @ <th>Uncompressed Size</th> | |
| 664 | + @ </tr></thead><tbody> | |
| 665 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 666 | + const char *zType = db_column_text(&q, 0); | |
| 667 | + int nTotal = db_column_int(&q, 1); | |
| 668 | + int nDelta = db_column_int(&q, 2); | |
| 669 | + int nFull = nTotal - nDelta; | |
| 670 | + sqlite3_int64 szCmpr = db_column_int64(&q, 3); | |
| 671 | + sqlite3_int64 szExp = db_column_int64(&q, 4); | |
| 672 | + char *z; | |
| 673 | + @ <tr><td>%h(zType)</td> | |
| 674 | + | |
| 675 | + z = sqlite3_mprintf("%,d", nTotal); | |
| 676 | + @ <td data-sortkey='%08x(nTotal)' align='right'>%s(z)</td> | |
| 677 | + sqlite3_free(z); | |
| 678 | + | |
| 679 | + z = sqlite3_mprintf("%,d", nFull); | |
| 680 | + @ <td data-sortkey='%08x(nFull)' align='right'>%s(z)</td> | |
| 681 | + sqlite3_free(z); | |
| 682 | + | |
| 683 | + z = sqlite3_mprintf("%,d", nDelta); | |
| 684 | + @ <td data-sortkey='%08x(nDelta)' align='right'>%s(z)</td> | |
| 685 | + sqlite3_free(z); | |
| 686 | + | |
| 687 | + z = sqlite3_mprintf("%,lld", szCmpr); | |
| 688 | + @ <td data-sortkey='%016x(szCmpr)' align='right'>%s(z)</td> | |
| 689 | + sqlite3_free(z); | |
| 690 | + | |
| 691 | + z = sqlite3_mprintf("%,lld", szExp); | |
| 692 | + @ <td data-sortkey='%016x(szExp)' align='right'>%s(z)</td> | |
| 693 | + sqlite3_free(z); | |
| 694 | + } | |
| 695 | + @ </tbody></table> | |
| 696 | + db_finalize(&q); | |
| 697 | + | |
| 698 | + if( db_exists("SELECT 1 FROM artstat WHERE atype='unknown'") ){ | |
| 699 | + @ <h2>Unknown Artifacts</h2> | |
| 700 | + db_prepare(&q, | |
| 701 | + "SELECT artstat.id, blob.uuid, user.login," | |
| 702 | + " datetime(rcvfrom.mtime), rcvfrom.ipaddr" | |
| 703 | + " FROM artstat JOIN blob ON artstat.id=blob.rid" | |
| 704 | + " LEFT JOIN rcvfrom USING(rcvid)" | |
| 705 | + " LEFT JOIN user USING(uid)" | |
| 706 | + " WHERE atype='unknown'" | |
| 707 | + ); | |
| 708 | + @ <table class='not-sortable' border='1' \ | |
| 709 | + @ data-column-types='ntttt' data-init-sort='0'> | |
| 710 | + @ <thead><tr> | |
| 711 | + @ <th>RecordID</th> | |
| 712 | + @ <th>Hash</th> | |
| 713 | + @ <th>User</th> | |
| 714 | + @ <th>Date</th> | |
| 715 | + @ <th>IP-Addr</th> | |
| 716 | + @ </tr></thead><tbody> | |
| 717 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 718 | + int rid = db_column_int(&q, 0); | |
| 719 | + const char *zHash = db_column_text(&q, 1); | |
| 720 | + const char *zUser = db_column_text(&q, 2); | |
| 721 | + const char *zDate = db_column_text(&q, 3); | |
| 722 | + const char *zIpAddr = db_column_text(&q, 4); | |
| 723 | + @ <tr><td>%d(rid)</td> | |
| 724 | + @ <td>%z(href("%R/info/%!S",zHash))%S(zHash)</a></td> | |
| 725 | + @ <td>%h(zUser)</td> | |
| 726 | + @ <td>%h(zDate)</td> | |
| 727 | + @ <td>%h(zIpAddr)</td></tr> | |
| 728 | + } | |
| 729 | + @ </tbody></table></div> | |
| 730 | + db_finalize(&q); | |
| 731 | + } | |
| 732 | + style_table_sorter(); | |
| 733 | + style_footer(); | |
| 734 | +} | |
| 574 | 735 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -95,10 +95,13 @@ | |
| 95 | if( !brief ){ |
| 96 | @ <tr><th>Number Of Artifacts:</th><td> |
| 97 | n = db_int(0, "SELECT count(*) FROM blob"); |
| 98 | m = db_int(0, "SELECT count(*) FROM delta"); |
| 99 | @ %d(n) (%d(n-m) fulltext and %d(m) deltas) |
| 100 | @ </td></tr> |
| 101 | if( n>0 ){ |
| 102 | int a, b; |
| 103 | Stmt q; |
| 104 | @ <tr><th>Uncompressed Artifact Size:</th><td> |
| @@ -569,5 +572,163 @@ | |
| 569 | piechart_render(800,500,PIE_OTHER|PIE_PERCENT); |
| 570 | @ </svg></center> |
| 571 | } |
| 572 | style_footer(); |
| 573 | } |
| 574 |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -95,10 +95,13 @@ | |
| 95 | if( !brief ){ |
| 96 | @ <tr><th>Number Of Artifacts:</th><td> |
| 97 | n = db_int(0, "SELECT count(*) FROM blob"); |
| 98 | m = db_int(0, "SELECT count(*) FROM delta"); |
| 99 | @ %d(n) (%d(n-m) fulltext and %d(m) deltas) |
| 100 | if( g.perm.Admin ){ |
| 101 | @ <a href='%R/artifact_stats'>Details</a> |
| 102 | } |
| 103 | @ </td></tr> |
| 104 | if( n>0 ){ |
| 105 | int a, b; |
| 106 | Stmt q; |
| 107 | @ <tr><th>Uncompressed Artifact Size:</th><td> |
| @@ -569,5 +572,163 @@ | |
| 572 | piechart_render(800,500,PIE_OTHER|PIE_PERCENT); |
| 573 | @ </svg></center> |
| 574 | } |
| 575 | style_footer(); |
| 576 | } |
| 577 | |
| 578 | /* |
| 579 | ** Gather statistics on artifact types, counts, and sizes. |
| 580 | */ |
| 581 | static void gather_artifact_stats(void){ |
| 582 | static const char zSql[] = |
| 583 | @ CREATE TEMP TABLE artstat( |
| 584 | @ id INTEGER PRIMARY KEY, -- Corresponds to BLOB.RID |
| 585 | @ atype TEXT, -- 'data', 'manifest', 'tag', 'wiki', etc. |
| 586 | @ isDelta BOOLEAN, -- true if stored as a delta |
| 587 | @ szExp, -- expanded, uncompressed size |
| 588 | @ szCmpr -- size as stored on disk |
| 589 | @ ); |
| 590 | @ INSERT INTO artstat(id,atype,isDelta,szExp,szCmpr) |
| 591 | @ SELECT blob.rid, NULL, |
| 592 | @ EXISTS(SELECT 1 FROM delta WHERE delta.rid=blob.rid), |
| 593 | @ size, length(content) |
| 594 | @ FROM blob |
| 595 | @ WHERE content IS NOT NULL; |
| 596 | @ UPDATE artstat SET atype='file' |
| 597 | @ WHERE id IN (SELECT fid FROM mlink) |
| 598 | @ AND atype IS NULL; |
| 599 | @ UPDATE artstat SET atype='manifest' |
| 600 | @ WHERE id IN (SELECT objid FROM event WHERE type='ci') AND atype IS NULL; |
| 601 | @ UPDATE artstat SET atype='cluster' |
| 602 | @ WHERE atype IS NULL |
| 603 | @ AND id IN (SELECT rid FROM tagxref |
| 604 | @ WHERE tagid=(SELECT tagid FROM tag |
| 605 | @ WHERE tagname='cluster')); |
| 606 | @ UPDATE artstat SET atype='ticket' |
| 607 | @ WHERE atype IS NULL |
| 608 | @ AND id IN (SELECT rid FROM tagxref |
| 609 | @ WHERE tagid IN (SELECT tagid FROM tag |
| 610 | @ WHERE tagname GLOB 'tkt-*')); |
| 611 | @ UPDATE artstat SET atype='wiki' |
| 612 | @ WHERE atype IS NULL |
| 613 | @ AND id IN (SELECT rid FROM tagxref |
| 614 | @ WHERE tagid IN (SELECT tagid FROM tag |
| 615 | @ WHERE tagname GLOB 'wiki-*')); |
| 616 | @ UPDATE artstat SET atype='technote' |
| 617 | @ WHERE atype IS NULL |
| 618 | @ AND id IN (SELECT rid FROM tagxref |
| 619 | @ WHERE tagid IN (SELECT tagid FROM tag |
| 620 | @ WHERE tagname GLOB 'event-*')); |
| 621 | @ UPDATE artstat SET atype='attachment' |
| 622 | @ WHERE atype IS NULL |
| 623 | @ AND id IN (SELECT attachid FROM attachment UNION |
| 624 | @ SELECT blob.rid FROM attachment JOIN blob ON uuid=src); |
| 625 | @ UPDATE artstat SET atype='tag' |
| 626 | @ WHERE atype IS NULL |
| 627 | @ AND id IN (SELECT srcid FROM tagxref); |
| 628 | @ UPDATE artstat SET atype='tag' |
| 629 | @ WHERE atype IS NULL |
| 630 | @ AND id IN (SELECT objid FROM event WHERE type='g'); |
| 631 | @ UPDATE artstat SET atype='unknown' WHERE atype IS NULL; |
| 632 | ; |
| 633 | db_multi_exec("%s", zSql/*safe-for-%s*/); |
| 634 | } |
| 635 | |
| 636 | /* |
| 637 | ** WEBPAGE: artifact_stats |
| 638 | ** |
| 639 | ** Show information about the sizes of artifacts in this repository |
| 640 | */ |
| 641 | void artifact_stats_page(void){ |
| 642 | Stmt q; |
| 643 | login_check_credentials(); |
| 644 | if( !g.perm.Admin ){ login_needed(g.anon.Admin); return; } |
| 645 | style_header("Artifact Statistics"); |
| 646 | gather_artifact_stats(); |
| 647 | db_prepare(&q, |
| 648 | "SELECT atype, count(*), sum(isDelta), sum(szCmpr), sum(szExp)" |
| 649 | " FROM artstat GROUP BY 1" |
| 650 | " UNION ALL " |
| 651 | "SELECT 'ALL', count(*), sum(isDelta), sum(szCmpr), sum(szExp)" |
| 652 | " FROM artstat" |
| 653 | " ORDER BY 5;" |
| 654 | ); |
| 655 | @ <table class='sortable' border='1' \ |
| 656 | @ data-column-types='tkkkkk' data-init-sort='5'> |
| 657 | @ <thead><tr> |
| 658 | @ <th>Artifact Type</th> |
| 659 | @ <th>Count</th> |
| 660 | @ <th>Full-Text</th> |
| 661 | @ <th>Delta</th> |
| 662 | @ <th>Compressed Size</th> |
| 663 | @ <th>Uncompressed Size</th> |
| 664 | @ </tr></thead><tbody> |
| 665 | while( db_step(&q)==SQLITE_ROW ){ |
| 666 | const char *zType = db_column_text(&q, 0); |
| 667 | int nTotal = db_column_int(&q, 1); |
| 668 | int nDelta = db_column_int(&q, 2); |
| 669 | int nFull = nTotal - nDelta; |
| 670 | sqlite3_int64 szCmpr = db_column_int64(&q, 3); |
| 671 | sqlite3_int64 szExp = db_column_int64(&q, 4); |
| 672 | char *z; |
| 673 | @ <tr><td>%h(zType)</td> |
| 674 | |
| 675 | z = sqlite3_mprintf("%,d", nTotal); |
| 676 | @ <td data-sortkey='%08x(nTotal)' align='right'>%s(z)</td> |
| 677 | sqlite3_free(z); |
| 678 | |
| 679 | z = sqlite3_mprintf("%,d", nFull); |
| 680 | @ <td data-sortkey='%08x(nFull)' align='right'>%s(z)</td> |
| 681 | sqlite3_free(z); |
| 682 | |
| 683 | z = sqlite3_mprintf("%,d", nDelta); |
| 684 | @ <td data-sortkey='%08x(nDelta)' align='right'>%s(z)</td> |
| 685 | sqlite3_free(z); |
| 686 | |
| 687 | z = sqlite3_mprintf("%,lld", szCmpr); |
| 688 | @ <td data-sortkey='%016x(szCmpr)' align='right'>%s(z)</td> |
| 689 | sqlite3_free(z); |
| 690 | |
| 691 | z = sqlite3_mprintf("%,lld", szExp); |
| 692 | @ <td data-sortkey='%016x(szExp)' align='right'>%s(z)</td> |
| 693 | sqlite3_free(z); |
| 694 | } |
| 695 | @ </tbody></table> |
| 696 | db_finalize(&q); |
| 697 | |
| 698 | if( db_exists("SELECT 1 FROM artstat WHERE atype='unknown'") ){ |
| 699 | @ <h2>Unknown Artifacts</h2> |
| 700 | db_prepare(&q, |
| 701 | "SELECT artstat.id, blob.uuid, user.login," |
| 702 | " datetime(rcvfrom.mtime), rcvfrom.ipaddr" |
| 703 | " FROM artstat JOIN blob ON artstat.id=blob.rid" |
| 704 | " LEFT JOIN rcvfrom USING(rcvid)" |
| 705 | " LEFT JOIN user USING(uid)" |
| 706 | " WHERE atype='unknown'" |
| 707 | ); |
| 708 | @ <table class='not-sortable' border='1' \ |
| 709 | @ data-column-types='ntttt' data-init-sort='0'> |
| 710 | @ <thead><tr> |
| 711 | @ <th>RecordID</th> |
| 712 | @ <th>Hash</th> |
| 713 | @ <th>User</th> |
| 714 | @ <th>Date</th> |
| 715 | @ <th>IP-Addr</th> |
| 716 | @ </tr></thead><tbody> |
| 717 | while( db_step(&q)==SQLITE_ROW ){ |
| 718 | int rid = db_column_int(&q, 0); |
| 719 | const char *zHash = db_column_text(&q, 1); |
| 720 | const char *zUser = db_column_text(&q, 2); |
| 721 | const char *zDate = db_column_text(&q, 3); |
| 722 | const char *zIpAddr = db_column_text(&q, 4); |
| 723 | @ <tr><td>%d(rid)</td> |
| 724 | @ <td>%z(href("%R/info/%!S",zHash))%S(zHash)</a></td> |
| 725 | @ <td>%h(zUser)</td> |
| 726 | @ <td>%h(zDate)</td> |
| 727 | @ <td>%h(zIpAddr)</td></tr> |
| 728 | } |
| 729 | @ </tbody></table></div> |
| 730 | db_finalize(&q); |
| 731 | } |
| 732 | style_table_sorter(); |
| 733 | style_footer(); |
| 734 | } |
| 735 |