reporter fixups ... bar and pie charts are working
authormiker <miker@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Sat, 19 Nov 2005 04:18:33 +0000 (04:18 +0000)
committermiker <miker@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Sat, 19 Nov 2005 04:18:33 +0000 (04:18 +0000)
git-svn-id: svn://svn.open-ils.org/ILS/trunk@2088 dcc99617-32d9-48b4-a31d-7c20da2025e4

Open-ILS/src/perlmods/OpenILS/WWW/Reporter.pm
Open-ILS/src/reporter/clark-kent.pl
Open-ILS/src/reporter/tables.example.xml
Open-ILS/src/reporter/templates/html/html
Open-ILS/src/reporter/templates/inputs
Open-ILS/src/reporter/templates/logic_header.ttk
Open-ILS/src/reporter/templates/stage2.ttk
Open-ILS/src/reporter/templates/stage3.ttk
Open-ILS/src/reporter/templates/widgets/string-choose.dropdown
Open-ILS/src/reporter/templates/widgets/string-choose.multiselect
Open-ILS/src/sql/Pg/002.schema.config.sql

index d9d4ecc..2709ea1 100644 (file)
@@ -76,6 +76,7 @@ sub handler {
 
 
        print "Content-type: text/html; charset=utf-8\n\n";
+       #print "Content-type: text/html\n\n";
 
        _process_template(
                        apache          => $apache,
index 9a5a50b..2681ab1 100755 (executable)
@@ -15,6 +15,12 @@ use Spreadsheet::WriteExcel;
 use OpenSRF::EX qw/:try/;
 use OpenSRF::Utils qw/:daemon/;
 use OpenSRF::Utils::Logger qw/:level/;
+use POSIX;
+use GD::Graph::pie;
+use GD::Graph::bars3d;
+use GD::Graph::lines;
+
+use open ':utf8';
 
 my $current_time = DateTime->from_epoch( epoch => time() )->strftime('%FT%T%z');
 
@@ -45,7 +51,7 @@ daemonize("Clark Kent, waiting for trouble") if ($daemon);
 
 DAEMON:
 
-$dbh = DBI->connect($dsn,$db_user,$db_pw);
+$dbh = DBI->connect($dsn,$db_user,$db_pw, {pg_enable_utf8 => 1, RaiseError => 1});
 
 # Move new reports into the run queue
 $dbh->do(<<'SQL', {}, $current_time);
@@ -73,6 +79,13 @@ SELECT       count(*)
 SQL
 
 if ($count <= $running) {
+       if ($daemon) {
+               $dbh->disconnect;
+               sleep 1;
+               POSIX::waitpid( -1, POSIX::WNOHANG );
+               sleep 60;
+               goto DAEMON;
+       }
        print "Already running maximum ($running) concurrent reports\n";
        exit 1;
 }
@@ -120,10 +133,9 @@ for my $r ( @reports ) {
        my $p = JSON->JSON2perl( $r->{stage3}->{params} );
        daemonize("Clark Kent reporting: $p->{reportname}");
 
-       $dbh = DBI->connect($dsn,$db_user,$db_pw);
+       $dbh = DBI->connect($dsn,$db_user,$db_pw, {pg_enable_utf8 => 1, RaiseError => 1});
 
        try {
-
                $dbh->do(<<'            SQL',{}, $r->{sql}->{'select'}, $$, $r->{id});
                        UPDATE  reporter.output
                          SET   state = 'running',
@@ -205,6 +217,8 @@ for my $r ( @reports ) {
 }
 
 if ($daemon) {
+       sleep 1;
+       POSIX::waitpid( -1, POSIX::WNOHANG );
        sleep 60;
        goto DAEMON;
 }
@@ -229,7 +243,11 @@ sub build_excel {
        my $p = JSON->JSON2perl( $r->{stage3}->{params} );
 
        my $xls = Spreadsheet::WriteExcel->new($file);
-       my $sheet = $xls->add_worksheet($p->{reportname});
+
+       my $sheetname = substr($p->{reportname},1,31);
+       $sheetname =~ s/\W/_/gos;
+       
+       my $sheet = $xls->add_worksheet($sheetname);
 
        $sheet->write_row('A1', $r->{sql}->{columns});
 
@@ -238,7 +256,219 @@ sub build_excel {
        $xls->close;
 }
 
-sub build_html {}
+sub build_html {
+       my $file = shift;
+       my $r = shift;
+       my $p = JSON->JSON2perl( $r->{stage3}->{params} );
+
+       my $index = new FileHandle (">$file");
+       my $raw = new FileHandle (">$file.raw.html");
+       
+       # index header
+       print $index <<"        HEADER";
+<html>
+       <head>
+               <title>$$p{reportname}</title>
+               <style>
+                       table { border-collapse: collapse; }
+                       th { background-color: lightgray; }
+                       td,th { border: solid black 1px; }
+                       * { font-family: sans-serif; font-size: 10px; }
+               </style>
+       </head>
+       <body>
+               <h2><u>$$p{reportname}</u></h2>
+       HEADER
+
+       
+       # add a link to the raw output html
+       print $index "<a href='report-data.html.raw.html'>Raw output data</a><br/><br/><br/><br/>";
+
+       # create the raw output html file
+       print $raw "<html><head><title>$$p{reportname}</title>";
+
+       print $raw <<'  CSS';
+               <style>
+                       table { border-collapse: collapse; }
+                       th { background-color: lightgray; }
+                       td,th { border: solid black 1px; }
+                       * { font-family: sans-serif; font-size: 10px; }
+               </style>
+       CSS
+
+       print $raw "</head><body><table>";
+       print $raw "<tr><th>".join('</th><th>',@{$r->{sql}->{columns}}).'</th></tr>';
+
+       print $raw "<tr><td>".join('</td><td>',@$_).'</td></tr>' for (@{$r->{data}});
+
+       print $raw '</table></body></html>';
+       
+       $raw->close;
+
+       # get the graph types
+       my @graphs;
+       if (ref $$p{html_graph_type}) {
+               @graphs = @{ $$p{html_graph_type} };
+       } else {
+               @graphs = ( $$p{html_graph_type} );
+       }
+
+       # Time for a pie chart
+       if (grep {$_ eq 'pie'} @graphs) {
+               my $pics = draw_pie($r, $p, $file);
+               for my $pic (@$pics) {
+                       print $index "<img src='report-data.html.$pic->{file}' alt='$pic->{name}'/><br/><br/><br/><br/>";
+               }
+       }
+
+       # Time for a bar chart
+       if (grep {$_ eq 'bar'} @graphs) {
+               my $pics = draw_bars($r, $p, $file);
+               for my $pic (@$pics) {
+                       print $index "<img src='report-data.html.$pic->{file}' alt='$pic->{name}'/><br/><br/><br/><br/>";
+               }
+       }
+
+
+       # and that's it!
+       print $index '</body></html>';
+       
+       $index->close;
+}
+
+sub draw_pie {
+       my $r = shift;
+       my $p = shift;
+       my $file = shift;
+       my $data = $r->{data};
+       my $settings = $r->{sql};
+
+       my @groups = (map { ($_ - 1) } @{ $settings->{groupby} });
+       
+       my @values = (0 .. (scalar(@{$settings->{columns}}) - 1));
+       delete @values[@groups];
+       
+       my @pics;
+       for my $vcol (@values) {
+               next unless (defined $vcol);
+
+               my @pic_data;
+               for my $row (@$data) {
+                       next if ($$row[$vcol] == 0);
+                       push @{$pic_data[0]}, join(' -- ', @$row[@groups]);
+                       push @{$pic_data[1]}, $$row[$vcol];
+               }
+
+               my $pic = new GD::Graph::pie;
+
+               $pic->set(
+                       label           => $p->{reportname}." -- ".$settings->{columns}->[$vcol],
+                       start_angle     => 180,
+                       legend_placement=> 'R'
+               );
+
+               my $format = $pic->export_format;
+
+               open(IMG, ">$file.pie.$vcol.$format");
+               binmode IMG;
+
+               my $forgetit = 0;
+               try {
+                       $pic->plot(\@pic_data) or die $pic->error;
+                       print IMG $pic->gd->$format;
+               } otherwise {
+                       my $e = shift;
+                       warn "Couldn't draw $file.pie.$vcol.$format : $e";
+                       $forgetit = 1;
+               };
+
+               close IMG;
+
+               next if ($forgetit);
+
+               push @pics,
+                       { file => "pie.$vcol.$format",
+                         name => $p->{reportname}." -- ".$settings->{columns}->[$vcol].' (Pie)',
+                       };
+
+       }
+       
+       return \@pics;
+}
+
+sub draw_bars {
+       my $r = shift;
+       my $p = shift;
+       my $file = shift;
+       my $data = $r->{data};
+       my $settings = $r->{sql};
+
+       my @groups = (map { ($_ - 1) } @{ $settings->{groupby} });
+       
+       my @values = (0 .. (scalar(@{$settings->{columns}}) - 1));
+       delete @values[@groups];
+       
+       my @pic_data;
+       for my $row (@$data) {
+               push @{$pic_data[0]}, join(' -- ', @$row[@groups]);
+       }
+
+       my @leg;
+       my $set = 1;
+
+       my $max_y = 0;
+       for my $vcol (@values) {
+               next unless (defined $vcol);
+
+               push @leg, $settings->{columns}->[$vcol];
+
+               for my $row (@$data) {
+                       my $val = $$row[$vcol] ? $$row[$vcol] : 0;
+                       push @{$pic_data[$set]}, $val;
+                       $max_y = $val if ($val > $max_y);
+               }
+
+               $set++;
+       }
+
+       my $pic = new GD::Graph::bars3d (100 + 10 * scalar(@$data), 500);
+
+       $pic->set(
+               title                   => $p->{reportname},
+               x_labels_vertical       => 1,
+               shading                 => 1,
+               bar_depth               => 5,
+               bar_spacing             => 2,
+               y_max_value             => $max_y,
+               legend_placement        => 'BL',
+               boxclr                  => 'lgray',
+       );
+       $pic->set_legend(@leg);
+
+       my $format = $pic->export_format;
+
+       open(IMG, ">$file.bar.$format");
+       binmode IMG;
+
+       my $forgetit = 0;
+       try {
+               $pic->plot(\@pic_data) or die $pic->error;
+               print IMG $pic->gd->$format;
+       } otherwise {
+               my $e = shift;
+               warn "Couldn't draw $file.bar.$format : $e";
+               $forgetit = 1;
+       };
+
+       close IMG;
+
+       next if ($forgetit);
+
+       return [{ file => "bar.$format",
+                 name => $p->{reportname}.' (Bar)',
+               }];
+
+}
 
 sub table_by_id {
        my $id = shift;
@@ -295,7 +525,13 @@ sub generate_query {
                '(SELECT ' . join(',', @dim_select) .
                '  FROM ' . join(',', @dim_from) . ') AS dims';
        
-       my @output_order = map { { (split ':')[1] => (split ':')[2] } } @{ $$p{output_order} };
+       my @opord;
+       if (ref $$p{output_order}) {
+               @opord = @{ $$p{output_order} };
+       } else {
+               @opord = ( $$p{output_order} );
+       }
+       my @output_order = map { { (split ':')[1] => (split ':')[2] } } @opord;
        
        my $col = 1;
        my @groupby;
@@ -388,14 +624,15 @@ sub generate_query {
        my $t = table_by_id($core)->findvalue('tablename');
        my $from = " FROM $t AS \"$core\" RIGHT JOIN $d_select ON (". join(' AND ', @join).")";
        my $select =
-               "SELECT ".join(',', @output).
-                 $from.
-                 ' WHERE '.join(' AND ', @where).
-                 ' GROUP BY '.join(',',@groupby);
+               "SELECT ".join(',', @output). $from;
+
+       $select .= ' WHERE '.join(' AND ', @where) if (@where);
+       $select .= ' GROUP BY '.join(',',@groupby) if (@groupby);
 
        $r->{sql}->{'select'}   = $select;
        $r->{sql}->{'bind'}     = \@bind;
        $r->{sql}->{columns}    = \@columns;
+       $r->{sql}->{groupby}    = \@groupby;
        
 }
 
index a84287c..86bb884 100644 (file)
                          key="id"
                          type="has_a"/>
                        <link
+                         field="item_lang"
+                         table="marc_lang_map"
+                         id="copy_language"
+                         key="code"
+                         type="has_a"/>
+                       <link
                          field="circ_lib"
                          table="org_unit"
                          id="copy_circ_lib"
                </fields>
        </table>
 
+       <table id="marc_lang_map" partition="false" fact-table="false">
+               <label>MARC Language Codes</label>
+               <description>Table mapping MARC three character codes to language names</description>
+               <tablename>config.language_map</tablename>
+
+               <fields>
+                       <field
+                         name="code"
+                         primary='true'
+                         datatype="text">
+                               <label>Code</label>
+                               <description>MARC Language Code</description>
+                       </field>
+                       <field
+                         name="value"
+                         datatype="text">
+                               <label>Language Name</label>
+                               <description>Name associated with the MARC code</description>
+                       </field>
+               </fields>
+       </table>
+
        <table id="silly.fact" partition="true" fact-table="false">
                <label>Silly Fact table</label>
                <description>Base table for creating reports on silly words</description>
index 20f0245..c45a0c6 100644 (file)
@@ -1,6 +1,6 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd>
+[% USE x = Unicode %]
 
 <html>
-[% "\n" _ content _ "\n" %]
+[% content; %]
 </html>
index 5f86ad2..77e4365 100644 (file)
@@ -11,6 +11,10 @@ BLOCK form;
                'id="' _ id _ '" ';
        END;
 
+       IF onsubmit;
+               'onsubmit="' _ onsubmit _ '" ';
+       END;
+
        IF action;
                'action="' _ action _ '" ';
        END;
index 185be26..b7cf103 100644 (file)
@@ -33,7 +33,7 @@ dsn = "dbi:" _ dbdriver _ ":dbname=" _ dbname _';host=' _ dbhost;
 
 logme([dsn,d_u,d_p]);
 
-DBI.connect(dsn,d_u,d_p);
+DBI.connect(dsn,d_u,d_p,pg_enable_utf8=1);
 
 stage2_insert = 'INSERT INTO reporter.stage2 (stage1, params, owner, pub)' _
                                                ' VALUES ( ?,?,?,? )';
index cb3d107..ab56d44 100644 (file)
@@ -5,12 +5,12 @@ WRAPPER html/html;
        PROCESS inputs;
        PROCESS class_manip;
        PROCESS widget_manip;
-       PROCESS logic_header.ttk;
        INCLUDE logout.ttk;
    END;
 
    WRAPPER html/body;
 
+       PROCESS logic_header.ttk;
        PROCESS select_sorter.js;
 
        templates = DBI.tie('reporter.stage2', 'id')
@@ -238,6 +238,26 @@ BLOCK run_stage2;
        %]
                <script language="javascript">
                        var outputs = {};
+                       var filters = {};
+                       function send_it (f) {
+                               var bad_filters = [];
+                               for (var i in filters) {
+                                       if (!f.elements[i].value) {
+                                               bad_filters.push(filters[i]); 
+                                       }
+                               }
+                               if (bad_filters.length) {
+                                       var fstring = '';
+                                       for (var i in bad_filters)
+                                               fstring = fstring + "\n\t" + bad_filters[i];
+
+                                       alert('You must choose a filter value for these filter:\n' + fstring);
+                                       return false;
+                               }
+
+                               Widget.Select.selectAll('output_order');
+                               return true;
+                       }
                </script>
                <br/>
        [%
@@ -256,7 +276,7 @@ BLOCK run_stage2;
 
        '<br/><br/>';
 
-       WRAPPER form id="stage3_new" name="stage3_new" action="stage3" method="POST";
+       WRAPPER form id="stage3_new" name="stage3_new" action="stage3" onsubmit="return send_it(this);" method="POST";
                INCLUDE hidden name='stage2' value=CGI.param('id');
 
                '<br/><center><b>Report Name:</b>';
@@ -277,7 +297,7 @@ BLOCK run_stage2;
                                END;
 
                                WRAPPER html/cell align="right";
-                                       INCLUDE checkbox name='output_format' value='csv';
+                                       INCLUDE checkbox name='output_format' value='csv' checked='checked';
                                END;
                                INCLUDE html/cell content="CSV" align="left";
                        END;
@@ -287,16 +307,41 @@ BLOCK run_stage2;
                                END;
 
                                WRAPPER html/cell align="right";
-                                       INCLUDE checkbox name='output_format' value='excel';
+                                       INCLUDE checkbox name='output_format' value='excel' checked='checked';
                                END;
                                INCLUDE html/cell content="Excel" align="left";
                        END;
                        WRAPPER html/row;
                                INCLUDE html/cell;
-                               WRAPPER html/cell align="right";
-                                       INCLUDE checkbox name='output_format' value='html';
+                               WRAPPER html/cell align="right" valign='top';
+                                       INCLUDE checkbox name='output_format' value='html' checked='checked';
+                               END;
+                               WRAPPER html/cell align="left";
+                                       'HTML<br/>';
+                                       WRAPPER html/table;
+                                               WRAPPER html/row + html/cell colspan=2;
+                                                       "<i>Graph types</i>";
+                                               END;
+                                               WRAPPER html/row;
+                                                       WRAPPER html/cell;
+                                                               INCLUDE checkbox name='html_graph_type' value='bar';
+                                                       END;
+                                                       INCLUDE html/cell content='Bar';
+                                               END;
+                                               WRAPPER html/row;
+                                                       WRAPPER html/cell;
+                                                               INCLUDE checkbox name='html_graph_type' value='pie';
+                                                       END;
+                                                       INCLUDE html/cell content='Pie';
+                                               END;
+                                               WRAPPER html/row;
+                                                       WRAPPER html/cell;
+                                                               INCLUDE checkbox name='html_graph_type' value='line';
+                                                       END;
+                                                       INCLUDE html/cell content='Line';
+                                               END;
+                                       END;
                                END;
-                               INCLUDE html/cell content="HTML" align="left";
                        END;
                END;
 
@@ -317,6 +362,9 @@ BLOCK run_stage2;
                                NEXT UNLESS params.filter.$t.keys;
                                f_table = INCLUDE find_table_id id=t;
                                table = config.findnodes( "/reporter/tables/table[@id='$f_table']");
+
+                               table_label = table.findvalue( 'label' );
+
                                WRAPPER html/row;
                                        WRAPPER html/cell colspan=3 align='center' style="border: solid black 1px; background: lightgray;";
                                                IF t != f_table;
@@ -326,11 +374,10 @@ BLOCK run_stage2;
                                                        link_label_xpath =
                                                                '../../fields/field[@name="' _
                                                                dim_link.findvalue('@field') _ '"]/label';
-                                                       dim_link.findvalue(link_label_xpath);
 
-                                                       ' -- ';
+                                                       table_label = table_label _ ' -- ' _ dim_link.findvalue(link_label_xpath);
                                                END;
-                                               table.findvalue( 'label' );
+                                               table_label;
                                        END;
                                END;
 
@@ -350,7 +397,7 @@ BLOCK run_stage2;
                                                        INCLUDE html/cell;
                                                        INCLUDE html/cell align='right' content=field.findvalue( 'label' );
 
-                                                       WRAPPER html/cell align='center';
+                                                       WRAPPER html/cell align='right';
                                                                TRY;
                                                                                classname = table.findvalue('@id');
                                                                                fieldname = field.findvalue('@name');
@@ -425,8 +472,8 @@ BLOCK run_stage2;
 
                INCLUDE checkbox name="publicreport" value="t";
                'Public Report<br>';
-               INCLUDE submit name="action" value="Run Now" onclick="Widget.Select.selectAll('output_order');this.form.submit();";
-               INCLUDE submit name="action" value="Schedule" onclick="Widget.Select.selectAll('output_order');this.form.submit();";
+               INCLUDE submit name="action" value="Run Now";
+               INCLUDE submit name="action" value="Schedule";
 
        END;
 END;
index 0edafb1..955fe4f 100644 (file)
@@ -171,8 +171,11 @@ BLOCK save_stage3;
        CALL q.execute(stage2, params, owner, pub, runtime, recurrence);
 
        FOR new_s3 = DBI.query("SELECT * FROM reporter.stage3_id_seq;");
-               tid = new_s3.last_value;
-               INCLUDE view_stage3 rpt = reports.$tid;
+               rid = new_s3.last_value;
+               rpt = reports.$rid;
+               tid = rpt.stage2;
+               tmpl = templates.$tid;
+               INCLUDE view_stage3;
        END;
 END;
 
index 3d47bf8..0cc12a4 100644 (file)
@@ -4,6 +4,8 @@ PROCESS inputs;
 
 q = 'SELECT * FROM ' _ table.findvalue('tablename') _ ' ORDER BY ' _ fieldname _ ';';
 
+'<script language="javascript">filters["' _ input_prefix _ '"] = "' _ table_label _ ' -- ' _ field.findvalue('label') _ '";</script>';
+
 WRAPPER select name=input_prefix;
        FOR f = DBI.query(q);
                INCLUDE option value=f.$fieldname;
index 6e9019c..f51b6a1 100644 (file)
@@ -4,7 +4,9 @@ PROCESS inputs;
 
 q = 'SELECT DISTINCT ' _ fieldname _ ' FROM ' _ table.findvalue('tablename') _ ' ORDER BY ' _ fieldname _ ';';
 
-WRAPPER select name=input_prefix multi=1 size=3;
+'<script language="javascript">filters["' _ input_prefix _ '"] = "' _ table_label _ ' -- ' _ field.findvalue('label') _ '";</script>';
+
+WRAPPER select name=input_prefix multi=1 size=10;
        FOR f = DBI.query(q);
                INCLUDE option value=f.$fieldname;
        END;
index c395419..45b740f 100644 (file)
@@ -416,5 +416,498 @@ INSERT INTO config.net_access_level (name) VALUES ('Restricted');
 INSERT INTO config.net_access_level (name) VALUES ('Full');
 INSERT INTO config.net_access_level (name) VALUES ('None');
 
+CREATE TABLE config.language_map (
+       code    TEXT    PRIMARY KEY,
+       value   TEXT    NOT NULL
+);
+
+COPY config.language_map FROM STDIN;
+aar    Afar
+abk    Abkhaz
+ace    Achinese
+ach    Acoli
+ada    Adangme
+ady    Adygei
+afa    Afroasiatic (Other)
+afh    Afrihili (Artificial language)
+afr    Afrikaans
+-ajm   Aljamía
+aka    Akan
+akk    Akkadian
+alb    Albanian
+ale    Aleut
+alg    Algonquian (Other)
+amh    Amharic
+ang    English, Old (ca. 450-1100)
+apa    Apache languages
+ara    Arabic
+arc    Aramaic
+arg    Aragonese Spanish
+arm    Armenian
+arn    Mapuche
+arp    Arapaho
+art    Artificial (Other)
+arw    Arawak
+asm    Assamese
+ast    Bable
+ath    Athapascan (Other)
+aus    Australian languages
+ava    Avaric
+ave    Avestan
+awa    Awadhi
+aym    Aymara
+aze    Azerbaijani
+bad    Banda
+bai    Bamileke languages
+bak    Bashkir
+bal    Baluchi
+bam    Bambara
+ban    Balinese
+baq    Basque
+bas    Basa
+bat    Baltic (Other)
+bej    Beja
+bel    Belarusian
+bem    Bemba
+ben    Bengali
+ber    Berber (Other)
+bho    Bhojpuri
+bih    Bihari
+bik    Bikol
+bin    Edo
+bis    Bislama
+bla    Siksika
+bnt    Bantu (Other)
+bos    Bosnian
+bra    Braj
+bre    Breton
+btk    Batak
+bua    Buriat
+bug    Bugis
+bul    Bulgarian
+bur    Burmese
+cad    Caddo
+cai    Central American Indian (Other)
+-cam   Khmer
+car    Carib
+cat    Catalan
+cau    Caucasian (Other)
+ceb    Cebuano
+cel    Celtic (Other)
+cha    Chamorro
+chb    Chibcha
+che    Chechen
+chg    Chagatai
+chi    Chinese
+chk    Truk
+chm    Mari
+chn    Chinook jargon
+cho    Choctaw
+chp    Chipewyan
+chr    Cherokee
+chu    Church Slavic
+chv    Chuvash
+chy    Cheyenne
+cmc    Chamic languages
+cop    Coptic
+cor    Cornish
+cos    Corsican
+cpe    Creoles and Pidgins, English-based (Other)
+cpf    Creoles and Pidgins, French-based (Other)
+cpp    Creoles and Pidgins, Portuguese-based (Other)
+cre    Cree
+crh    Crimean Tatar
+crp    Creoles and Pidgins (Other)
+cus    Cushitic (Other)
+cze    Czech
+dak    Dakota
+dan    Danish
+dar    Dargwa
+day    Dayak
+del    Delaware
+den    Slave
+dgr    Dogrib
+din    Dinka
+div    Divehi
+doi    Dogri
+dra    Dravidian (Other)
+dua    Duala
+dum    Dutch, Middle (ca. 1050-1350)
+dut    Dutch
+dyu    Dyula
+dzo    Dzongkha
+efi    Efik
+egy    Egyptian
+eka    Ekajuk
+elx    Elamite
+eng    English
+enm    English, Middle (1100-1500)
+epo    Esperanto
+-esk   Eskimo languages
+-esp   Esperanto
+est    Estonian
+-eth   Ethiopic
+ewe    Ewe
+ewo    Ewondo
+fan    Fang
+fao    Faroese
+-far   Faroese
+fat    Fanti
+fij    Fijian
+fin    Finnish
+fiu    Finno-Ugrian (Other)
+fon    Fon
+fre    French
+-fri   Frisian
+frm    French, Middle (ca. 1400-1600)
+fro    French, Old (ca. 842-1400)
+fry    Frisian
+ful    Fula
+fur    Friulian
+gaa    Gã
+-gae   Scottish Gaelic
+-gag   Galician
+-gal   Oromo
+gay    Gayo
+gba    Gbaya
+gem    Germanic (Other)
+geo    Georgian
+ger    German
+gez    Ethiopic
+gil    Gilbertese
+gla    Scottish Gaelic
+gle    Irish
+glg    Galician
+glv    Manx
+gmh    German, Middle High (ca. 1050-1500)
+goh    German, Old High (ca. 750-1050)
+gon    Gondi
+gor    Gorontalo
+got    Gothic
+grb    Grebo
+grc    Greek, Ancient (to 1453)
+gre    Greek, Modern (1453- )
+grn    Guarani
+-gua   Guarani
+guj    Gujarati
+gwi    Gwich'in
+hai    Haida
+hat    Haitian French Creole
+hau    Hausa
+haw    Hawaiian
+heb    Hebrew
+her    Herero
+hil    Hiligaynon
+him    Himachali
+hin    Hindi
+hit    Hittite
+hmn    Hmong
+hmo    Hiri Motu
+hun    Hungarian
+hup    Hupa
+iba    Iban
+ibo    Igbo
+ice    Icelandic
+ido    Ido
+iii    Sichuan Yi
+ijo    Ijo
+iku    Inuktitut
+ile    Interlingue
+ilo    Iloko
+ina    Interlingua (International Auxiliary Language Association)
+inc    Indic (Other)
+ind    Indonesian
+ine    Indo-European (Other)
+inh    Ingush
+-int   Interlingua (International Auxiliary Language Association)
+ipk    Inupiaq
+ira    Iranian (Other)
+-iri   Irish
+iro    Iroquoian (Other)
+ita    Italian
+jav    Javanese
+jpn    Japanese
+jpr    Judeo-Persian
+jrb    Judeo-Arabic
+kaa    Kara-Kalpak
+kab    Kabyle
+kac    Kachin
+kal    Kalâtdlisut
+kam    Kamba
+kan    Kannada
+kar    Karen
+kas    Kashmiri
+kau    Kanuri
+kaw    Kawi
+kaz    Kazakh
+kbd    Kabardian
+kha    Khasi
+khi    Khoisan (Other)
+khm    Khmer
+kho    Khotanese
+kik    Kikuyu
+kin    Kinyarwanda
+kir    Kyrgyz
+kmb    Kimbundu
+kok    Konkani
+kom    Komi
+kon    Kongo
+kor    Korean
+kos    Kusaie
+kpe    Kpelle
+kro    Kru
+kru    Kurukh
+kua    Kuanyama
+kum    Kumyk
+kur    Kurdish
+-kus   Kusaie
+kut    Kutenai
+lad    Ladino
+lah    Lahnda
+lam    Lamba
+-lan   Occitan (post-1500)
+lao    Lao
+-lap   Sami
+lat    Latin
+lav    Latvian
+lez    Lezgian
+lim    Limburgish
+lin    Lingala
+lit    Lithuanian
+lol    Mongo-Nkundu
+loz    Lozi
+ltz    Letzeburgesch
+lua    Luba-Lulua
+lub    Luba-Katanga
+lug    Ganda
+lui    Luiseño
+lun    Lunda
+luo    Luo (Kenya and Tanzania)
+lus    Lushai
+mac    Macedonian
+mad    Madurese
+mag    Magahi
+mah    Marshallese
+mai    Maithili
+mak    Makasar
+mal    Malayalam
+man    Mandingo
+mao    Maori
+map    Austronesian (Other)
+mar    Marathi
+mas    Masai
+-max   Manx
+may    Malay
+mdr    Mandar
+men    Mende
+mga    Irish, Middle (ca. 1100-1550)
+mic    Micmac
+min    Minangkabau
+mis    Miscellaneous languages
+mkh    Mon-Khmer (Other)
+-mla   Malagasy
+mlg    Malagasy
+mlt    Maltese
+mnc    Manchu
+mni    Manipuri
+mno    Manobo languages
+moh    Mohawk
+mol    Moldavian
+mon    Mongolian
+mos    Mooré
+mul    Multiple languages
+mun    Munda (Other)
+mus    Creek
+mwr    Marwari
+myn    Mayan languages
+nah    Nahuatl
+nai    North American Indian (Other)
+nap    Neapolitan Italian
+nau    Nauru
+nav    Navajo
+nbl    Ndebele (South Africa)
+nde    Ndebele (Zimbabwe)  
+ndo    Ndonga
+nds    Low German
+nep    Nepali
+new    Newari
+nia    Nias
+nic    Niger-Kordofanian (Other)
+niu    Niuean
+nno    Norwegian (Nynorsk)
+nob    Norwegian (Bokmål)
+nog    Nogai
+non    Old Norse
+nor    Norwegian
+nso    Northern Sotho
+nub    Nubian languages
+nya    Nyanja
+nym    Nyamwezi
+nyn    Nyankole
+nyo    Nyoro
+nzi    Nzima
+oci    Occitan (post-1500)
+oji    Ojibwa
+ori    Oriya
+orm    Oromo
+osa    Osage
+oss    Ossetic
+ota    Turkish, Ottoman
+oto    Otomian languages
+paa    Papuan (Other)
+pag    Pangasinan
+pal    Pahlavi
+pam    Pampanga
+pan    Panjabi
+pap    Papiamento
+pau    Palauan
+peo    Old Persian (ca. 600-400 B.C.)
+per    Persian
+phi    Philippine (Other)
+phn    Phoenician
+pli    Pali
+pol    Polish
+pon    Ponape
+por    Portuguese
+pra    Prakrit languages
+pro    Provençal (to 1500)
+pus    Pushto
+que    Quechua
+raj    Rajasthani
+rap    Rapanui
+rar    Rarotongan
+roa    Romance (Other)
+roh    Raeto-Romance
+rom    Romani
+rum    Romanian
+run    Rundi
+rus    Russian
+sad    Sandawe
+sag    Sango (Ubangi Creole)
+sah    Yakut
+sai    South American Indian (Other)
+sal    Salishan languages
+sam    Samaritan Aramaic
+san    Sanskrit
+-sao   Samoan
+sas    Sasak
+sat    Santali
+scc    Serbian
+sco    Scots
+scr    Croatian
+sel    Selkup
+sem    Semitic (Other)
+sga    Irish, Old (to 1100)
+sgn    Sign languages
+shn    Shan
+-sho   Shona
+sid    Sidamo
+sin    Sinhalese
+sio    Siouan (Other)
+sit    Sino-Tibetan (Other)
+sla    Slavic (Other)
+slo    Slovak
+slv    Slovenian
+sma    Southern Sami
+sme    Northern Sami
+smi    Sami
+smj    Lule Sami
+smn    Inari Sami
+smo    Samoan
+sms    Skolt Sami
+sna    Shona
+snd    Sindhi
+-snh   Sinhalese
+snk    Soninke
+sog    Sogdian
+som    Somali
+son    Songhai
+sot    Sotho
+spa    Spanish
+srd    Sardinian
+srr    Serer
+ssa    Nilo-Saharan (Other)
+-sso   Sotho
+ssw    Swazi
+suk    Sukuma
+sun    Sundanese
+sus    Susu
+sux    Sumerian
+swa    Swahili
+swe    Swedish
+-swz   Swazi
+syr    Syriac
+-tag   Tagalog
+tah    Tahitian
+tai    Tai (Other)
+-taj   Tajik
+tam    Tamil
+-tar   Tatar
+tat    Tatar
+tel    Telugu
+tem    Temne
+ter    Terena
+tet    Tetum
+tgk    Tajik
+tgl    Tagalog
+tha    Thai
+tib    Tibetan
+tig    Tigré
+tir    Tigrinya
+tiv    Tiv
+tkl    Tokelauan
+tli    Tlingit
+tmh    Tamashek
+tog    Tonga (Nyasa)
+ton    Tongan
+tpi    Tok Pisin
+-tru   Truk
+tsi    Tsimshian
+tsn    Tswana
+tso    Tsonga
+-tsw   Tswana
+tuk    Turkmen
+tum    Tumbuka
+tup    Tupi languages
+tur    Turkish
+tut    Altaic (Other)
+tvl    Tuvaluan
+twi    Twi
+tyv    Tuvinian
+udm    Udmurt
+uga    Ugaritic
+uig    Uighur
+ukr    Ukrainian
+umb    Umbundu
+und    Undetermined
+urd    Urdu
+uzb    Uzbek
+vai    Vai
+ven    Venda
+vie    Vietnamese
+vol    Volapük
+vot    Votic
+wak    Wakashan languages
+wal    Walamo
+war    Waray
+was    Washo
+wel    Welsh
+wen    Sorbian languages
+wln    Walloon
+wol    Wolof
+xal    Kalmyk
+xho    Xhosa
+yao    Yao (Africa)
+yap    Yapese
+yid    Yiddish
+yor    Yoruba
+ypk    Yupik languages
+zap    Zapotec
+zen    Zenaga
+zha    Zhuang
+znd    Zande
+zul    Zulu
+zun    Zuni
+\.
 
 COMMIT;