{"id":11798,"date":"2018-11-05T15:00:40","date_gmt":"2018-11-05T14:00:40","guid":{"rendered":"https:\/\/www.dbi-services.com\/blog\/a-graphical-overview-of-a-repository\/"},"modified":"2025-10-24T09:32:20","modified_gmt":"2025-10-24T07:32:20","slug":"a-graphical-overview-of-a-repository","status":"publish","type":"post","link":"https:\/\/www.dbi-services.com\/blog\/a-graphical-overview-of-a-repository\/","title":{"rendered":"A Graphical Overview of a Repository"},"content":{"rendered":"<p>As the saying goes, &#8220;A Picture Is Worth A Thousands Words&#8221;. I&#8217;d add &#8220;And A Simple Graph Is Worth A Long, Abstruse List of Numbers&#8221;. And of words too, so let&#8217;s show off a little bit:<br \/>\n<a href=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2022\/04\/Screenshot-from-2018-11-03-19-39-57.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2022\/04\/Screenshot-from-2018-11-03-19-39-57.png\" alt=\"Screenshot from 2018-11-03 19-39-57\" width=\"300\" height=\"119\" class=\"alignnone size-medium wp-image-29159\" \/><\/a><br \/>\nInterested ? Then, please read on.<br \/>\nIt happens not so infrequently that we wish we could quickly plot a few numbers to have a look at their overall trend. The usual procedure is to output the numbers into a csv file, transfer it to a desktop machine, subsequently import it into a spreadsheet program and interactively set up a chart, a tedious manual procedure at best, especially if it has to be repeated several times.<br \/>\nWhat if we could query a few relevant values from a system and plot them in one go to visualize the produced graphs from within a browser ? What if we could generalize that procedure to any extracted tabular data ? I&#8217;m not talking of a sophisticated interface to some enterprise-class graphing tool but just of a simple way to get a quick visual overall feeling of some variables with as little installed software as possible.<br \/>\nI&#8217;ll show here how to do that for a Documentum repository but the target system can be anything, a database, an ldap, an O\/S, just adapt the queries and the script as needed.<br \/>\nTo simplify, I assume that we generally work on a server machine, likely a Linux headless VM, and that the browser runs remotely on any GUI-based desktop, quite a common configuration for Documentum.<\/p>\n<h3>The Data<\/h3>\n<p>As an administrator, I often have to connect to docbases I never visited before, and I find it useful to run the following queries to help me make acquaintance with those new beasts. In the examples below, the target docbase is an out of the box one with no activity in it, which explains the low numbers and lack of custom doctypes:<\/p>\n<p>&#8212; 1. what distinct document types are there ?<br \/>\n<code><br \/>\nselect r_object_type, count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) group by r_object_type order by 1<br \/>\nr_object_type                     count(*)                tot_size<br \/>\n--------------------------------  ----------------------  ----------------------<br \/>\ndm_document                                         1295                 6384130<br \/>\ndm_esign_template                                      1                   46255<br \/>\ndm_format_preferences                                  1                     109<br \/>\ndm_menu_system                                         2                  352034<br \/>\ndm_plugin                                              2                  212586<br \/>\ndm_xml_config                                          1                     534<br \/>\ndmc_jar                                              236                50451386<br \/>\ndmc_preset_package                                     2                   11951<br \/>\ndmc_tcf_activity_template                             10                   12162<br \/>\n(9 rows affected)<br \/>\n<\/code><br \/>\nA reminder of some definitions: The result table above is a dataset. The r_object_type is the <em>category<\/em>, typically shown in the X-axis. The <em>count(*)<\/em> and <em>tot_size<\/em> columns are the &#8220;variables&#8221; to be plot, typically as bars, lines or pie slices. They are also named &#8220;traces&#8221; in some graphing tools. Some of the datasets here have 1, 2 or 3 variables to be plot. Datasets with 1 variable can be plot as bars, lines or pies charts. Datasets with 2 variables can be plot as grouped or stacked bars, or as 2 distinct graphs of one variable. There are many possibilities and combinations, and the choice depends on which representation offers the best visual clarity. Sometimes, the plotting library even lets one edit the programmatically generated graph and interactively choose the best type of graph with no coding needed !<\/p>\n<p>&#8212; 2. how does their population vary over time ?<br \/>\n<code><br \/>\nselect r_object_type, datefloor(month, r_creation_date) as \"creation_month\", count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) group by r_object_type, datefloor(month, r_creation_date) order by 1, 2;<br \/>\nr_object_type                     creation_month             count(*)      tot_size<br \/>\n--------------------------------  -------------------------  ------------  ------------<br \/>\ndm_document                       11\/1\/2017 01:00:00                  448       3055062<br \/>\ndm_document                       12\/1\/2017 01:00:00                  120        323552<br \/>\ndm_document                       3\/1\/2018 01:00:00                    38         66288<br \/>\ndm_document                       4\/1\/2018 02:00:00                   469       1427865<br \/>\ndm_document                       5\/1\/2018 02:00:00                    86        584453<br \/>\ndm_document                       6\/1\/2018 02:00:00                    20        150464<br \/>\ndm_document                       7\/1\/2018 02:00:00                    40        301341<br \/>\ndm_document                       8\/1\/2018 02:00:00                    32        151333<br \/>\ndm_document                       9\/1\/2018 02:00:00                    46        356386<br \/>\ndm_esign_template                 11\/1\/2017 01:00:00                    1         46255<br \/>\ndm_format_preferences             11\/1\/2017 01:00:00                    1           109<br \/>\ndm_menu_system                    11\/1\/2017 01:00:00                    2        352034<br \/>\ndm_plugin                         11\/1\/2017 01:00:00                    2        212586<br \/>\ndm_xml_config                     11\/1\/2017 01:00:00                    1           534<br \/>\ndmc_jar                           11\/1\/2017 01:00:00                  236      50451386<br \/>\ndmc_preset_package                11\/1\/2017 01:00:00                    2         11951<br \/>\ndmc_tcf_activity_template         11\/1\/2017 01:00:00                   10         12162<br \/>\n(17 rows affected)<br \/>\n<\/code><br \/>\nThis query tells how heavily used the repository is. Also, a broad spectre of custom document types tends to indicate that the repository is used in the context of applications.<\/p>\n<p>&#8212; 3. how changing are those documents ?<br \/>\n<code><br \/>\nselect r_object_type, datefloor(month, r_modify_date) as \"modification_month\", count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) where r_creation_date &lt; r_modify_date group by r_object_type, datefloor(month, r_modify_date) order by 1, 2;<br \/>\nr_object_type                     modification_month         count(*)      tot_size<br \/>\n--------------------------------  -------------------------  ------------  ------------<br \/>\ndm_document                       11\/1\/2017 01:00:00                  127        485791<br \/>\ndm_document                       12\/1\/2017 01:00:00                   33        122863<br \/>\ndm_document                       3\/1\/2018 01:00:00                    16         34310<br \/>\ndm_document                       4\/1\/2018 02:00:00                   209        749370<br \/>\ndm_document                       5\/1\/2018 02:00:00                    42        311211<br \/>\ndm_document                       6\/1\/2018 02:00:00                    10         79803<br \/>\ndm_document                       7\/1\/2018 02:00:00                    20        160100<br \/>\ndm_document                       8\/1\/2018 02:00:00                    12         81982<br \/>\ndm_document                       9\/1\/2018 02:00:00                    23        172299<br \/>\ndm_esign_template                 8\/1\/2018 02:00:00                     1         46255<br \/>\ndmc_jar                           11\/1\/2017 01:00:00                   14       1616218<br \/>\ndmc_preset_package                11\/1\/2017 01:00:00                    2         11951<br \/>\ndmc_tcf_activity_template         11\/1\/2017 01:00:00                   10         12162<br \/>\n(13 rows affected)<br \/>\n<\/code><br \/>\nThis query shows if a repository is used interactively rather than for archiving. If there are lots of editions, the docbase is a lively one; on the contrary, if documents are rarely or never edited, the docbase is mostly used for archiving. The document ownership can tell too, technical accounts vs. real people.<\/p>\n<p>&#8212; samething but without distinction of document type;<br \/>\n&#8212; 4. new documents;<br \/>\n<code><br \/>\nselect datefloor(month, r_creation_date) as \"creation_month\", count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) group by datefloor(month, r_creation_date) order by 1;<br \/>\ncreation_month             count(*)                tot_size<br \/>\n-------------------------  ----------------------  ----------------------<br \/>\n11\/1\/2017 01:00:00                            703                54142079<br \/>\n12\/1\/2017 01:00:00                            120                  323552<br \/>\n3\/1\/2018 01:00:00                              38                   66288<br \/>\n4\/1\/2018 02:00:00                             469                 1427865<br \/>\n5\/1\/2018 02:00:00                              86                  584453<br \/>\n6\/1\/2018 02:00:00                              20                  150464<br \/>\n7\/1\/2018 02:00:00                              40                  301341<br \/>\n8\/1\/2018 02:00:00                              32                  151333<br \/>\n9\/1\/2018 02:00:00                              42                  323772<br \/>\n(9 rows affected)<br \/>\n<\/code><br \/>\n&#8212; 5. modified documents;<br \/>\n<code><br \/>\nselect datefloor(month, r_modify_date) as \"modification_month\", count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) where r_creation_date &lt; r_modify_date group by datefloor(month, r_modify_date) order by 1;<br \/>\nmodification_month         count(*)                tot_size<br \/>\n-------------------------  ----------------------  ----------------------<br \/>\n11\/1\/2017 01:00:00                            153                 2126122<br \/>\n12\/1\/2017 01:00:00                             33                  122863<br \/>\n3\/1\/2018 01:00:00                              16                   34310<br \/>\n4\/1\/2018 02:00:00                             209                  749370<br \/>\n5\/1\/2018 02:00:00                              42                  311211<br \/>\n6\/1\/2018 02:00:00                              10                   79803<br \/>\n7\/1\/2018 02:00:00                              20                  160100<br \/>\n8\/1\/2018 02:00:00                              13                  128237<br \/>\n9\/1\/2018 02:00:00                              20                  140354<br \/>\n(9 rows affected)<br \/>\n<\/code><br \/>\n&#8212; 6. what content types are used in the repository ?<br \/>\n<code><br \/>\nselect a_content_type, count(*) as \"count_content_type\", sum(r_full_content_size) as \"tot_content_size\" from dm_document(all) group by a_content_type order by 1<br \/>\ncol a_content_type 20<br \/>\na_content_type        count_content_type  tot_content_size<br \/>\n--------------------  ------------------  ----------------<br \/>\n                                      11                 0<br \/>\namipro                                 1              4558<br \/>\ncrtext                                43            389681<br \/>\ndtd                                    5            163392<br \/>\nexcel12bbook                           1              8546<br \/>\nexcel12book                            1              7848<br \/>\nexcel12mebook                          1              7867<br \/>\nexcel12metemplate                      1              7871<br \/>\nexcel12template                        1              7853<br \/>\nexcel5book                             1             15360<br \/>\nexcel8book                             1             13824<br \/>\nexcel8template                         1             13824<br \/>\nibmshrlib                              2            212586<br \/>\njar                                  233          48856783<br \/>\njava                                   3           1594603<br \/>\nmaker55                                5            117760<br \/>\nmdoc55                                 9            780288<br \/>\nms_access7                             1             83968<br \/>\nms_access8                             1             59392<br \/>\nms_access8_mde                         1             61440<br \/>\nmsw12                                  1             11280<br \/>\nmsw12me                                1             10009<br \/>\nmsw12metemplate                        1             10004<br \/>\nmsw12template                          1              9993<br \/>\nmsw6                                   1             11776<br \/>\nmsw8                                   1             19456<br \/>\nmsw8template                           1             27136<br \/>\npdf                                    2             50214<br \/>\npowerpoint                             1             14848<br \/>\nppt12                                  1             29956<br \/>\nppt12me                                1             29944<br \/>\nppt12meslideshow                       1             29943<br \/>\nppt12metemplate                        1             29941<br \/>\nppt12slideshow                         1             29897<br \/>\nppt12template                          1             29914<br \/>\nppt8                                   1              7680<br \/>\nppt8_template                          1              9728<br \/>\ntext                                1175           4236008<br \/>\nustn                                   1             10240<br \/>\nvrf                                    1            158993<br \/>\nwp6                                    1               941<br \/>\nwp7                                    1              1362<br \/>\nwp8                                    1              1362<br \/>\nxml                                   28             37953<br \/>\nzip                                    1            255125<br \/>\n(45 rows affected)<br \/>\n<\/code><br \/>\nThe content type may indicates the kind of activity the repository is used for. While pdf are generally final documents suitable for archiving, a predominance of illustrator\/pagemaker\/QuarkXPress vs photoshop vs multimedia vs Office documents can give a hint at the docbase&#8217;s general function.<br \/>\nSince most content types are unused (who uses Word Perfect or Ami Pro any more ?), a constraint on the count can be introduced, e.g. &#8220;having count(*) &gt; 10&#8221; to filter out those obsolete formats.<\/p>\n<p>&#8212; 7. and how do they evolve over time ?<br \/>\n<code><br \/>\nselect datefloor(month, r_creation_date) as \"creation_month\", a_content_type, count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) group by datefloor(month, r_creation_date), a_content_type order by 1, 2<br \/>\ncreation_month             a_content_type        count(*)      tot_size<br \/>\n-------------------------  --------------------  ------------  ------------<br \/>\n11\/1\/2017 01:00:00                                          1             0<br \/>\n11\/1\/2017 01:00:00         amipro                           1          4558<br \/>\n11\/1\/2017 01:00:00         crtext                          43        389681<br \/>\n11\/1\/2017 01:00:00         dtd                              5        163392<br \/>\n11\/1\/2017 01:00:00         excel12bbook                     1          8546<br \/>\n11\/1\/2017 01:00:00         excel12book                      1          7848<br \/>\n11\/1\/2017 01:00:00         excel12mebook                    1          7867<br \/>\n11\/1\/2017 01:00:00         excel12metemplate                1          7871<br \/>\n11\/1\/2017 01:00:00         excel12template                  1          7853<br \/>\n11\/1\/2017 01:00:00         excel5book                       1         15360<br \/>\n11\/1\/2017 01:00:00         excel8book                       1         13824<br \/>\n11\/1\/2017 01:00:00         excel8template                   1         13824<br \/>\n11\/1\/2017 01:00:00         ibmshrlib                        2        212586<br \/>\n11\/1\/2017 01:00:00         jar                            233      48856783<br \/>\n11\/1\/2017 01:00:00         java                             3       1594603<br \/>\n11\/1\/2017 01:00:00         maker55                          5        117760<br \/>\n11\/1\/2017 01:00:00         mdoc55                           9        780288<br \/>\n11\/1\/2017 01:00:00         ms_access7                       1         83968<br \/>\n11\/1\/2017 01:00:00         ms_access8                       1         59392<br \/>\n11\/1\/2017 01:00:00         ms_access8_mde                   1         61440<br \/>\n11\/1\/2017 01:00:00         msw12                            1         11280<br \/>\n11\/1\/2017 01:00:00         msw12me                          1         10009<br \/>\n11\/1\/2017 01:00:00         msw12metemplate                  1         10004<br \/>\n11\/1\/2017 01:00:00         msw12template                    1          9993<br \/>\n11\/1\/2017 01:00:00         msw6                             1         11776<br \/>\n11\/1\/2017 01:00:00         msw8                             1         19456<br \/>\n11\/1\/2017 01:00:00         msw8template                     1         27136<br \/>\n11\/1\/2017 01:00:00         pdf                              2         50214<br \/>\n11\/1\/2017 01:00:00         powerpoint                       1         14848<br \/>\n11\/1\/2017 01:00:00         ppt12                            1         29956<br \/>\n11\/1\/2017 01:00:00         ppt12me                          1         29944<br \/>\n11\/1\/2017 01:00:00         ppt12meslideshow                 1         29943<br \/>\n11\/1\/2017 01:00:00         ppt12metemplate                  1         29941<br \/>\n11\/1\/2017 01:00:00         ppt12slideshow                   1         29897<br \/>\n11\/1\/2017 01:00:00         ppt12template                    1         29914<br \/>\n11\/1\/2017 01:00:00         ppt8                             1          7680<br \/>\n11\/1\/2017 01:00:00         ppt8_template                    1          9728<br \/>\n11\/1\/2017 01:00:00         text                           338        906940<br \/>\n11\/1\/2017 01:00:00         ustn                             1         10240<br \/>\n11\/1\/2017 01:00:00         vrf                              1        158993<br \/>\n11\/1\/2017 01:00:00         wp6                              1           941<br \/>\n11\/1\/2017 01:00:00         wp7                              1          1362<br \/>\n11\/1\/2017 01:00:00         wp8                              1          1362<br \/>\n11\/1\/2017 01:00:00         xml                             28         37953<br \/>\n11\/1\/2017 01:00:00         zip                              1        255125<br \/>\n12\/1\/2017 01:00:00         text                           120        323552<br \/>\n3\/1\/2018 01:00:00          text                            38         66288<br \/>\n4\/1\/2018 02:00:00          text                           469       1427865<br \/>\n5\/1\/2018 02:00:00          text                            86        584453<br \/>\n6\/1\/2018 02:00:00          text                            20        150464<br \/>\n7\/1\/2018 02:00:00          text                            40        301341<br \/>\n8\/1\/2018 02:00:00                                          10             0<br \/>\n8\/1\/2018 02:00:00          text                            22        151333<br \/>\n9\/1\/2018 02:00:00          text                            42        323772<br \/>\n(54 rows affected)<br \/>\n<\/code><br \/>\n&#8212; 8. where are those contents stored ?<br \/>\n<code><br \/>\nselect a_storage_type, count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) group by a_storage_type order by a_storage_type<br \/>\ncol a_storage_type 20<br \/>\na_storage_type        count(*)      tot_size<br \/>\n--------------------  ------------  ------------<br \/>\n                                11             0<br \/>\nfilestore_01                  1539      57471147<br \/>\n(2 rows affected)<br \/>\n<\/code><br \/>\nFilestores are conceptually quite similar to Oracle RDBMS tablespaces. It is a good practice to separate filestores by applications\/type of documents and\/or even by time if the volume of documents in important. This query tells if this is in place and, if so, what the criteria were.<\/p>\n<p>&#8212; 9. dig by document type;<br \/>\n<code><br \/>\nselect r_object_type, a_storage_type, count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) group by r_object_type, a_storage_type order by r_object_type, a_storage_type<br \/>\nr_object_type                     a_storage_type        count(*)      tot_size<br \/>\n--------------------------------  --------------------  ------------  ------------<br \/>\ndm_document                                                       11             0<br \/>\ndm_document                       filestore_01                  1284       6384130<br \/>\ndm_esign_template                 filestore_01                     1         46255<br \/>\ndm_format_preferences             filestore_01                     1           109<br \/>\ndm_menu_system                    filestore_01                     2        352034<br \/>\ndm_plugin                         filestore_01                     2        212586<br \/>\ndm_xml_config                     filestore_01                     1           534<br \/>\ndmc_jar                           filestore_01                   236      50451386<br \/>\ndmc_preset_package                filestore_01                     2         11951<br \/>\ndmc_tcf_activity_template         filestore_01                    10         12162<br \/>\n(10 rows affected)<br \/>\n<\/code><\/p>\n<p>&#8212; 10. same but by content format;<br \/>\n<code><br \/>\nselect a_content_type, a_storage_type, count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) group by a_content_type, a_storage_type having count(*) &gt; 10 order by a_content_type, a_storage_type<br \/>\na_content_type                    a_storage_type   count(*)                tot_size<br \/>\n--------------------------------  ---------------  ----------------------  ----------------------<br \/>\ncrtext                            filestore_01                         43                  389681<br \/>\njar                               filestore_01                        233                48856783<br \/>\ntext                              filestore_01                       1204                 4432200<br \/>\nxml                               filestore_01                         28                   37953<br \/>\n(4 rows affected)<br \/>\n<\/code><\/p>\n<p>&#8212; 11. what ACLs do exist and who created them ?<br \/>\n<code><br \/>\nselect owner_name, count(*) from dm_acl group by owner_name order by 2 desc<br \/>\ncol owner_name 30<br \/>\nowner_name                      count(*)<br \/>\n------------------------------  ------------<br \/>\ndmadmin                                  179<br \/>\ndmtest                                    44<br \/>\ndm_bof_registry                            6<br \/>\ndmc_wdk_preferences_owner                  2<br \/>\ndmc_wdk_presets_owner                      2<br \/>\ndm_mediaserver                             1<br \/>\ndm_audit_user                              1<br \/>\ndm_autorender_mac                          1<br \/>\ndm_fulltext_index_user                     1<br \/>\ndm_autorender_win31                        1<br \/>\ndm_report_user                             1<br \/>\n(11 rows affected)<br \/>\n<\/code><br \/>\nSpoiler alert: the attentive readers have probably noticed the command &#8220;col&#8221; preceding several queries, like in &#8220;col name 20&#8221;; this is not a standard idql command but part of an extended idql which will be presented in a future blog.<br \/>\nACLs are part of the security model in place, if any. Usually, the owner is a technical account, sometimes a different one for each application or application&#8217;s functionality or business line, so this query can show if the repository is under control of applications or rather simply used as a replacement for a shared drive. Globally, it can tell how the repository&#8217;s security is managed, if at all.<\/p>\n<p>&#8212; 12. ACLs most in use;<br \/>\n<code><br \/>\nselect acl_name, count(*) from dm_document(all) where acl_name not like 'dm_%' group by acl_name having count(*) &gt;= 10 order by 1<br \/>\nacl_name                          count(*)<br \/>\n--------------------------------  ----------------------<br \/>\nBOF_acl                                              233<br \/>\n(1 row affected)<br \/>\n<\/code><br \/>\nHere too, a filter by usage can be introduced so that rarely used acls are not reported, e.g. &#8220;having count(*) &gt; 10&#8221;.<\/p>\n<p>&#8212; 13. queue items;<br \/>\n<code><br \/>\nselect name, count(*) from dmi_queue_item group by name order by 2 desc<br \/>\nname                  count(*)<br \/>\n--------------------  ------------<br \/>\ndm_autorender_win31            115<br \/>\ndmadmin                        101<br \/>\ndmtest                          54<br \/>\n(3 rows affected)<br \/>\n<\/code><br \/>\nThis query is mainly useful to show if external systems are used for generating renditions, thumbnails or any other document transformation.<\/p>\n<p>&#8212; non quantifiable queries;<br \/>\n<code><br \/>\nselect object_name from dm_acl where object_name not like 'dm_%' order by 1<br \/>\nobject_name<br \/>\n--------------------------------<br \/>\nBOF_acl<br \/>\nBOF_acl2<br \/>\nBPM Process Variable ACL<br \/>\nBatchPromoteAcl<br \/>\nDefault Preset Permission Set<br \/>\nGlobal User Default ACL<br \/>\nWebPublishingAcl<br \/>\nWork Queue User Default ACL<br \/>\ndce_world_write<br \/>\ndesktop_client_acl1<br \/>\nreplica_acl_default<br \/>\n(11 rows affected)<br \/>\n&nbsp;<br \/>\ncol name 20<br \/>\ncol root 30<br \/>\ncol file_system_path 70<br \/>\nselect f.name, f.root, l.file_system_path from dm_filestore f, dm_location l where f.root = l.object_name<br \/>\nname                  root                            file_system_path<br \/>\n--------------------  ------------------------------  ----------------------------------------------------------------------<br \/>\nfilestore_01          storage_01                      \/home\/dmadmin\/documentum\/data\/dmtest\/content_storage_01<br \/>\nthumbnail_store_01    thumbnail_storage_01            \/home\/dmadmin\/documentum\/data\/dmtest\/thumbnail_storage_01<br \/>\nstreaming_store_01    streaming_storage_01            \/home\/dmadmin\/documentum\/data\/dmtest\/streaming_storage_01<br \/>\nreplicate_temp_store  replicate_location              \/home\/dmadmin\/documentum\/data\/dmtest\/replicate_temp_store<br \/>\nreplica_filestore_01  replica_storage_01              \/home\/dmadmin\/documentum\/data\/dmtest\/replica_content_storage_01<br \/>\n(5 rows affected)<br \/>\n<\/code><br \/>\nNo numbers here, just plain text information.<\/p>\n<p>There is of course much more information to query (e.g. what about lifecycles and workflows, users and groups ?) depending on what one needs to look at in the repositories, but those are enough examples for our demonstration&#8217;s purpose. The readers can always add queries or refine the existing ones to suit their needs.<br \/>\nAt this point, we have a lot of hard-to-ingest numbers. Plotting them gives a pleasant 2-dimensional view of the data and will allow to easily compare and analyze them, especially if the generated plot offers some interactivity.<\/p>\n<h3>The Graphing Library<\/h3>\n<p>To do this, we need a graphing library and since we want to be able to look at the graphs from within a browser for platform-independence and zero-installation on the desktop, we need one that lets us generate an HTML page containing the charts. This is possible in 2 ways:<br \/>\no   &nbsp; the library is a javascript one; we will programmatically build up json literals, pass them to the library&#8217;s plotting function and wrap everything into an html page which is saved on disk for later viewing;<br \/>\no   &nbsp; the library is usable from several scripting languages and includes a function to generate an HTML page containing the graph; we will chose one for python since this language is ubiquitous and we have now a binding for Documentum (see my blog <a title=\"Adding A Documentum In Python\" href=\"https:\/\/www.dbi-services.com\/blog\/adding-a-documentum-extension-into-python\/\" target=\"_blank\" rel=\"noopener noreferrer\">here<\/a>);<br \/>\nNow, there are tons of such javascript libraries available on-line (see for example <a title=\"15 Best Javascript Charting Libraries\" href=\"https:\/\/www.sitepoint.com\/15-best-javascript-charting-libraries\/\" target=\"_blank\" rel=\"noopener noreferrer\">here<\/a> for a sample) but our choice criteria will be simple:<br \/>\no   &nbsp; the library must be rich enough but at the same time easy to use;<br \/>\no   &nbsp; it must do its work entirely locally as servers are generally not allowed to access the Internet;<br \/>\no   &nbsp; it must be free in order to get rid of the licensing complexity as it can often be overkill in such a simple and confined usage;<br \/>\nAs we must pick one, let&#8217;s choose <a title=\"Plotly\" href=\"https:\/\/plot.ly\/javascript\/\" target=\"_blank\" rel=\"noopener noreferrer\">Plotly<\/a> since it is very capable and largely covers our needs. Besides, it works either as a javascript or as a python library but for more generality let&#8217;s just use it as a javascript library and generate ourselves the html pages. As an additional bonus, Plotly also allows to interactively edit a plotted graph so it can be tweaked at will. This is very convenient because it lessens the effort to optimize the graphs&#8217; readability as this can be done later by the users themselves while viewing the graphs. For example, the users can zoom, unselect variables, move legends around and much more.<br \/>\nTo install it, simply right-click <a title=\"Download Plotly\" href=\"https:\/\/cdn.plot.ly\/plotly-latest.min.js\" target=\"_blank\" rel=\"noopener noreferrer\">here<\/a> and save the link to the default graph location in the project&#8217;s directory, e.g. \/home\/dmadmin\/graph-stats\/graphs.<br \/>\n<code><br \/>\npwd<br \/>\n\/home\/dmadmin\/graph-stats\/graphs<br \/>\nls -l<br \/>\n...<br \/>\n-rw-rw-r-- 1 dmadmin dmadmin 2814564 Oct 11 11:40 plotly-latest.min.js<br \/>\n...<br \/>\n<\/code><br \/>\nThat&#8217;s less than 2.7 MiB of powerful compact js source code. Since it is a text file, there shouldn&#8217;t be any security concern in copying that file on a server. Also, as it is executed inside the sandbox of a browser, security is normally not an issue.<br \/>\nThe library needs to be in the same directory as the generated html files. If those files are copied to a desktop for direct viewing from a browser with File\/Open, don&#8217;t forget to copy over the Plotly.js library too, open the html file in an editor, go to the line 6 below and set src to the path of the html file:<\/p>\n<pre class=\"brush: python; gutter: true; first-line: 1; highlight: [6]\">\n&lt;!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD HTML 3.2\/\/EN\"&gt;\n&lt;html&gt;\n   &lt;head&gt;\n      &lt;title&gt;Graphical Overview of Repository SubWay&lt;\/title&gt;\n      &lt;meta http-equiv=\"Content-Type\" content=\"text\/html; charset=utf-8\"&gt;\n      &lt;script src=\".....\/plotly-latest.min.js\"&gt;&lt;\/script&gt;\n   &lt;\/head&gt;\n<\/pre>\n<h3>Viewing the html pages<\/h3>\n<p>Since those pages must be accessed from any machine on the network (typically, a desktop with a browser), despite they may be created directly on the server that hosts the repositories of interest, we need a web server. It may happen that a disk volume is shared between desktops and servers but this is far from being the general case. Fortunately, python is very helpful here and saves us from the tedious task of setting up a full-fledged web server such as apache. The one-liner below will start a server for visualizing the files in the current directory and its sub-directories:<br \/>\n<code><br \/>\npython3 -m http.server [<i>port<\/i>]     # for python &gt;= v3<br \/>\npython -m SimpleHTTPServer [<i>port<\/i>] # for python 2.x<br \/>\n<\/code><br \/>\nIt can also be moved in the background to free the command-line. To stop it, bring it in the foreground and type ctrl-C.<br \/>\nIts default port is 8000.<br \/>\nThis mini-web server is now waiting for requests on the given port and its IP address. In order to determine that IP address, use the ifconfig command and try one address that is on the same network as the desktop with the browser. Then, load the URL, e.g. http:\/\/192.168.56.10:8000\/, and you&#8217;ll be greeted by a familiar point-and-click interface.<br \/>\nThe server presents a directory listing from which to select the html files containing the generated graphs.<\/p>\n<h3>An Example<\/h3>\n<p>Here is a complete example, from the query to the graph.<br \/>\nThe 4th DQL query and its output:<br \/>\n<code><br \/>\nselect datefloor(month, r_creation_date) as \"creation_month\", count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) group by datefloor(month, r_creation_date) order by 1;<br \/>\ncreation_month             count(*)                tot_size<br \/>\n-------------------------  ----------------------  ----------------------<br \/>\n11\/1\/2017 01:00:00                            703                54142079<br \/>\n12\/1\/2017 01:00:00                            120                  323552<br \/>\n3\/1\/2018 01:00:00                              38                   66288<br \/>\n4\/1\/2018 02:00:00                             469                 1427865<br \/>\n5\/1\/2018 02:00:00                              86                  584453<br \/>\n6\/1\/2018 02:00:00                              20                  150464<br \/>\n7\/1\/2018 02:00:00                              40                  301341<br \/>\n8\/1\/2018 02:00:00                              32                  151333<br \/>\n9\/1\/2018 02:00:00                              42                  323772<br \/>\n(9 rows affected)<br \/>\n<\/code><br \/>\nThe command to generate the graph:<br \/>\n<code><br \/>\npwd<br \/>\n\/home\/dmadmin\/graph-stats<br \/>\n.\/graph-stats.py --docbase dmtest<br \/>\n<\/code><br \/>\nA simplified generated html page showing only the count(*) variable:<\/p>\n<pre class=\"brush: html; gutter: true; first-line: 1; highlight: []\">\n&lt;!DOCTYPE html PUBLIC &quot;-\/\/W3C\/\/DTD HTML 3.2\/\/EN&quot;&gt;\n&lt;html&gt;\n      &lt;head&gt;\n         &lt;title&gt;Statistical Graphs for Repository dmtest&lt;\/title&gt;\n         &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text\/html; charset=utf-8&quot;&gt;\n         &lt;script src=&quot;http:\/\/192.168.56.10:8000\/plotly-latest.min.js&quot;&gt;&lt;\/script&gt;\n      &lt;\/head&gt;\n      &lt;body&gt;\n      &lt;div id=&quot;TheChart&quot; style=&quot;width:400px;height:400px;&quot;&gt;&lt;\/div&gt;\n      &lt;script&gt;\n         var ctx = document.getElementById(&quot;TheChart&quot;);\n         Plotly.newPlot(ctx, [{\n                             type: &quot;bar&quot;,\n                             name: &quot;new docs&quot;,\n                             x: ['2017\/11', '2017\/12', '2018\/03', '2018\/04', '2018\/05', '2018\/06', '2018\/07', '2018\/08', '2018\/09'],\n                             y: ['703', '120', '38', '469', '86', '20', '40', '32', '42'],\n                             marker: {\n                                        color: '#4e6bed',\n                                        line: {\n                                                 width: 2.5\n                                              }\n                                     }\n                            }],\n                            {\n                               title: &quot;New Documents&quot;,\n                               font: {size: 18}\n                            },\n                        {responsive: true});\n      &lt;\/script&gt;\n      &lt;\/body&gt;\n&lt;\/html&gt;\n<\/pre>\n<p>Its resulting file:<br \/>\n<code><br \/>\nll graphs<br \/>\ntotal 2756<br \/>\n-rw-rw-r-- 1 dmadmin dmadmin 2814564 Oct 12 14:18 plotly-latest.min.js<br \/>\n-rw-rw-r-- 1 dmadmin dmadmin    1713 Oct 12 14:18 dmtest-20181012-141811.html<br \/>\n<\/code><br \/>\nSame but exposed by the mini-web server and viewed from a browser:<br \/>\n<a href=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2022\/04\/Screenshot-from-2018-11-03-15-02-56.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2022\/04\/Screenshot-from-2018-11-03-15-02-56.png\" alt=\"Screenshot from 2018-11-03 15-02-56\" width=\"300\" height=\"147\" class=\"alignnone size-medium wp-image-29130\" \/><\/a><br \/>\nWhen clicking on one of the html file, a graph such as the one below is displayed:<br \/>\n<a href=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2022\/04\/Screenshot-from-2018-11-03-15-08-52.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2022\/04\/Screenshot-from-2018-11-03-15-08-52.png\" alt=\"Screenshot from 2018-11-03 15-08-52\" width=\"300\" height=\"219\" class=\"alignnone size-medium wp-image-29132\" \/><\/a><\/p>\n<h3>The python script<\/h3>\n<p>The following python script runs the 13 queries above and produce one large HTML page containing the graphs of all the datasets. It can also be used to query a list of local docbases and generate one single, huge html report or one report per docbase. It can even directly output the produced html code to stdout for further in-line processing, e.g. to somewhat compact the javascript code:<br \/>\n<code><br \/>\n.\/graph-stats.py --docbase dmtest -o stdout | gawk '{printf $0}' &gt; graphs\/dmtest-gibberish.html<br \/>\n&nbsp;<br \/>\nwc -l graphs\/-gibberish.html<br \/>\n0 graphs\/-gibberish.html<br \/>\n&nbsp;<br \/>\nless graphs\/dmtest-gibberish.html<br \/>\n<a href=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2022\/04\/Screenshot-from-2018-11-03-15-26-11.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2022\/04\/Screenshot-from-2018-11-03-15-26-11.png\" alt=\"Screenshot from 2018-11-03 15-26-11\" width=\"800\" height=\"300\" class=\"alignnone size-medium wp-image-29135\" \/><\/a><br \/>\n<\/code><br \/>\nSo, here is the script:<\/p>\n<pre class=\"brush: python; gutter: true; first-line: 1; highlight: [615,632,654,676,699,722,738,761,777,793,809,825,841]\">\n#!\/usr\/bin\/env python\n\n# 10\/2018, C. Cervini, dbi-services;\n \nimport sys\nimport getopt\nfrom datetime import datetime\nimport json\nimport DctmAPI\n\ndef Usage():\n   print(\"\"\"\nUsage:\nConnects as dmadmin\/xxxx to a local repository and generates an HTML page containing the plots of several DQL queries result;\nUsage:\n       .\/graph-stats.py -h|--help | -d|--docbase &lt;docbase&gt;{,&lt;docbase&gt;} \\[-o|--output_file &lt;output_file&gt;\\]\n&lt;docbase&gt; can be one repository or a comma-separated list of repositories;\nif &lt;output_file&gt; is omitted, the html page is output to .\/graphs\/&lt;docbase&gt;-$(date +\"%Y%m%d-%H%M%S\").html;\n&lt;output_file&gt; can be \"stdout\", which is useful for CGI programming;\nExample:\n       .\/graph-stats.py -d dmtest,mail_archive,doc_engineering\nwill query the docbases dmtest, mail_archive and doc_engineering and output the graphs to the files &lt;docbase&gt;-$(date +\"%Y%m%d-%H%M%S\").html, one file per docbase;\n       .\/graph-stats.py -d mail_archive,doc_engineering -o all_docbase_current_status\nwill query the docbases mail_archive and doc_engineering and output all the graphs to the unique file all_docbase_current_status;\n       .\/graph-stats.py -d mail-archive\nwill query docbase mail-archive and output the graphs to the file mail-archive-$(date +\"%Y%m%d-%H%M%S\").html;\n       .\/graph-stats.py --docbase dmtest --output dmtest.html\nwill query docbase dmtest and output the graphs to the file dmtest.html;\n       .\/graph-stats.py -d dmtest --output stdout\nwill query docbase dmtest and output the graphs to stdout;\n\"\"\")\n\ndef Plot2HTML(div, graph_title, data, data_labels, bLineGraph = False, mode = None):\n   global html_output\n   if None != html_output:\n      sys.stdout = open(html_output, \"a\")\n\n   # start the html page;\n   if \"b\" == mode:\n      global server, page_title\n      print('''\n&lt;!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD HTML 3.2\/\/EN\"&gt;\n&lt;html&gt;\n   &lt;head&gt;\n      &lt;title&gt;''' + page_title + '''&lt;\/title&gt;\n      &lt;meta http-equiv=\"Content-Type\" content=\"text\/html; charset=utf-8\"&gt;\n      &lt;script src=\"http:\/\/''' + server + '''\/plotly-latest.min.js\"&gt;&lt;\/script&gt;\n   &lt;\/head&gt;\n   &lt;body&gt;\n   &lt;center&gt;&lt;h3&gt;''' + page_title + '''&lt;\/h3&gt;&lt;\/center&gt;\n''')\n\n   # append to the body of the html page;\n   if len(data_labels) == 4:\n      stack_labels = {}\n      for point in data:\n         stack_labels[point[data_labels[0]]] = 0\n      print('''\n      &lt;div style=\"width:1500px;height:600px;\"&gt;\n          &lt;div id=\"''' + div + '-' + data_labels[2] + '''\" style=\"width:50%; float:left;\"&gt;&lt;\/div&gt;\n          &lt;div id=\"''' + div + '-' + data_labels[3] + '''\" style=\"width:50%; float:left;\"&gt;&lt;\/div&gt;\n      &lt;\/div&gt;\n      &lt;script&gt;\n         var ctx = document.getElementById(\"''' + div + '-' + data_labels[2] + '''\");\n      ''')\n      variables = \"\"\n      for stack in stack_labels:\n         x = [point[data_labels[1]] for i, point in enumerate(data) if point[data_labels[0]] == stack]\n         y1 = [point[data_labels[2]] for i, point in enumerate(data) if point[data_labels[0]] == stack]\n         variables +=  (\"\" if not variables else \", \") + \"data_\" + stack \n         if bLineGraph:\n            vars()['data_' + stack] = {\n                                        'name': stack,\n                                        'type': \"scatter\",\n                                        'mode': \"lines\",\n                                        'x': x,\n                                        'y': y1,\n                                      };\n         else:\n            vars()['data_' + stack] = {\n                                        'name': stack,\n                                        'type': \"bar\",\n                                        'x': x,\n                                        'y': y1,\n                                        'width': 0.25,\n                                        'marker': {\n                                                     #'color': '#009933',\n                                                     'line': {\n                                                                'width': 1.0\n                                                             }\n                                                  }\n                                      };\n         print('data_' + stack + ' = ' + json.dumps(vars()['data_' + stack]) + ';')\n         layout = {\n                     'title': '&lt;b&gt;' + graph_title + '&lt;br&gt;' + data_labels[2] + '&lt;\/b&gt;',\n                     'legend': {'x': -.1, 'y': 1.2, 'font': {'size': 8}},\n                     'font': {'size': 12},\n                     'width': 750,\n                     'height': 600,\n                     'xaxis': {\n                                 'title': '&lt;b&gt;' + data_labels[1] + '&lt;\/b&gt;',\n                                 'titlefont': {'size': 10},\n                                 'tickangle': -45,\n                                 'tickfont': {'size': 8},\n                                 'zeroline': True,\n                                 'showline': True,\n                                 'categoryorder': \"category ascending\",\n                                 'type': \"category\"\n                              },\n                     'yaxis': {\n                                 'title': '&lt;b&gt;' + data_labels[2] + '&lt;\/b&gt;',\n                                 'titlefont': {'size': 10},\n                                 'zeroline': True,\n                                 'showline': True,\n                                 'tickfont': {'size': 8},\n                                 'showgrid': False\n                              }\n                  }\n         if not bLineGraph:\n            layout.update({'barmode': \"stack\", 'bargap': 0.15, 'bargroupgap': 0.5})\n         interaction = {'responsive': True, 'scrollZoom': True, 'editable': True}\n      print('''Plotly.newPlot(ctx,\n                              [''' + variables + '], ' +\n                              json.dumps(layout) + ',' +\n                              json.dumps(interaction) + ''');''')\n      for stack in stack_labels:\n         x = [point[data_labels[1]] for i, point in enumerate(data) if point[data_labels[0]] == stack]\n         y2 = [point[data_labels[3]] for i, point in enumerate(data) if point[data_labels[0]] == stack]\n         if bLineGraph:\n            vars()['data_' + stack] = {\n                                         'name': stack,\n                                         'type': \"scatter\",\n                                         'mode': \"lines\",\n                                         'x': x,\n                                         'y': y2\n                                      };\n         else:\n            vars()['data_' + stack] = {\n                                         'name': stack,\n                                         'type': \"bar\",\n                                         'x': x,\n                                         'y': y2,\n                                         'width': 0.25,\n                                         'marker': {\n                                                      #'color': '#009933',*\/\n                                                      'line': {\n                                                                 'width': 1.0\n                                                              }\n                                                   }\n                                      };\n         print('data_' + stack + ' = ' + json.dumps(vars()['data_' + stack]) + ';')\n      layout = {\n                  'title': '&lt;b&gt;' + graph_title + '&lt;br&gt;' + data_labels[3] + '&lt;\/b&gt;',\n                  'legend': {'x': -.1, 'y': 1.2, 'font': {'size': 8}},\n                  'font': {\n                             'size': 12\n                          },\n                  'width': 750,\n                  'height': 600,\n                  'xaxis': {\n                            'title': '&lt;b&gt;' + data_labels[1] + '&lt;\/b&gt;',\n                            'titlefont': {'size': 10},\n                            'tickangle': -45,\n                            'tickfont': {'size': 8},\n                            'zeroline': True,\n                            'showline': True,\n                            'categoryorder': \"category ascending\",\n                            'type': \"category\"\n                         },\n                  'yaxis': {\n                    'title': '&lt;b&gt;' + data_labels[3] + '&lt;\/b&gt;',\n                    'titlefont': {'size': 10},\n                    'zeroline': True,\n                    'showline': True,\n                    'tickfont': {'size': 8},\n                    'showgrid': False\n                  }\n               }\n      if not bLineGraph:\n         layout.update({'barmode': \"stack\", 'bargap': 0.15, 'bargroupgap': 0.5})\n      interaction = {'responsive': True, 'scrollZoom': True, 'editable': True}\n      print('''\n      var ctx = document.getElementById(\"''' + div + '-' + data_labels[3] + '''\");\n      Plotly.newPlot(ctx,\n                     [''' + variables + '],' + \n                     json.dumps(layout) + ''',\n                     ''' + json.dumps(interaction) + ''');\n      &lt;\/script&gt;\n''')\n   elif len(data_labels) == 3:\n      print('''\n      &lt;div style=\"width:1200px;height:600px;\"&gt;\n          &lt;div id=\"''' + div + '''\" style=\"width:75%; float:left;\"&gt;&lt;\/div&gt;\n          &lt;div id=\"''' + div + '''-pie\" style=\"width:25%; float:left;\"&gt;&lt;\/div&gt;\n      &lt;\/div&gt;\n      &lt;script&gt;\n         var ctx = document.getElementById(\"''' + div + '''\");\n''')\n      traces = []\n      if not bLineGraph:\n         traces = [\n                     {\n                        'name': data_labels[1],\n                        'type': \"bar\",\n                        'x': [point[data_labels[0]] for i, point in enumerate(data)],\n                        'y': [point[data_labels[1]] for i, point in enumerate(data)],\n                        'width': 0.25,\n                        'marker': {\n                                     'color': '#009933',\n                                     'line': {\n                                              'width': 1.0\n                                             }\n                                  },\n                     },\n                     # work around for bug \"Grouped bar charts do not work with multiple Y axes\";\n                     # see https:\/\/github.com\/plotly\/plotly.js\/issues\/78;\n                     # must be inserted here;\n                     # invisible second trace in the first group\n                     {\n                        'x': [point[data_labels[0]] for i, point in enumerate(data)],\n                        'barmode': \"overlay\",\n                        'y': [0], 'type': 'bar', 'hoverinfo': 'none', 'showlegend': False\n                     },\n                     # invisible first trace in the second group\n                     {\n                        'x': [point[data_labels[0]] for i, point in enumerate(data)],\n                        'y': [0], 'type': 'bar', 'yaxis': 'y2', 'hoverinfo': 'none', 'showlegend': False\n                     },\n                     {\n                        'name': data_labels[2],\n                        'type': \"bar\",\n                        'x': [point[data_labels[0]] for i, point in enumerate(data)],\n                        'y': [point[data_labels[2]] for i, point in enumerate(data)],\n                        'width': 0.25,\n                        'yaxis': \"y2\",\n                        'marker': {\n                                     'color': '#4e6bed',\n                                     'line': {\n                                                'width': 1.0\n                                             }\n                                  }\n                     }\n                   ]\n      else:\n         traces = [\n                     {\n                        'name': data_labels[1],\n                        'type': \"scatter\",\n                        'mode': \"lines\",\n                        'x': [point[data_labels[0]] for i, point in enumerate(data)],\n                        'y': [point[data_labels[1]] for i, point in enumerate(data)],\n                        'width': 0.25,\n                     },\n                     {\n                        'name': data_labels[2],\n                        'type': \"scatter\",\n                        'mode': \"lines\",\n                        'x': [point[data_labels[0]] for i, point in enumerate(data)],\n                        'y': [point[data_labels[2]] for i, point in enumerate(data)],\n                        'width': 0.25,\n                        'yaxis': \"y2\",\n                     }\n                  ]\n      layout = {\n                  'title': '&lt;b&gt;' + graph_title + '&lt;\/b&gt;',\n                  'legend': {'x': -.1, 'y': 1.2, 'font': {'size': 8}},\n                  'font': {'size': 12},\n                  'width': 800,\n                  'height': 600,\n                  'xaxis': {\n                              'title': '&lt;b&gt;' + data_labels[0] + '&lt;\/b&gt;',\n                              'titlefont': {'size': 10},\n                              'tickangle': -45,\n                              'tickfont': {'size': 8},\n                              'zeroline': True,\n                              'showline': True\n                           },\n                  'yaxis': {\n                              'title': '&lt;b&gt;' + data_labels[1] + '&lt;\/b&gt;',\n                              'titlefont': {'size': 10},\n                              'zeroline': True,\n                              'showline': True,\n                              'tickfont': {'size': 8},\n                              'showgrid': False,\n                           },\n                  'yaxis2': {\n                               'title': '&lt;b&gt;' + data_labels[2] + '&lt;\/b&gt;',\n                               'titlefont': {'size': 10},\n                               'tickfont': {'size': 8},\n                               'zeroline': True,\n                               'showline': True,\n                               'overlaying': \"y\",\n                               'side': \"right\",\n                               'showgrid': False,\n                            }\n               }\n      if bLineGraph:\n         layout['yaxis'].update({'type': \"linear\"})\n         layout['yaxis2'].update({'type': \"linear\"})\n         pass\n      else:\n         layout.update({'barmode': \"group\", 'bargap': 0.15, 'bargroupgap': 0.5})\n      interaction = {'responsive': True, 'scrollZoom': True, 'editable': True}\n      print('''\n         Plotly.newPlot(ctx,\n                        ''' + json.dumps(traces) + ''',\n                        ''' + json.dumps(layout) + ''',\n                        ''' + json.dumps(interaction) + ''');\n      &lt;\/script&gt;\n\n''')\n      if not bLineGraph:\n         traces =  [\n                      {\n                         'name': data_labels[1],\n                         'hole': .4,\n                         'type': \"pie\",\n                         'labels': [point[data_labels[0]] for i, point in enumerate(data)],\n                         'values': [point[data_labels[1]] for i, point in enumerate(data)],\n                         'domain': {\n                            'row': 0,\n                            'column': 0\n                         },\n                         'outsidetextfont': {'size': 8},\n                         'insidetextfont': {'size': 8},\n                         'legend': {'font': {'size': 8}},\n                         'textfont': {'size': 8},\n                         'font': {'size': 8},\n                         'hoverinfo': 'label+percent+name',\n                         'hoverlabel': {'font': {'size': 8}},\n                         'textinfo': 'none'\n                      },\n                      {\n                         'name': data_labels[2],\n                         'hole': .4,\n                         'type': \"pie\",\n                         'labels': [point[data_labels[0]] for i, point in enumerate(data)],\n                         'values': [point[data_labels[2]] for i, point in enumerate(data)],\n                         'domain': {\n                            'row': 1,\n                            'column': 0\n                         },\n                         'outsidetextfont': {'size': 8},\n                         'insidetextfont': {'size': 8},\n                         'legend': {'font': {'size': 8}},\n                         'textfont': {'size': 8},\n                         'font': {'size': 8},\n                         'hoverinfo': 'label+percent+name',\n                         'hoverlabel': {'font': {'size': 8}},\n                         'textinfo': 'none'\n                      },\n                   ]\n         layout = {\n                     'title': '&lt;b&gt;' + graph_title + '&lt;\/b&gt;',\n                     'annotations': [\n                                       {\n                                          'font': {\n                                                    'size': 8\n                                                  },\n                                          'showarrow': False,\n                                          'text': '&lt;b&gt;' + data_labels[1] + '&lt;\/b&gt;',\n                                          'x': 0.5,\n                                          'y': 0.8\n                                       },\n                                       {\n                                          'font': {\n                                                     'size': 8\n                                                  },\n                                          'showarrow': False,\n                                          'text': '&lt;b&gt;' + data_labels[2] + '&lt;\/b&gt;',\n                                          'x': 0.5,\n                                          'y': 0.25\n                                       }\n                                    ],\n                     'height': 600,\n                     'width': 600,\n                     'grid': {'rows': 2, 'columns': 1},\n                     'legend': {'font': {'size': 10}}\n                  }\n\n         print('''\n      &lt;script&gt;\n         var ctx = document.getElementById(\"''' + div + '''-pie\");\n         Plotly.newPlot(ctx,\n                        ''' + json.dumps(traces) + ''',\n                        ''' + json.dumps(layout) + '''\n);\n      &lt;\/script&gt;\n''')\n   elif len(data_labels) == 2:\n      trace = [\n                 {\n                    'name': data_labels[1],\n                    'type': \"bar\",\n                    'x': [point[data_labels[0]] for i, point in enumerate(data)],\n                    'y': [point[data_labels[1]] for i, point in enumerate(data)],\n                    'width': 0.25,\n                    'marker': {\n                                 'color': '#009933',\n                                 'line': {\n                                            'width': 1.0\n                                         }\n                              }\n                 }\n              ]\n      layout = {\n                  'title': '&lt;b&gt;' + graph_title + '&lt;\/b&gt;',\n                  'legend': {'x': -.1, 'y': 1.2, 'font': {'size': 8}},\n                  'font': {'size': 12},\n                  'height': 600,\n                  'width': 800,\n                  'xaxis': {\n                            'title': '&lt;b&gt;' + data_labels[0] + '&lt;\/b&gt;',\n                            'titlefont': {'size': 10},\n                            'tickangle': -45,\n                            'tickfont': {'size': 8},\n                            'zeroline': True,\n                          },\n                  'yaxis': {\n                            'title': '&lt;b&gt;' + data_labels[1] + '&lt;\/b&gt;',\n                            'titlefont': {'size': 10},\n                            'zeroline': True,\n                            'showline': True,\n                            'tickfont': {'size': 8},\n                            'showgrid': False\n                          },\n                  'barmode': \"group\",\n                  'bargap': 0.15,\n                  'bargroupgap': 0.5\n               }\n      interaction = {'responsive': True, 'scrollZoom': True, 'editable': True};\n      print('''        \n      &lt;div style=\"width:1200px;height:600px;\"&gt;\n          &lt;div id=\"''' + div + '''\" style=\"width:75%; float:left;\"&gt;&lt;\/div&gt; \n          &lt;div id=\"''' + div + '''-pie\" style=\"width:25%; float:left;\"&gt;&lt;\/div&gt;\n      &lt;\/div&gt;\n      &lt;script&gt;\n         var ctx = document.getElementById(\"''' + div + '''\");\n         Plotly.newPlot(ctx,\n                        ''' + json.dumps(trace) + ''',\n                        ''' + json.dumps(layout) + ''',\n                        ''' + json.dumps(interaction) + ''');\n      &lt;\/script&gt;\n''')\n      trace =  [\n                  {\n                     'name': data_labels[1],\n                     'hole': .4,\n                     'type': \"pie\",\n                     'labels': [point[data_labels[0]] for i, point in enumerate(data)],\n                     'values': [point[data_labels[1]] for i, point in enumerate(data)],\n                     'domain': {\n                                  'row': 0,\n                                  'column': 0\n                               },\n                     'outsidetextfont': {'size': 8},\n                     'insidetextfont': {'size': 8},\n                     'legend': {'font': {'size': 8}},\n                     'textfont': {'size': 8},\n                     'font': {'size': 8},\n                     'hoverinfo': 'label+percent+name',\n                     'hoverlabel': {'font': {'size': 8}},\n                     'textinfo': 'none'\n                  },\n               ]\n      layout = {\n                  'title': '&lt;b&gt;' + graph_title + '&lt;\/b&gt;',\n                  'annotations': [\n                                    {\n                                       'font': {\n                                                  'size': 8\n                                               },\n                                       'showarrow': False,\n                                       'text': '&lt;b&gt;' + data_labels[1] + '&lt;\/b&gt;',\n                                       'x': 0.5,\n                                       'y': 0.5\n                                    },\n                                 ],\n                  'height': 600,\n                  'width': 600,\n                  'grid': {'rows': 1, 'columns': 1},\n                  'legend': {'font': {'size': 10}}\n               }\n      print('''        \n      &lt;script&gt;\n         var ctx = document.getElementById(\"''' + div + '''-pie\");\n         Plotly.newPlot(ctx,\n                        ''' + json.dumps(trace) + ''',\n                        ''' + json.dumps(layout) + ''');\n      &lt;\/script&gt;\n''')\n   else:\n      print(\"illegal data_label value: \" + repr(data_label))\n\n   # closes the html page;\n   if \"e\" == mode:\n      print('''\n   &lt;\/body&gt;\n&lt;\/html&gt;\n''')\n\n   # restores default output stream;\n   if None != html_output:\n      sys.stdout = sys.__stdout__\n\ndef cumulAndExtend2():\n   \"\"\"\n   for 2 variables in resultset;\n   \"\"\"\n   global rawdata\n   sum_count = 0\n   sum_size = 0\n   new_month = str(datetime.now().year) + \"\/\" + str(datetime.now().month)\n   for ind, point in enumerate(rawdata):\n      sum_count += int(point['count(*)'])\n      point['count(*)'] = sum_count\n      sum_size += int(point['tot_size'])\n      point['tot_size'] = sum_size\n   if rawdata[ind]['month'] &lt; new_month:\n      new_point = dict(rawdata[ind])\n      new_point['month'] = new_month\n      rawdata.append(new_point)\n   DctmAPI.show(rawdata)\n\ndef cumulAndExtend3(key):\n   \"\"\"\n   for 3 variables in resultset;\n   \"\"\"\n   global rawdata\n   prec_doc = \"\"\n   sum_count = 0\n   sum_size = 0\n   new_month = str(datetime.now().year) + \"\/\" + str(datetime.now().month)\n   for ind, point in enumerate(rawdata):\n      if \"\" == prec_doc:\n         prec_doc = point[key]\n      if point[key] != prec_doc:\n         # duplicate the last point so the line graphe shows a flat line and not a simple dot when there is a unique point for the document type;\n         if rawdata[ind - 1]['month'] &lt; new_month:\n            new_point = dict(rawdata[ind - 1])\n            new_point['month'] = new_month\n            rawdata.insert(ind, new_point)\n            prec_doc = point[key]\n            sum_count = 0\n            sum_size = 0\n      else:\n         sum_count += int(point['count(*)'])\n         point['count(*)'] = sum_count\n         sum_size += int(point['tot_size'])\n         point['tot_size'] = sum_size\n   if rawdata[ind]['month'] &lt; new_month:\n      new_point = dict(rawdata[ind])\n      new_point['month'] = new_month\n      rawdata.append(new_point)\n   DctmAPI.show(rawdata)\n\n# -----------------\n# main;\nif __name__ == \"__main__\":\n   DctmAPI.logLevel = 0\n \n   # parse the command-line parameters;\n   # old-style for I don't need more flexibility here;\n   repository = None\n   output_file = None\n   try:\n      (opts, args) = getopt.getopt(sys.argv[1:], \"hd:o:\", [\"help\", \"docbase=\", \"output=\"])\n   except getopt.GetoptError:\n      print(\"Illegal option\")\n      print(\".\/graph-stats.py -h|--help | -d|--docbase &lt;docbase&gt;{,&lt;docbase&gt;} [-o|--output_file &lt;output_file&gt;]\")\n      sys.exit(1)\n   for opt, arg in opts:\n      if opt in (\"-h\", \"--help\"):\n         Usage()\n         sys.exit()\n      elif opt in (\"-d\", \"--docbase\"):\n         repository = arg\n      elif opt in (\"-o\", \"--output\"):\n         output_file = arg\n   if None == repository:\n      print(\"at least one repository must be specified\")\n      Usage()\n      sys.exit()\n   DctmAPI.show(\"Will connect to docbase(s): \" + repository + \" and output to \" + (\"stdout\" if \"stdout\" == output_file else \"one single file \" + output_file if output_file is not None else \"one file per docbase\"))\n \n   # needed to locally import the js library Plotly;\n   server = \"192.168.56.10:8000\"\n\n   docbase_done = set()\n   status = DctmAPI.dmInit()\n   for pointer, docbase in enumerate(repository.split(\",\")):\n      # graphe_1;\n      if docbase in docbase_done:\n         print(\"Warning: docbase {:s} was already processed and won't be again, skipping ...\".format(docbase))\n         continue\n      docbase_done.add(docbase)\n      session = DctmAPI.connect(docbase = docbase, user_name = \"dmadmin\", password = \"dmadmin\")\n      if session is None:\n         print(\"no session opened, exiting ...\")\n         exit(1)\n\n      page_title = \"Graphical Overview of Repository \" + docbase\n      if None == output_file:\n         html_output = \".\/graphs\/\" + docbase + \"-\" + datetime.today().strftime(\"%Y%m%d-%H%M%S\") + \".html\"\n      elif \"stdout\" == output_file:\n         html_output = None\n      else:\n         html_output = output_file\n\n      graph_name = \"graphe_1\"\n      DctmAPI.show(graph_name, beg_sep = True)\n      stmt = \"\"\"select r_object_type, count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) group by r_object_type order by 1\"\"\"\n      rawdata = []\n      attr_name = []\n      status = DctmAPI.select2dict(session, stmt, rawdata, attr_name)\n      DctmAPI.show(rawdata)\n      if not status:\n         print(\"select [\" + stmt + \"] was not successful\")\n         exit(1)\n      Plot2HTML(div = graph_name,\n                graph_title = \"Q1. Present Count &amp; Size Per Document Type\",\n                data = rawdata,\n                data_labels = attr_name,\n                mode = (\"b\" if ((\"stdout\" == output_file or output_file is not None) and 0 == pointer) or output_file is None else None))\n\n      # graphe_2;\n      graph_name = \"graphe_2\"\n      DctmAPI.show(graph_name, beg_sep = True)\n      stmt = \"\"\"select r_object_type, datetostring(r_creation_date, 'yyyy\/mm') as \"month\", count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) group by r_object_type, datetostring(r_creation_date, 'yyyy\/mm') order by 1, 2\"\"\"\n      rawdata = []\n      attr_name = []\n      status = DctmAPI.select2dict(session, stmt, rawdata, attr_name)\n      DctmAPI.show(rawdata)\n      if not status:\n         print(\"select [\" + stmt + \"] was not successful\")\n         exit(1)\n      Plot2HTML(div = graph_name,\n                graph_title = \"Q2. Monthly New Documents per Type\",\n                data = rawdata,\n                data_labels = attr_name)\n      cumulAndExtend3('r_object_type')\n      Plot2HTML(div = graph_name + \"l\",\n                graph_title = \"Q2l. Cumulated Monthly Documents per Type\",\n                data = rawdata,\n                data_labels = attr_name,\n                bLineGraph = True)\n   \n      # graphe_3;\n      graph_name = \"graphe_3\"\n      DctmAPI.show(graph_name, beg_sep = True)\n      stmt = \"\"\"select r_object_type, datetostring(r_modify_date, 'yyyy\/mm') as \"month\", count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) where r_creation_date &lt; r_modify_date group by r_object_type, datetostring(r_modify_date, 'yyyy\/mm') order by 1, 2\"\"\"\n      rawdata = []\n      attr_name = []\n      status = DctmAPI.select2dict(session, stmt, rawdata, attr_name)\n      DctmAPI.show(rawdata)\n      if not status:\n         print(\"select [\" + stmt + \"] was not successful\")\n         exit(1)\n      Plot2HTML(div = graph_name,\n                graph_title = \"Q3. Monthly Modified Documents per Type\",\n                data = rawdata,\n                data_labels = attr_name)\n      cumulAndExtend3('r_object_type')\n      Plot2HTML(div = graph_name + \"l\",\n                graph_title = \"Q3l. Cumulated Monthly Modified Documents per Type\",\n                data = rawdata,\n                data_labels = attr_name,\n                bLineGraph = True)\n\n      # graphe_4;\n      graph_name = \"graphe_4\"\n      DctmAPI.show(graph_name, beg_sep = True)\n      stmt = \"\"\"select datetostring(r_creation_date, 'yyyy\/mm') as \"month\", count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) group by datetostring(r_creation_date, 'yyyy\/mm') order by 1\"\"\"\n      rawdata = []\n      attr_name = []\n      status = DctmAPI.select2dict(session, stmt, rawdata, attr_name)\n      DctmAPI.show(rawdata)\n      if not status:\n         print(\"select [\" + stmt + \"] was not successful\")\n         exit(1)\n      Plot2HTML(div = graph_name,\n                graph_title = \"Q4. Monthly New Documents\",\n                data = rawdata,\n                data_labels = attr_name )\n      cumulAndExtend2()\n      Plot2HTML(div = graph_name + \"l\",\n                graph_title = \"Q4l. Cumulated Monthly Documents\",\n                data = rawdata,\n                data_labels = attr_name,\n                bLineGraph = True)\n\n      # graphe_5;\n      graph_name = \"graphe_5\"\n      DctmAPI.show(graph_name, beg_sep = True)\n      stmt = \"\"\"select datetostring(r_modify_date, 'yyyy\/mm') as \"month\", count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) where r_creation_date != r_modify_date group by datetostring(r_modify_date, 'yyyy\/mm') order by 1;\n        \"\"\"\n      rawdata = []\n      attr_name = []\n      status = DctmAPI.select2dict(session, stmt, rawdata, attr_name)\n      DctmAPI.show(rawdata)\n      if not status:\n         print(\"select [\" + stmt + \"] was not successful\")\n         exit(1)\n      Plot2HTML(div = graph_name,\n                graph_title = \"Q5. Monthly Modified Documents\",\n                data = rawdata,\n                data_labels = attr_name)\n      cumulAndExtend2()\n      Plot2HTML(div = graph_name + \"l\",\n                graph_title = \"Q5l. Cumulated Monthly Modified Documents\",\n                data = rawdata,\n                data_labels = attr_name,\n                bLineGraph = True,\n                mode = 'e')\n\n      # graphe_6;\n      graph_name = \"graphe_6\"\n      DctmAPI.show(graph_name, beg_sep = True)\n      stmt = \"\"\"select a_content_type, count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) group by a_content_type order by 1\"\"\"\n      rawdata = []\n      attr_name = []\n      status = DctmAPI.select2dict(session, stmt, rawdata, attr_name)\n      DctmAPI.show(rawdata)\n      if not status:\n         print(\"select [\" + stmt + \"] was not successful\")\n         exit(1)\n      Plot2HTML(div = graph_name,\n                graph_title = \"Q6. Count &amp; Size Per Content Format\",\n                data = rawdata,\n                data_labels = attr_name)\n\n      # graphe_7;\n      graph_name = \"graphe_7\"\n      DctmAPI.show(graph_name, beg_sep = True)\n      stmt = \"\"\"select a_content_type, datetostring(r_creation_date, 'yyyy-mm') as \"month\", count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) group by a_content_type, datetostring(r_creation_date, 'yyyy-mm') having count(*) &gt; 10 order by 1, 2\"\"\"\n      rawdata = []\n      attr_name = []\n      status = DctmAPI.select2dict(session, stmt, rawdata, attr_name)\n      DctmAPI.show(rawdata)\n      if not status:\n         print(\"select [\" + stmt + \"] was not successful\")\n         exit(1)\n      Plot2HTML(div = graph_name,\n                graph_title = \"Q7. Monthly Created Documents Per Content Format with count(*) &gt; 10\",\n                data = rawdata,\n                data_labels = attr_name)\n      cumulAndExtend3('a_content_type')\n      DctmAPI.show(rawdata)\n      Plot2HTML(div = graph_name + \"l\",\n                graph_title = \"Q7l. Cumulated Monthly Created Documents Per Content Format with count(*) &gt; 10\",\n                data = rawdata,\n                data_labels = attr_name,\n                bLineGraph = True)\n\n      # graphe_8;\n      graph_name = \"graphe_8\"\n      DctmAPI.show(graph_name, beg_sep = True)\n      stmt = \"\"\"select a_storage_type, count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) group by a_storage_type order by a_storage_type\"\"\"\n      rawdata = []\n      attr_name = []\n      status = DctmAPI.select2dict(session, stmt, rawdata, attr_name)\n      DctmAPI.show(rawdata)\n      if not status:\n         print(\"select [\" + stmt + \"] was not successful\")\n         exit(1)\n      Plot2HTML(div = graph_name,\n                graph_title = \"Q8. Count &amp; Size Per Filestore\",\n                data = rawdata,\n                data_labels = attr_name)\n\n      # graphe_9;\n      graph_name = \"graphe_9\"\n      DctmAPI.show(graph_name, beg_sep = True)\n      stmt = \"\"\"select r_object_type, a_storage_type, count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) group by r_object_type, a_storage_type order by r_object_type, a_storage_type\"\"\"\n      rawdata = []\n      attr_name = []\n      status = DctmAPI.select2dict(session, stmt, rawdata, attr_name)\n      DctmAPI.show(rawdata)\n      if not status:\n         print(\"select [\" + stmt + \"] was not successful\")\n         exit(1)\n      Plot2HTML(div = graph_name,\n                graph_title = \"Q9. Count &amp; Size Per Document Type &amp; Filestore\",\n                data = rawdata,\n                data_labels = attr_name)\n\n      # graphe_10;\n      graph_name = \"graphe_10\"\n      DctmAPI.show(graph_name, beg_sep = True)\n      stmt = \"\"\"select a_content_type, a_storage_type, count(*), sum(r_full_content_size) as \"tot_size\" from dm_document(all) group by a_content_type, a_storage_type having count(*) &gt; 10 order by a_content_type, a_storage_type\"\"\"\n      rawdata = []\n      attr_name = []\n      status = DctmAPI.select2dict(session, stmt, rawdata, attr_name)\n      DctmAPI.show(rawdata)\n      if not status:\n         print(\"select [\" + stmt + \"] was not successful\")\n         exit(1)\n      Plot2HTML(div = graph_name,\n                graph_title = \"Q10. Count &amp; Size Per Content Format &amp; Filestore with count(*) &gt; 10\",\n                data = rawdata,\n                data_labels = attr_name)\n\n      # graphe_11;\n      graph_name = \"graphe_11\"\n      DctmAPI.show(graph_name, beg_sep = True)\n      stmt = \"\"\"select owner_name, count(*) from dm_acl group by owner_name order by 2 desc\"\"\"\n      rawdata = []\n      attr_name = []\n      status = DctmAPI.select2dict(session, stmt, rawdata, attr_name)\n      DctmAPI.show(rawdata)\n      if not status:\n         print(\"select [\" + stmt + \"] was not successful\")\n         exit(1)\n      Plot2HTML(div = graph_name,\n                graph_title = \"Q11. ACLs per owner\",\n                data = rawdata,\n                data_labels = attr_name)\n\n      # graphe_12;\n      graph_name = \"graphe_12\"\n      DctmAPI.show(graph_name, beg_sep = True)\n      stmt = \"\"\"select acl_name, count(*) from dm_document(all) where acl_name not like 'dm_%' group by acl_name having count(*) &gt;= 10 order by 1\"\"\"\n      rawdata = []\n      attr_name = []\n      status = DctmAPI.select2dict(session, stmt, rawdata, attr_name)\n      DctmAPI.show(rawdata)\n      if not status:\n         print(\"select [\" + stmt + \"] was not successful\")\n         exit(1)\n      Plot2HTML(div = graph_name,\n                graph_title = \"Q12. External ACLs in Use by &gt;= 10 documents\",\n                data = rawdata,\n                data_labels = attr_name)\n\n      # graphe_13;\n      graph_name = \"graphe_13\"\n      DctmAPI.show(graph_name, beg_sep = True)\n      stmt = \"\"\"select name, count(*) from dmi_queue_item group by name order by 2 desc\"\"\"\n      rawdata = []\n      attr_name = []\n      status = DctmAPI.select2dict(session, stmt, rawdata, attr_name)\n      DctmAPI.show(rawdata)\n      if not status:\n         print(\"select [\" + stmt + \"] was not successful\")\n         exit(1)\n      Plot2HTML(div = graph_name,\n                graph_title = \"Q13. Queue Items by Name\",\n                data = rawdata,\n                data_labels = attr_name,\n                mode = ('e' if ((\"stdout\" == output_file or output_file is not None) and len(repository.split(\",\")) - 1 == pointer) or output_file is None else None))\n\n   status = DctmAPI.disconnect(session)\n   if not status:\n      print(\"error while  disconnecting\")\n<\/pre>\n<p>A pdf (sorry, uploading python scripts is not allowed on this site) of the script is available here too <a href=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2018\/11\/graph-stats.pdf\">graph-stats<\/a>.<br \/>\nHere is the DctmAPI.py as pdf too: <a href=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2018\/11\/DctmAPI.pdf\">DctmAPI<\/a>.<br \/>\nThe script invokes select2dict() from the module DctmAPI (as said before, it is also presented and accessible <a title=\"Adding A Documentum In Python\" href=\"https:\/\/www.dbi-services.com\/blog\/adding-a-documentum-extension-into-python\/\" target=\"_blank\" rel=\"noopener noreferrer\">here<\/a>) with the query and receives back the result into an array of dictionaries, i.e. each line of the array is a line from the query&#8217;s resultset. It then goes on creating the HTML page by invoking the function Plot2HTML(). On line 47, the js library is imported. Each graph is plotted in its own DIV section by Plotly&#8217;s js function newPlot(). All the required parameters are set in python dictionaries and passed as json literals to newPlot(). The conversion from python to javascript is conveniently done by the function dumps() from the json module. The HTML and js parts are produced by multi-line python&#8217;s print() statements.<\/p>\n<h3>Invoking the script<\/h3>\n<p>The script is invoked from the command-line through the following syntax:<\/p>\n<pre class=\"brush: python; gutter: false; first-line: 1; highlight: []\">\n.\/graph-stats.py -h|--help | -d|--docbase &lt;docbase&gt;{,&lt;docbase&gt;} [-o|--output_file &lt;output_file&gt;]\n<\/pre>\n<p>A Help option is available, which outputs the text below:<br \/>\n<code><br \/>\n.\/graph-stats.py --help<br \/>\nUsage:<br \/>\nConnects as dmadmin\/xxxx to a repository and generates an HTML page containing the plots of several DQL queries result;<br \/>\nUsage:<br \/>\n       .\/graph-stats.py -h|--help | -d|--docbase {,&lt;docbase&gt;} [-o|--output_file &lt;output_file&gt;]<br \/>\noutput_file can be one repository or a comma-separated list of repositories;<br \/>\nif output_file is omitted, the html page is output to .\/graphs\/-$(date +\"%Y%m%d-%H%M%S\").html;<br \/>\noutput_file can be \"stdout\", which is useful for CGI programming;<br \/>\nExample:<br \/>\n       .\/graph-stats.py -d dmtest,mail_archive,doc_engineering<br \/>\nwill query the docbases dmtest, mail_archive and doc_engineering and output the graphs to the files -$(date +\"%Y%m%d-%H%M%S\").html, one file per docbase;<br \/>\n       .\/graph-stats.py -d mail_archive,doc_engineering -o all_docbase_current_status<br \/>\nwill query the docbases mail_archive and doc_engineering and output all the graphs to the unique file all_docbase_current_status;<br \/>\n       .\/graph-stats.py -d mail-archive<br \/>\nwill query docbase mail-archive and output the graphs to the file mail-archive-$(date +\"%Y%m%d-%H%M%S\").html;<br \/>\n       .\/graph-stats.py --docbase dmtest --output dmtest.html<br \/>\nwill query docbase dmtest and output the graphs to the file dmtest.html;<br \/>\n       .\/graph-stats.py -d dmtest --output stdout<br \/>\nwill query docbase dmtest and output the graphs to stdout;<br \/>\n<\/code><br \/>\nAs the account used is the trusted, password-less dmadmin one, the script must run locally on the repository server machine but it is relatively easy to set it up for remote docbases, with a password prompt or without if using public key authentication (and still password-less in the remote docbases if using dmadmin). <\/p>\n<h3>Examples of charts<\/h3>\n<p>Now that we&#8217;ve seen the bits and pieces, let&#8217;s generate an HTML page with the graphs of all the above queries.<br \/>\nHere is an example when the queries where run against an out of the box repository named dmtest (all the graphs in one pdf file): <a href=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2018\/11\/output.pdf\">dmtest.pdf<\/a><br \/>\nFor more complex examples, just run the script against one of your docbases and see the results by yourself, as I can&#8217;t upload neither html files nor compressed files here.<br \/>\nAs shown, some queries are charted using 2 to 4 graphs, the objective being to offer the clearest view. E.g. query 1 computes counts and total sizes. They will be plotted on the same graph as grouped bars of 2 independent variables and on two distinct pie charts, one for each variable. Query 2 shows the count(*) and the total sizes by document type as 2 stacked bar graphs plus 2 line graphs of the cumulated monthly created documents by document type. The line graphs show the total number of created documents at the end of a month, not the total number of existing documents as of the end of each month; to have those numbers (an historical view of query 1), one needs to query the docbase monthly and store the numbers somewhere. Query 1 returns this status but only at the time it was run.<br \/>\nDiscussing Plotly is out of the scope of this article but suffice it to say that it allows one to work the chart interactively, e.g. removing variables (aka traces in Plotly&#8217;s parlance) so the rest of them are re-scaled for better readability, zoom parts of the charts, and scroll the graphs horizontally or vertically. A nice tip on hover is also available to show the exact values of a point, e.g.:<br \/>\n<a href=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2022\/04\/Screenshot-from-2018-11-03-19-07-28.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2022\/04\/Screenshot-from-2018-11-03-19-07-28.png\" alt=\"Screenshot from 2018-11-03 19-07-28\" width=\"300\" height=\"249\" class=\"alignnone size-medium wp-image-29157\" \/><\/a><br \/>\nPlotly even lets one completely edit the chart from within a js application hosted at Plotly, so this may not suit anybody if confidentiality is required; moreover, the graph can only be saved in a public area on their site, and subsequently downloaded from there, unless a per annum paying upgrade is subscribed. Nonetheless, the free features are well enough for most needs.<\/p>\n<h3>Leveraging Plot2HTML()<\/h3>\n<p>The script&#8217;s Plot2HTML() function can be leveraged so it is used to plot data from any source, even from a text file so the query is separated from its result&#8217;s graphical representation, which is useful if a direct access to the system to query is not possible. To do this, an easy to parse file format is necessary, e.g.:<\/p>\n<pre class=\"brush: python; gutter: false; first-line: 1; highlight: []\">\n# this is a commented line;\n# line_no   variable1    variable2 .....\n0          type of graph                                   # use B for bars, S for stacked bars, L for lines, P for pie or any combination of them (BL, LB, BP, PB, LP, PL, BPL);\n                                                           # one graph for each combination will be put on the html page in a nx2 cell grid, i.e. graphs  1 and 2 horizontally and graph 3 below,\n                                                           # in the same order as the graph type letters;\n1          graph title                                     # can be on several lines separated by html <br> for sub-titles;\n2          Xaxis_name     Yaxis1_name    Yaxis2_name       # names of axis, one X and up to 2 Y;\n3          Xaxis_values1  value1         value2            # first line of (x,y) pairs or (x,y1,y2) triplets if a second axis was specified;\n4          Xaxis_values2  value1         value2            # second line of data;\n...                                                        # the other lines;\nn          Xaxis_valuesn  valuen         valuen            # last line of data;\n<\/pre>\n<p>And that script could be invoked like this:<\/p>\n<pre class=\"brush: python; gutter: false; first-line: 1; highlight: []\">\nUsage:\n       .\/graph-it.py -h|--help | -f|--input_file &lt;filename&gt;{,&lt;filename&gt;} [-o|--output_file &lt;output_file&gt;]\n<\/pre>\n<p>The script itself just needs to parse and load the data from the input file(s) into an array of dictionaries and pass it to the plotting function Plot2HTML(). Of course, if the data is already in json format, the parsing step will be greatly simplified.<br \/>\nTime and mood permitting, I&#8217;ll do it in a future article.<\/p>\n<h3>Useful tools<\/h3>\n<p>The color of the many chart&#8217;s option (e.g. on line 87) can be specified through an rgb triplet or an HTML code. There are many color pickers on the web, e.g. <a title=\"HTML Color Picker\" href=\"https:\/\/www.w3schools.com\/colors\/colors_picker.asp\" target=\"_blank\" rel=\"noopener noreferrer\">this one<\/a>. Here, I let Plotly chose the colors as it sees fit, hence the not so attractive results.<br \/>\nIf you need to paste HTML code into an HTML page without it being interpreted by the browser, e.g. the code generated by graph-stats.py, some transformations are required, e.g. all the angle brackets need be replaced by their HTML entities representations. If that code does not contain confidential information, an on-line service can be used for that, such as <a title=\"Special Character to HTML Entities Converter\" href=\"https:\/\/online-toolz.com\/tools\/text-html-entities-convertor.php\" target=\"_blank\" rel=\"noopener noreferrer\">this one<\/a>, but it shouldn&#8217;t be too difficult to write a list of sed expressions that do the substitutions (e.g. this gawk one-liner takes care of the  characters &lt; and &gt;:<\/p>\n<pre class=\"brush: python; gutter: false; first-line: 1; highlight: []\">\ngawk -c '{gsub(\/&gt;\/, \"\\\\&gt;\"); gsub(\/&lt;\/, &quot;\\\\&lt;&quot;); print}&#039; myfile.html).\n<\/pre>\n<p>Other replacements can be added as the need arises.<\/p>\n<h3>Conclusion<\/h3>\n<p>All the above confirms yet again that it is possible to create cheap but useful, custom tools with almost no installation, except here a textual javascript library. All the rest is already available on any administrator&#8217;s decent system, command-line and python interpreters, and a browser which, by the way, is taking more and more importance as a tiers because of the wide spreading of javascript libraries that execute locally and the ever increasing performance of javascript engines. By the way, chromium is highly recommended here for visualizing those complex charts from real production repositories.<br \/>\nAs the source code is provided, it is possible to modify it in order to produce better looking graphs. This is the real arduous and time-consuming part in my humble opinion as it requires some artistic inclination and many experimentations. If you find a nicer color scheme, please feel free to contribute in the comments.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As the saying goes, &#8220;A Picture Is Worth A Thousands Words&#8221;. I&#8217;d add &#8220;And A Simple Graph Is Worth A Long, Abstruse List of Numbers&#8221;. And of words too, so let&#8217;s show off a little bit: Interested ? Then, please read on. It happens not so infrequently that we wish we could quickly plot a [&hellip;]<\/p>\n","protected":false},"author":40,"featured_media":11804,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[525],"tags":[],"type_dbi":[],"class_list":["post-11798","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-enterprise-content-management"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v27.2 (Yoast SEO v27.4) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>A Graphical Overview of a Repository - dbi Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.dbi-services.com\/blog\/a-graphical-overview-of-a-repository\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"A Graphical Overview of a Repository\" \/>\n<meta property=\"og:description\" content=\"As the saying goes, &#8220;A Picture Is Worth A Thousands Words&#8221;. I&#8217;d add &#8220;And A Simple Graph Is Worth A Long, Abstruse List of Numbers&#8221;. And of words too, so let&#8217;s show off a little bit: Interested ? Then, please read on. It happens not so infrequently that we wish we could quickly plot a [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.dbi-services.com\/blog\/a-graphical-overview-of-a-repository\/\" \/>\n<meta property=\"og:site_name\" content=\"dbi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2018-11-05T14:00:40+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-10-24T07:32:20+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2022\/04\/Screenshot-from-2018-10-12-14-32-46.png\" \/>\n\t<meta property=\"og:image:width\" content=\"493\" \/>\n\t<meta property=\"og:image:height\" content=\"572\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Middleware Team\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Middleware Team\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"41 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/a-graphical-overview-of-a-repository\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/a-graphical-overview-of-a-repository\\\/\"},\"author\":{\"name\":\"Middleware Team\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#\\\/schema\\\/person\\\/8d8563acfc6e604cce6507f45bac0ea1\"},\"headline\":\"A Graphical Overview of a Repository\",\"datePublished\":\"2018-11-05T14:00:40+00:00\",\"dateModified\":\"2025-10-24T07:32:20+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/a-graphical-overview-of-a-repository\\\/\"},\"wordCount\":2875,\"commentCount\":0,\"image\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/a-graphical-overview-of-a-repository\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/2\\\/2022\\\/04\\\/Screenshot-from-2018-10-12-14-32-46.png\",\"articleSection\":[\"Enterprise content management\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/a-graphical-overview-of-a-repository\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/a-graphical-overview-of-a-repository\\\/\",\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/a-graphical-overview-of-a-repository\\\/\",\"name\":\"A Graphical Overview of a Repository - dbi Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/a-graphical-overview-of-a-repository\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/a-graphical-overview-of-a-repository\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/2\\\/2022\\\/04\\\/Screenshot-from-2018-10-12-14-32-46.png\",\"datePublished\":\"2018-11-05T14:00:40+00:00\",\"dateModified\":\"2025-10-24T07:32:20+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#\\\/schema\\\/person\\\/8d8563acfc6e604cce6507f45bac0ea1\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/a-graphical-overview-of-a-repository\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/a-graphical-overview-of-a-repository\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/a-graphical-overview-of-a-repository\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/2\\\/2022\\\/04\\\/Screenshot-from-2018-10-12-14-32-46.png\",\"contentUrl\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/2\\\/2022\\\/04\\\/Screenshot-from-2018-10-12-14-32-46.png\",\"width\":493,\"height\":572},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/a-graphical-overview-of-a-repository\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"A Graphical Overview of a Repository\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/\",\"name\":\"dbi Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#\\\/schema\\\/person\\\/8d8563acfc6e604cce6507f45bac0ea1\",\"name\":\"Middleware Team\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/ddcae7ba0f9d1a0e7ae707f0e689e4a9c95bb48ec49c8e6d9cc86d43f4121cb6?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/ddcae7ba0f9d1a0e7ae707f0e689e4a9c95bb48ec49c8e6d9cc86d43f4121cb6?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/ddcae7ba0f9d1a0e7ae707f0e689e4a9c95bb48ec49c8e6d9cc86d43f4121cb6?s=96&d=mm&r=g\",\"caption\":\"Middleware Team\"},\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/author\\\/middleware-team\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"A Graphical Overview of a Repository - dbi Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.dbi-services.com\/blog\/a-graphical-overview-of-a-repository\/","og_locale":"en_US","og_type":"article","og_title":"A Graphical Overview of a Repository","og_description":"As the saying goes, &#8220;A Picture Is Worth A Thousands Words&#8221;. I&#8217;d add &#8220;And A Simple Graph Is Worth A Long, Abstruse List of Numbers&#8221;. And of words too, so let&#8217;s show off a little bit: Interested ? Then, please read on. It happens not so infrequently that we wish we could quickly plot a [&hellip;]","og_url":"https:\/\/www.dbi-services.com\/blog\/a-graphical-overview-of-a-repository\/","og_site_name":"dbi Blog","article_published_time":"2018-11-05T14:00:40+00:00","article_modified_time":"2025-10-24T07:32:20+00:00","og_image":[{"width":493,"height":572,"url":"http:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2022\/04\/Screenshot-from-2018-10-12-14-32-46.png","type":"image\/png"}],"author":"Middleware Team","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Middleware Team","Est. reading time":"41 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.dbi-services.com\/blog\/a-graphical-overview-of-a-repository\/#article","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/a-graphical-overview-of-a-repository\/"},"author":{"name":"Middleware Team","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/8d8563acfc6e604cce6507f45bac0ea1"},"headline":"A Graphical Overview of a Repository","datePublished":"2018-11-05T14:00:40+00:00","dateModified":"2025-10-24T07:32:20+00:00","mainEntityOfPage":{"@id":"https:\/\/www.dbi-services.com\/blog\/a-graphical-overview-of-a-repository\/"},"wordCount":2875,"commentCount":0,"image":{"@id":"https:\/\/www.dbi-services.com\/blog\/a-graphical-overview-of-a-repository\/#primaryimage"},"thumbnailUrl":"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2022\/04\/Screenshot-from-2018-10-12-14-32-46.png","articleSection":["Enterprise content management"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.dbi-services.com\/blog\/a-graphical-overview-of-a-repository\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.dbi-services.com\/blog\/a-graphical-overview-of-a-repository\/","url":"https:\/\/www.dbi-services.com\/blog\/a-graphical-overview-of-a-repository\/","name":"A Graphical Overview of a Repository - dbi Blog","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.dbi-services.com\/blog\/a-graphical-overview-of-a-repository\/#primaryimage"},"image":{"@id":"https:\/\/www.dbi-services.com\/blog\/a-graphical-overview-of-a-repository\/#primaryimage"},"thumbnailUrl":"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2022\/04\/Screenshot-from-2018-10-12-14-32-46.png","datePublished":"2018-11-05T14:00:40+00:00","dateModified":"2025-10-24T07:32:20+00:00","author":{"@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/8d8563acfc6e604cce6507f45bac0ea1"},"breadcrumb":{"@id":"https:\/\/www.dbi-services.com\/blog\/a-graphical-overview-of-a-repository\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.dbi-services.com\/blog\/a-graphical-overview-of-a-repository\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.dbi-services.com\/blog\/a-graphical-overview-of-a-repository\/#primaryimage","url":"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2022\/04\/Screenshot-from-2018-10-12-14-32-46.png","contentUrl":"https:\/\/www.dbi-services.com\/blog\/wp-content\/uploads\/sites\/2\/2022\/04\/Screenshot-from-2018-10-12-14-32-46.png","width":493,"height":572},{"@type":"BreadcrumbList","@id":"https:\/\/www.dbi-services.com\/blog\/a-graphical-overview-of-a-repository\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/www.dbi-services.com\/blog\/"},{"@type":"ListItem","position":2,"name":"A Graphical Overview of a Repository"}]},{"@type":"WebSite","@id":"https:\/\/www.dbi-services.com\/blog\/#website","url":"https:\/\/www.dbi-services.com\/blog\/","name":"dbi Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.dbi-services.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/8d8563acfc6e604cce6507f45bac0ea1","name":"Middleware Team","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/ddcae7ba0f9d1a0e7ae707f0e689e4a9c95bb48ec49c8e6d9cc86d43f4121cb6?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/ddcae7ba0f9d1a0e7ae707f0e689e4a9c95bb48ec49c8e6d9cc86d43f4121cb6?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/ddcae7ba0f9d1a0e7ae707f0e689e4a9c95bb48ec49c8e6d9cc86d43f4121cb6?s=96&d=mm&r=g","caption":"Middleware Team"},"url":"https:\/\/www.dbi-services.com\/blog\/author\/middleware-team\/"}]}},"_links":{"self":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/11798","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/users\/40"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/comments?post=11798"}],"version-history":[{"count":1,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/11798\/revisions"}],"predecessor-version":[{"id":41193,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/11798\/revisions\/41193"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/media\/11804"}],"wp:attachment":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/media?parent=11798"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/categories?post=11798"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/tags?post=11798"},{"taxonomy":"type","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/type_dbi?post=11798"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}