Skip to content
Snippets Groups Projects
Commit a19be844 authored by Magali Ruffier's avatar Magali Ruffier
Browse files

this script has now moved to the ensembl-production checkout

parent bc67e6c3
No related branches found
No related tags found
No related merge requests found
#!/usr/bin/env perl
# 1st Feb 2011
# Generate an HTML documentation page from an SQL file.
#
# It needs to have a "javascript like" documentation above each table.
# See the content of the method sql_documentation_format();
####################################################################################
use strict;
use POSIX;
use Getopt::Long;
use Bio::EnsEMBL::DBSQL::DBAdaptor;
###############
### Options ###
###############
my ($sql_file,$html_file,$db_team,$show_colour,$version,$header_flag,$format_headers,$sort_headers,$sort_tables,$intro_file,$help,$help_format);
my ($host,$port,$dbname,$user,$pass,$skip_conn,$db_handle);
usage() if (!scalar(@ARGV));
GetOptions(
'i=s' => \$sql_file,
'o=s' => \$html_file,
'd=s' => \$db_team,
'c=i' => \$show_colour,
'v=i' => \$version,
'show_header=i' => \$header_flag,
'format_headers=i' => \$format_headers,
'sort_headers=i' => \$sort_headers,
'sort_tables=i' => \$sort_tables,
'host=s' => \$host,
'port=i' => \$port,
'dbname=s' => \$dbname,
'user=s' => \$user,
'pass=s' => \$pass,
'skip_connection' => \$skip_conn,
'intro=s' => \$intro_file,
'help!' => \$help,
'help_format' => \$help_format,
);
usage() if ($help);
sql_documentation_format() if ($help_format);
if (!$sql_file) {
print "> Error! Please give a sql file using the option '-i' \n";
usage();
}
if (!$html_file) {
print "> Error! Please give an output file using the option '-o'\n";
usage();
}
$show_colour = 1 if (!defined($show_colour));
$header_flag = 1 if (!defined($header_flag));
$format_headers = 1 if (!defined($format_headers));
$sort_headers = 1 if (!defined($sort_headers));
$sort_tables = 1 if (!defined($sort_tables));
$skip_conn = undef if ($skip_conn == 0);
# Dababase connection (optional)
if (defined($host) && !defined($skip_conn)) {
my $db_adaptor = new Bio::EnsEMBL::DBSQL::DBAdaptor(
-host => $host,
-user => $user,
-pass => $pass,
-port => $port,
-dbname => $dbname
) or die("DATABASE CONNECTION ERROR: Could not get a database adaptor for $dbname on $host:$port\n");
$db_handle = $db_adaptor->dbc->db_handle;
}
################
### Settings ##
################
my $default_colour = '#000'; # Black
my $list_bg = "background-color:#F2F2F2";
my %display_col = ('Show' => 'none', 'Hide' => 'inline');
my $documentation = {};
my $tables_names = {'default' => []};
my @header_names = ('default');
my @colours = ($default_colour);
my %legend;
my $in_doc = 0;
my $in_table = 0;
my $header = 'default';
my $table = '';
my $info = '';
my $nb_by_col = 15;
my $count_sql_col = 0;
my $tag_content = '';
my $tag = '';
my $display = 'Show';
my $parenth_count = 0;
my $header_colour;
my $SQL_LIMIT = 50;
my $img_plus = qq{<img src="/i/16/plus-button.png" style="width:12px;height:12px;vertical-align:middle" alt="show"/>};
my $img_minus = qq{<img src="/i/16/minus-button.png" style="width:12px;height:12px;vertical-align:middle;margin-right:6px" alt="hide"/>};
my $link_text = 'columns';
##############
### Header ###
##############
my $html_header = qq{
<html>
<head>
<meta http-equiv="CONTENT-TYPE" content="text/html; charset=utf-8" />
<title>$db_team Schema Documentation</title>
<script language="Javascript" type="text/javascript">
var img_plus = '$img_plus';
var img_minus = '$img_minus';
var link_text = 'columns';
var span_open = ' <span style="vertical-align:middle">';
var span_close = '</span>';
// Function to show/hide the columns table
function show_hide (param, type) {
// Example tables
if (type === 'example') {
div = document.getElementById('ex_'+param);
alink = document.getElementById('e_'+param);
a_text = 'query results';
}
// Schema tables
else {
div = document.getElementById('div_'+param);
alink = document.getElementById('a_'+param);
a_text = link_text;
}
if (div.style.display=='inline') {
div.style.display='none';
alink.innerHTML=img_plus+span_open+'Show '+a_text+span_close;
}
else {
if (div.style.display=='none') {
div.style.display='inline';
alink.innerHTML=img_minus+span_open+'Hide '+a_text+span_close;
}
}
}
// Function to show/hide all the tables
function show_hide_all () {
expand_flag = document.getElementById('expand');
divs = document.getElementsByTagName('div');
for(var i=0; i < divs.length; i++){
param = divs[i].id.substring(4);
div = document.getElementById('div_'+param);
alink = document.getElementById('a_'+param);
if (div && alink) {
if (expand_flag.value=='0') {
div.style.display='inline';
alink.innerHTML=img_minus+span_open+'Hide '+link_text+span_close;
}
else {
div.style.display='none';
alink.innerHTML=img_plus+span_open+'Show '+link_text+span_close;
}
}
}
if (expand_flag.value=='0') {
expand_flag.value='1';
}
else {
expand_flag.value='0';
}
}
</script>
</head>
<body>
};
##############
### Footer ##
##############
my $html_footer = qq{
</body>
</html>};
#############
## Parser ##
#############
# Create a complex hash "%$documentation" to store all the documentation content
open SQLFILE, "< $sql_file" or die "Can't open $sql_file : $!";
while (<SQLFILE>) {
chomp $_;
next if ($_ eq '');
next if ($_ =~ /^\s*(DROP|PARTITION)/i);
next if ($_ =~ /^\s*(#|--)/); # Escape characters
# Verifications
if ($_ =~ /^\/\*\*/) { $in_doc=1; next; } # start of a table documentation
if ($_ =~ /^\s*create\s+table\s+(if\s+not\s+exists\s+)?`?(\w+)`?/i) { # start to parse the content of the table
if ($table eq $2) {
$in_table=1;
$parenth_count++;
}
else {
print STDERR "The documentation of the table $2 has not be found!\n";
}
next;
}
next if ($in_doc==0 and $in_table==0);
my $doc = remove_char($_);
#================================================#
# Parsing the documentation part of the SQL file #
#================================================#
if ($in_doc==1) {
# Header name
if ($doc =~ /^\@header\s*(.+)$/i and $header_flag == 1) {
$header = $1;
push (@header_names,$header);
$tables_names->{$header} = [];
next;
}
# Table name
elsif ($doc =~ /^\@table\s*(\w+)/i) {
$table = $1;
push(@{$tables_names->{$header}},$table);
$documentation->{$header}{'tables'}{$table} = { 'desc' => '', 'colour' => '', 'column' => [], 'example' => [], 'see' => [], 'info' => [] };
$tag = $tag_content = '';
}
# Description (used for both set, table and info tags)
elsif ($doc =~ /^\@(desc)\s*(.+)$/i) {
fill_documentation ($1,$2);
}
# Colour of the table header (used for both set, table) (optional)
elsif ($doc =~ /^\@(colour)\s*(.+)$/i and $show_colour) {
fill_documentation ($1,$2);
}
# Column
elsif ($doc =~ /^\@(column)\s*(.+)$/i) {
fill_documentation ($1,$2);
}
# Example
elsif ($doc =~ /^\@(example)\s*(.+)$/i) {
fill_documentation ($1,$2);
}
# See other tables
elsif ($doc =~ /^\@(see)\s*(\w+)\s*$/i) {
fill_documentation ($1,$2);
}
# Addtional information block
elsif ($doc =~ /^\@(info)\s*(.+)$/i) {
fill_documentation ();
$info = $2;
next;
}
# End of documentation
elsif ($doc =~ /^\*\//) { # End of the documentation block
fill_documentation (); # Add the last tag content to the documentation hash
$in_doc=0;
next;
}
# Add legend colour description
elsif ($doc =~ /^\@(legend)\s*(#\S+)\s+(.+)$/i) {
$legend{$2} = $3;
}
elsif ($doc =~ /^\s*(.+)$/) { # If a tag content is split in several lines
$tag_content .= " $1";
}
}
#=====================================================#
# Parsing of the SQL table to fetch the columns types #
#=====================================================#
elsif ($in_table==1) {
# END OF TABLE DEFINITION
# Can't do this easily with a simply regex as there are varying valid formats
# The end of the table definition is actually defined by 2nd enclosing bracket
# Regex counting VOODOO!
# This basically puts the regex in a list context
# before inc/dec'ing with it in a scalar context.
$parenth_count +=()= $doc =~ /\(/gi;
$parenth_count -=()= $doc =~ /\)/gi;
if ($parenth_count == 0) { # End of the sql table definition
if (scalar @{$documentation->{$header}{'tables'}{$table}{column}} > $count_sql_col) {
print STDERR "Description of a non existant column in the table $table!\n";
}
$in_table=0;
$count_sql_col = 0;
$table='';
$parenth_count = 0;
}
else {
#---------#
# INDEXES #
#---------#
# Skip the FOREIGN KEY
next if ($doc =~ /^\s*foreign\s+key/i);
if ($doc =~ /^\s*(primary\s+key)\s*\w*\s*\((.+)\)/i or $doc =~ /^\s*(unique)\s*\((.+)\)/i){ # Primary or unique;
add_column_index($1,$2);
next;
}
elsif ($doc =~ /^\s*(unique\s+)?(key|index)\s+([^\s\(]+)\s*\((.+)\)/i) { # Keys and indexes
add_column_index("$1$2",$4,$3);
next;
}
elsif ($doc =~ /^\s*(unique)\s+(\S*)\s*\((.+)\)/i) { # Unique
add_column_index("$1",$3,$2);
next;
}
elsif ($doc =~ /^\s*(key)\s+\((.+)\)/i) { # Keys
add_column_index("$1",$2);
next;
}
#----------------------------------#
# COLUMNS & TYPES & DEFAULT VALUES #
#----------------------------------#
my $col_name = '';
my $col_type = '';
my $col_def = '';
# All the type is contained in the same line (type followed by parenthesis)
if ($doc =~ /^\W*(\w+)\W+(\w+\s?\(.*\))/ ){
$col_name = $1;
$col_type = $2;
if ($doc =~ /default\s+([^,\s]+)\s*.*(,|#).*/i) { $col_def = $1; } # Default value
add_column_type_and_default_value($col_name,$col_type,$col_def);
}
# The type is written in several lines
elsif ($doc =~ /^\W*(\w+)\W+(enum|set)(\s?\(.*)/i){ # The content is split in several lines
$col_name= $1;
$col_type="$2$3<br />";
my $end_type = 0;
while ($end_type != 1){
my $line = <SQLFILE>;
chomp $line;
$line = remove_char($line);
# Regex counting VOODOO again
$parenth_count +=()= $line =~ /\(/gi;
$parenth_count -=()= $line =~ /\)/gi;
if ($line =~ /\)/) { # Close parenthesis
$end_type=1;
$line =~ /^\s*(.+)\)/;
$col_type .= "$1)";
}
else { # Add the content of the line
$line =~ /^\s*(.+)/;
$col_type .= $1.'<br />';
}
if ($line =~ /default\s+([^,\s]+)\s*.*(,|#).*/i) { $col_def = $1; } # Default value
}
add_column_type_and_default_value($col_name,$col_type,$col_def);
}
# All the type is contained in the same line (type without parenthesis)
elsif ($doc =~ /^\s*\W*(\w+)\W+(\w+)/ ){
$col_name = $1;
$col_type = $2;
if ($doc =~ /default\s*([^,\s]+)\s*.*(,|#).*/i) { $col_def = $1;} # Default value
add_column_type_and_default_value($col_name,$col_type,$col_def);
}
}
}
}
close(SQLFILE);
###########
## Core ##
###########
my $html_content;
# Sort the headers names by alphabetic order
if ($sort_headers == 1) {
@header_names = sort(@header_names);
}
# Sort the tables names by alphabetic order
if ($sort_tables == 1) {
while ( my($header_name,$tables) = each (%{$tables_names})) {
@{$tables} = sort(@{$tables});
}
}
# Legend link
if ($show_colour and scalar @colours > 1 and $header_flag != 1) {
$html_content .= qq{A colour legend is available at the <a href="#legend">bottom of the page</a>.\n<br /><br />};
}
#=============================================#
# List of tables by header (i.e. like a menu) #
#=============================================#
$html_content .= display_tables_list();
my $table_count = 1;
my $col_count = 1;
#========================================#
# Display the detailled tables by header #
#========================================#
foreach my $header_name (@header_names) {
my $tables = $tables_names->{$header_name};
my $hcolour = $documentation->{$header_name}{'colour'};
#----------------#
# Header display #
#----------------#
if ($header_flag == 1 and $header_name ne 'default') {
$html_content .= qq{\n<br /><br />
<div style="$list_bg;padding:5px;margin:5px 0px;border-top:2px solid $hcolour">
<h2 style="display:inline;color:#000">$header_name</h2>
</div>\n};
my $header_desc = $documentation->{$header_name}{'desc'};
$html_content .= qq{<p style="width:800px">$header_desc</p>} if (defined($header_desc));
}
#------------------------#
# Additional information #
#------------------------#
if ($header_name eq 'default' and defined($documentation->{$header_name}{'info'})) {
$html_content .= qq{<h2>Additional information about the schema</h2>\n};
}
$html_content .= add_info($documentation->{$header_name}{'info'});
#----------------#
# Tables display #
#----------------#
foreach my $t_name (@{$tables}) {
my $data = $documentation->{$header_name}{'tables'}{$t_name};
my $colour = ($header_flag && $hcolour) ? $hcolour : $data->{colour};
$html_content .= add_table_name($t_name,$colour);
$html_content .= add_description($data);
$html_content .= add_info($data->{info},$data);
$html_content .= add_columns($t_name,$data);
$html_content .= add_examples($t_name,$data);
$html_content .= add_see($data->{see});
}
}
$html_content .= add_legend();
######################
## HTML/output file ##
######################
open HTML, "> $html_file" or die "Can't open $html_file : $!";
print HTML $html_header."\n";
print HTML slurp_intro($intro_file)."\n";
print HTML $html_content."\n";
print HTML $html_footer."\n";
close(HTML);
###############
## Methods ##
###############
# List the table names.
# Group them if the header option "-format_headers" is selected.
# By default there is one group, named "default" and it contains all the tables.
sub display_tables_list {
my $html;
$header_flag = 0 if (scalar @header_names == 1);
if ($header_flag == 1) {
$html .= qq{\n<h3 id="top">List of the tables:</h3>\n};
$html .= qq{<div>\n} if ($format_headers == 1);
}
else {
my $list_width;
if (scalar @header_names == 1) {
my $list_count = scalar @{$tables_names->{'default'}};
my $list_nb_col = ceil($list_count/$nb_by_col);
$list_width = length_names($tables_names->{'default'},$list_nb_col);
}
$html .= qq{
<div>
<div id="top" style="$list_bg;border-radius:5px;margin-bottom:20px;float:left;$list_width">
<div style="padding:5px;background-color:#336;border-top-left-radius:5px;border-top-right-radius:5px">
<img src="/i/16/rev/info.png" style="vertical-align:top" />
<h3 style="display:inline;color:#FFF">List of the tables:</h3>
</div>};
}
my $has_header = 0;
my $nb_col_line = 0;
foreach my $header_name (@header_names) {
my $tables = $tables_names->{$header_name};
my $count = scalar @{$tables};
next if ($count == 0);
# Number of columns needed to display the tables of the group
my $nb_col = ceil($count/$nb_by_col);
my $nbc = $nb_col;
my $table_count = 0;
my $col_count = 1;
if ($nb_col>3) {
while ($nb_col>3) {
$nb_by_col += 5;
$nb_col = ceil($count/$nb_by_col);
}
$nb_col = 3;
}
# Header #
if ($header_flag == 1) {
if ($header_name ne 'default') {
if ($nb_col_line+$nbc > 4 and $format_headers == 1) {
$html .= qq{ <div style="clear:both" />\n</div>\n\n<div>};
$nb_col_line = 0;
}
$html .= display_header($header_name,$nbc);
$nb_col_line += $nbc;
$has_header = 1;
}
# List of tables #
$html .= qq{\n <div style="float:left">} if ($count > $nb_by_col);
$html .= qq{\n <ul style="padding:0px 4px 0px 22px;margin-bottom:2px">\n};
my $t_count = 0;
foreach my $t_name (@{$tables}) {
my $t_colour;
if ($has_header == 0 && $show_colour) {
$t_colour = $documentation->{$header_name}{'tables'}{$t_name}{'colour'};
}
$html .= add_table_name_to_list($t_name,$t_colour);
$t_count++;
if ($t_count>=$nb_by_col) {
$html .= qq{\n </ul>\n </div>};
$html .= qq{\n <div style="float:left">};
$html .= qq{\n <ul style="padding:0px 4px 0px 22px;margin-bottom:2px">\n};
$t_count = 0;
}
}
$html .= qq{\n </ul>};
$html .= qq{\n </div>} if ($count > $nb_by_col);
$html .= qq{\n </div>\n} if ($format_headers == 1);
}
else {
$html .= qq{\n <table style="padding:0px 2px"><tr><td>\n <ul style="padding-left:20px">\n};
# List of tables #
foreach my $t_name (@{$tables}) {
if ($table_count == $nb_by_col and $col_count<$nb_col and $nb_col>1){
$html .= qq{ </ul>\n </td><td>\n <ul style="padding-left:20px">\n};
$table_count = 0;
}
my $t_colour;
if ($has_header == 0 && $show_colour) {
$t_colour = $documentation->{$header_name}{'tables'}{$t_name}{'colour'};
}
$html .= add_table_name_to_list($t_name,$t_colour);
$table_count ++;
}
$html .= qq{ </ul>\n </td></tr></table>\n};
}
}
my $input_margin;
if ($header_flag == 1 and $format_headers == 1){
$html .= qq{\n <div style="clear:both" />\n</div>};
} else {
$input_margin = qq{ style="margin-left:10px;margin-bottom:5px"};
}
$html .= qq{
<input type="button" onclick="show_hide_all()" class="fbutton" value="Show/hide all"$input_margin/>
<input type="hidden" id="expand" value="0" />
};
$html .= qq{\n </div>\n <div style="clear:both" />\n</div>} if ($header_flag!=1 and $format_headers == 1);
return $html;
}
# If the option "-show_header" is selected, the tables will be displayed by group ("Header") in the HTML page.
# This method generates the HTML code to display the group names & descriptions.
sub display_header {
my $header_name = shift;
my $nb_col = shift;
my $html;
if ($format_headers == 1) {
my $width = length_names($tables_names->{$header_name},$nb_col);
$html .= qq{\n <div style="$list_bg;border-radius:5px;margin-bottom:15px;float:left;margin-right:20px$width">
<div style="padding:2px 5px;background-color:#336;border-top-left-radius:5px;border-top-right-radius:5px">};
my $text_colour = '#FFF';
if ($show_colour && $header_colour) {
my $hcolour = $documentation->{$header_name}{colour};
$hcolour = $default_colour if (!defined($hcolour));
$html .= qq{
<div style="background-color:$hcolour;border:1px solid #FFF;padding:0px 8px;display:inline;vertical-align:middle"></div>
<h2 style="margin-left:8px;display:inline;color:$text_colour;vertical-align:middle">$header_name</h2>\n};
}
else {
$html .= qq{<h2 style="display:inline;color:$text_colour">$header_name</h2>\n};
}
$html .= qq{ </div>};
}
else {
$html .= qq{ <h2 >$header_name</h2>};
}
return $html;
}
# Method to pick up the documentation information contained in the SQL file.
# If the line starts by a @<tag>, the previous tag content is added to the documentation hash.
# This method allows to describe the content of a tag in several lines.
sub fill_documentation {
my $t1 = shift;
my $t2 = shift;
if ($tag ne '') {
# Description tag (info, table or header)
if ($tag eq 'desc') {
# Additional description
if ($info ne '') {
$tag_content = $info.'@info@'.$tag_content;
# Table: additional information
if ($table ne '') {
push(@{$documentation->{$header}{'tables'}{$table}{'info'}},$tag_content);
}
# Header: additional information
else {
if (!$documentation->{$header}{'info'}) {
$documentation->{$header}{'info'} = [];
}
push(@{$documentation->{$header}{'info'}},$tag_content);
}
$info = '';
}
# Header description
elsif(!$documentation->{$header}{'tables'}) {
$documentation->{$header}{'desc'} = $tag_content;
}
# Table description
else {
$documentation->{$header}{'tables'}{$table}{$tag} = $tag_content;
}
}
elsif ($tag eq 'colour') {
if(!$documentation->{$header}{'tables'}) {
$documentation->{$header}{'colour'} = $tag_content;
$header_colour = 1;
}
elsif ($table ne '') {
$documentation->{$header}{'tables'}{$table}{$tag} = $tag_content;
if (! grep {$tag_content eq $_} @colours) {
push (@colours,$tag_content);
}
}
}
elsif ($tag eq 'column') {
$tag_content =~ /(\w+)[\s\t]+(.*)/;
my $column = { 'name' => $1,
'type' => '',
'default' => '',
'index' => '',
'desc' => $2
};
if ($2 eq '') {
print STDERR "COLUMN: The description content of the column '$1' is missing in the table $table!\n";
}
push(@{$documentation->{$header}{'tables'}{$table}{$tag}},$column);
}
else{
push(@{$documentation->{$header}{'tables'}{$table}{$tag}},$tag_content);
}
}
# New tag initialized
if ($t1) {
$tag = $t1;
$tag_content = $t2;
}
else {
$tag = $tag_content = '';
}
}
# Method generating the HTML code to display the table name into the top menu.
sub add_table_name_to_list {
my $t_name = shift;
my $t_colour = shift;
my $c = $t_colour;
if (defined($t_colour)) {
$t_colour = ($t_colour ne '') ? qq{;background-color:$t_colour} : '';
$t_colour = qq{<div style="padding:0px;margin-left:0px$t_colour;display:inline">&nbsp;</div> };
}
my $html = qq{ <li>$t_colour<a href="#$t_name"><b>$t_name</b></a></li>\n};
return $html;
}
# Method generating the HTML code to display the title/header of the table description block
sub add_table_name {
my $t_name = shift;
my $colour = shift || '#000';
my $c_box = '';
if ($show_colour) {
$c_box = qq{ <div style="float:left;padding:0px;height:20px;width:10px;background-color:$colour;margin-right:10px"></div>};
}
my $html = qq{
<div id="$t_name" style="width:820px;height:20px;border: 2px groove #CCC;background-color:#FAFAFF;padding:2px;margin-top:35px;margin-bottom:2px">
$c_box
<div style="float:left;text-align:left;font-size:11pt;font-weight:bold">$t_name</div>
<div style="float:right;text-align:right;padding-right:1px">
<a id="a_$t_name" style="cursor:pointer;text-decoration:none" onclick="show_hide('$t_name')">
$img_plus
<span style="vertical-align:middle">Show $link_text</span>
</a>
<b> | </b> <a href="#top">[Back to top]</a>
</div>
</div>\n};
return $html;
}
# Method generating the HTML code to display the description content
sub add_description {
my $data = shift;
# Search if there are some @link tags in the description text.
my $desc = add_internal_link($data->{desc},$data);
return qq{ <p style="padding:5px 0px;margin-bottom:0px;width:800px">$desc</p>\n};
}
# Method generating the HTML code to display additional information contained in the tags @info
sub add_info {
my $infos = shift;
my $data = shift;
my $html = '';
foreach my $inf (@{$infos}) {
my ($title,$content) = split('@info@', $inf);
$content = add_internal_link($content,$data) if (defined($data));
$html .= qq{
<table>
<tr class="bg3"><th>$title</th></tr>
<tr class="bg1"><td>$content</td></tr>
</table>\n};
}
return $html;
}
# Method generating the HTML code of the table listing the columns of the given SQL table.
sub add_columns {
my $table = shift;
my $data = shift;
my $cols = $data->{column};
my $display_style = $display_col{$display};
my $html = qq{\n <div id="div_$table" style="display:$display_style">
<table style="border:1px solid #667aa6;padding:0px;min-width:1000px;max-width:1200px">
<tr class="center" style="color:#FFFFFF;background-color:#667aa6"><th style="color:#FFF;padding:2px">Column</th><th style="color:#FFF;padding:2px">Type</th><th style="color:#FFF;padding:2px;min-width:80px">Default value</th><th style="color:#FFF;padding:2px;min-width:500px">Description</th><th style="color:#FFF;padding:2px;min-width:100px">Index</th></tr>\n};
my $bg = 1;
foreach my $col (@$cols) {
my $name = $col->{name};
my $type = $col->{type};
my $default = $col->{default};
my $desc = $col->{desc};
my $index = $col->{index};
# links
$desc = add_internal_link($desc,$data);
$html .= qq{ <tr class="bg$bg"><td><b>$name</b></td><td>$type</td><td>$default</td><td>$desc</td><td>$index</td></tr>\n};
if ($bg==1) { $bg=2; }
else { $bg=1; }
}
$html .= qq { </table>\n </div>\n};
return $html;
}
# Method generating the HTML code to display the content of the tags @example (description + SQL query + Table of SQL query results)
sub add_examples {
my $table = shift;
my $data = shift;
my $examples = $data->{example};
my $html;
my $nb = (scalar(@$examples) > 1) ? 1 : '';
foreach my $ex (@$examples) {
my @lines = split("\n",$ex);
my $nb_display = ($nb ne '') ? " $nb" : $nb;
$html .= qq{<div style="margin: 10px 5px"><p style="font-weight:bold">Example$nb_display:</p>};
my $has_desc = 0;
my $sql;
# Parse the example lines
foreach my $line (@lines) {
chomp($line);
# Pick up the SQl query if it exists
if ($line =~ /(.*)\s*\@sql\s*(.+)/) {
$html .= ($has_desc == 1) ? $1 : qq{<p style="padding-left:10px">$1};
$sql = $2;
} elsif (!defined($sql)){
$html .= qq{<p style="padding-left:10px">} if ($has_desc == 0);
$html .= $line;
$has_desc = 1;
}
else {
$sql .= $line;
}
}
$html .= qq{</p>};
# Search if there are some @link tags in the example description.
$html = add_internal_link($html,$data);
# Add a table of examples
if (defined($sql)) {
my $show_hide = '';
my $sql_table = '';
if (!defined($skip_conn) && defined($host)) {
$show_hide .= qq{<a id="e_$table$nb" style="cursor:pointer;text-decoration:none" onclick="show_hide('$table$nb','example')">$img_plus<span style="vertical-align:middle"> Show query results</span></a>};
$sql_table = get_example_table($sql,$table,$nb);
}
if (defined($sql)) {
foreach my $word (qw(SELECT DISTINCT CONCAT FROM LEFT JOIN USING WHERE LIMIT DESC ORDER GROUP)) {
$sql =~ s/$word /$word /i;
}
}
$html .= qq{<pre style="display:inline;border:1px solid #555;padding:2px;margin-right:15px;margin-left:10px">$sql</pre> $show_hide\n$sql_table};
}
$html .= qq{</div>};
$nb ++;
}
return $html;
}
# Method generating the HTML code to display the content of the tags @see
sub add_see {
my $sees = shift;
my $html = '';
if (scalar @$sees) {
$html .= qq{ <p style="margin-top:10px"><b>See also:</b></p>\n <ul>\n};
foreach my $see (@$sees) {
$html .= qq{ <li><a href="#$see">$see</a></li>\n};
}
$html .= qq{ </ul>\n\n};
}
return $html;
}
# Method searching the tag @link into the string given as argument and replace it by an internal HTML link
sub add_internal_link {
my $desc = shift;
my $data = shift;
while ($desc =~ /\@link\s?(\w+)/) {
my $link = $1;
if ((!grep {$link eq $_} @{$data->{see}}) and defined($link)) {
push @{$data->{see}}, $link;
}
my $table_to_link = qq{<a href="#$link">$link</a>};
$desc =~ s/\@link\s?\w+/$table_to_link/;
}
return $desc;
}
# Method parsing the index information from the SQL table description in order to display it in the
# HTML table listing the columns of the corresponding SQL table.
sub add_column_index {
my $idx_type = shift;
my $idx_col = shift;
my $idx_name = shift;
my $index = $idx_type;
if (!defined($idx_name)) {
$idx_name = $idx_col;
}
if ($idx_type !~ /primary/i) {
$index .= ": $idx_name";
}
my @idx_cols = split(',',$idx_col); # The index can involve several columns
my %is_found = ();
foreach my $i_col (@idx_cols) {
$i_col =~ s/\s//g; # Remove white spaces
# In case of index using a number characters for a column
if ($i_col =~ /(.+)\(\d+\)/) {
$i_col = $1;
}
$is_found{$i_col} = 0;
foreach my $col (@{$documentation->{$header}{tables}{$table}{column}}) {
if ($col->{name} eq $i_col) {
if ($col->{index} ne '') {
$col->{index} .= '<br />';
}
$col->{index} .= lc($index);
$is_found{$i_col} = 1;
last;
}
}
}
# Description missing
while (my ($k,$v) = each(%is_found)) {
if ($v==0) {
print STDERR "INDEX: The description of the column '$k' is missing in the table $table!\n";
}
}
}
# Method parsing the column type and default value from the SQL table description, in order to display them in the
# HTML table listing the columns of the corresponding SQL table.
sub add_column_type_and_default_value {
my $c_name = shift;
my $c_type = shift;
my $c_default = shift;
$count_sql_col ++;
my $is_found = 0;
foreach my $col (@{$documentation->{$header}{'tables'}{$table}{column}}) {
if ($col->{name} eq $c_name) {
$col->{type} = $c_type;
$col->{default} = $c_default if ($c_default ne '');
$is_found = 1;
last;
}
}
# Description missing
if ($is_found==0) {
print STDERR "COLUMN: The description of the column '$c_name' is missing in the table $table!\n";
}
}
# Method to query the database with the SQL query example, get the result and display it
# in an HTML table.
sub get_example_table {
my $sql = shift;
my $table = shift;
my $nb = shift;
my $html;
$sql =~ /select\s+(.+)\s+from/i;
my $cols = $1;
my @tcols;
foreach my $col (split(',',$cols)) {
# Columns selection like the expressions "table.*" or "*"
my $table_name;
$table_name = $table if ($cols eq '*');
$table_name = $1 if ($col =~ /(\S+)\.\*/ and !defined($table_name));
if (defined($table_name)) {
my $table_cols = $db_handle->selectall_arrayref(qq{SHOW COLUMNS FROM $table_name});
foreach my $col (@$table_cols) {
push(@tcols,$col->[0]);
}
next;
}
# Check for alias
$col = $1 if ($col =~ /\s+as\s+(\S+)$/i);
$col =~ s/ //g;
push(@tcols,$col);
}
my $results = $db_handle->selectall_arrayref($sql);
if (scalar(@$results)) {
$html .= qq{
<div id="ex_$table$nb" style="display:none;">
<table class="ss" style="width:50%;margin-top:20px;margin-left:10px;border-spacing:2px">\n <tr><th>};
$html .= join("</th><th>",@tcols);
$html .= qq{</th></tr>};
my $bg = '';
my $count = 0;
foreach my $result (@$results) {
last if ($count >= $SQL_LIMIT);
$html .= qq{ <tr$bg><td>};
$html .= join("</td><td>", @$result);
$html .= qq{</td></tr>};
$bg = ($bg eq '') ? ' class="bg2"' : '';
$count ++;
}
$html .= qq{ </table>\n </div>};
} else {
my $msg = qq{ERROR: the SQL query displayed above returned no results!};
$html .= qq{<div style="padding:5px;margin:10px;width:500px;font-weight:bold;border:2px solid red;color:red">$msg</div>};
print STDERR qq{SQL: $sql\n$msg\n};
}
return $html;
}
# Method generating a "Colour legend" paragraph, based on the header colours.
sub add_legend {
my $html = '';
my $default = 'Other tables';
return $html if (scalar @colours == 1 or $header_colour);
$html .= qq{<br />\n<hr />\n<h3 id="legend">Colour legend</h3>\n<table>};
foreach my $c (@colours) {
my $desc = '';
if ($c eq $default_colour && !$legend{$default_colour}) {
$desc = $default;
}
else {
$desc = $legend{$c};
}
$html .= qq{ <tr><td style="width:25px;height:15px;background-color:$c"></td><td>$desc</td></tr>\n};
}
$html .= qq{</table>};
return $html;
}
# Removed the character(s) ` from the read line.
sub remove_char {
my $text = shift;
$text =~ s/`//g;
return $text;
}
# Count largest length in the table names
sub length_names {
my $list = shift;
my $nb_col = shift;
my $max = 0;
foreach my $name (@$list) {
my $length = length($name);
$max = $length if ($length>$max);
}
return '' if ($nb_col>1 || $max>25);
return ";width:200px" if ($max<=25);
}
# Insert the introduction text of the web page
sub slurp_intro {
my ($intro_file) = @_;
if (!defined $intro_file) {
return qq{<h1>Ensembl $db_team Schema Documentation</h1>\n<h2>Introduction</h2>\n<p><i>please, insert your introduction here</i><p><br />};
}
local $/=undef;
open my $fh, "< $intro_file" or die "Can't open $intro_file: $!";
my $intro_html = <$fh>;
close $fh;
$intro_html =~ s/####DB_VERSION####/$version/g if (defined($version));
return $intro_html;
}
##################
## Help methods ##
##################
sub sql_documentation_format {
print q{
#--------------------------#
# Example of documentation #
#--------------------------#
/**
@table variation
@desc This is the schema's generic representation of a variation.
@colour #FF0000
@column variation_id Primary key, internal identifier.
@column source_id Foreign key references to the \@link source table.
@column name Name of the variation. e.g. "rs1333049".
@column validation_status Variant discovery method and validation from dbSNP.
@column ancestral_allele Taken from dbSNP to show ancestral allele for the variation.
@column flipped This is set to 1 if the variant is flipped.
@column class_so_id Class of the variation, based on the Sequence Ontology.
@example Example of SQL query for to retrieve data from this table:
@sql SELECT * FROM variation WHERE source_id=1 LIMIT 10;
@see variation_synonym
@see flanking_sequence
@see failed_variation
@see variation_feature
@see variation_group_variation
@see allele
@see allele_group_allele
@see individual_genotype_multiple_bp
*/
create table variation (
variation_id int(10) unsigned not null auto_increment, # PK
source_id int(10) unsigned not null,
name varchar(255),
validation_status SET('cluster','freq','submitter','doublehit','hapmap','1000Genome','failed','precious'),
ancestral_allele text,
flipped tinyint(1) unsigned NULL DEFAULT NULL,
class_so_id ENUM('SO:0001483','SO:1000002','SO:0000667') DEFAULT 'SO:0001059', # default to sequence_alteration
primary key( variation_id ),
unique ( name ),
key source_idx (source_id)
);
/**
@legend #FF0000 Table storing variation data
*/
#========================================================================================================================#
#------------------#
# Tags description #
#------------------#
/** and */ : begin and end of the document block
@header : tag to create a group of tables
@table : name of the sql table
@desc : description of the role/content of the table, set or info tags
@colour : tag to colour the header of the table (e.g. if the tables are coloured in the graphic SQL schema and you want to reproduce it in the HTML version)
@column : column_name [tab(s)] Column description. Note: 1 ligne = 1 column
@see : tables names linked to the described table
@link : Internal link to an other table description. The format is ... @link table_name ...
@info : tag to describe additional information about a table or a set of tables
@legend : tag to fill the colour legend table at the end of the HTML page
@example : tag to add some examples, like examples of SQL queries
@sql : tag inside the @example tag, used to delimit a SQL query
};
exit(0);
}
sub usage {
print q{
Usage: perl sql2html.pl [OPTION]
Convert the SQL documentation into an HTML document.
Options:
-help Print this message
-help_format Print the description of the documentation format in the SQL files
An input file must be specified. This file must be a SQL file, with the "Java-doc like documentation".
For more information, please visit the following page:
http://www.ebi.ac.uk/seqdb/confluence/display/EV/SQL+documentation
-i A SQL file name (Required)
-o An HTML output file name (Required)
-d The name of the database (e.g Core, Variation, Functional Genomics, ...)
-c A flag to display the colours associated with the tables (1) or not (0). By default, the value is set to 1.
-v Version of the schema. Replace the string ####DB_VERSION#### by the value of the parameter "-v", in the introduction text. (Optional)
-intro A html/text file to include in the Introduction section (Optional. If not provided a default text will be inserted)
-show_header A flag to display headers for a group of tables (1) or not (0). By default, the value is set to 1.
-format_headers A flag to display formatted headers for a group of tables (1) or not (0) in the top menu list. By default, the value is set to 1.
-sort_headers A flag to sort (1) or not (0) the headers by alphabetic order. By default, the value is set to 1.
-sort_tables A flag to sort (1) or not (0) the tables by alphabetic order. By default, the value is set to 1.
Other optional options - if you want to add some SQL query results as examples
-host Host name of the MySQL server
-port Port of the MySQL server
-dbname Database name
-user MySQL user name
-pass MySQL password (not always required)
-skip_connection Avoid to run the MySQL queries contained in the "@example" tags.
} . "\n";
exit(0);
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment