File Coverage

lib/CallBackery/GuiPlugin/AbstractTable.pm
Criterion Covered Total %
statement 24 85 28.2
branch 0 22 0.0
condition 0 17 0.0
subroutine 8 13 61.5
pod 4 4 100.0
total 36 141 25.5


line stmt bran cond sub pod time code
1             package CallBackery::GuiPlugin::AbstractTable;
2 1     1   627 use Carp qw(carp croak);
  1         3  
  1         65  
3 1     1   7 use CallBackery::Translate qw(trm);
  1         2  
  1         40  
4 1     1   5 use CallBackery::Exception qw(mkerror);
  1         2  
  1         52  
5 1     1   842 use Text::CSV;
  1         16770  
  1         46  
6 1     1   1525 use Excel::Writer::XLSX;
  1         204180  
  1         70  
7 1     1   12 use Mojo::JSON qw(true false);
  1         3  
  1         75  
8 1     1   764 use Time::Piece;
  1         7897  
  1         5  
9              
10             =head1 NAME
11              
12             CallBackery::GuiPlugin::AbstractTable - Base Class for a table plugin
13              
14             =head1 SYNOPSIS
15              
16             use Mojo::Base 'CallBackery::GuiPlugin::AbstractTable';
17              
18             =head1 DESCRIPTION
19              
20             The base class for table plugins, derived from CallBackery::GuiPlugin::AbstractForm
21              
22             =cut
23              
24 1     1   92 use Mojo::Base 'CallBackery::GuiPlugin::AbstractForm';
  1         3  
  1         11  
25              
26             =head1 ATTRIBUTES
27              
28             The attributes of the L class and these:
29              
30             =cut
31              
32             has screenCfg => sub {
33             my $self = shift;
34             my $screen = $self->SUPER::screenCfg;
35             $screen->{table} = $self->tableCfg;
36             $screen->{type} = 'table';
37             return $screen;
38             };
39              
40             =head2 tableCfg
41              
42             a table configuration
43              
44             return [
45             {
46             label => trm('Id'),
47             type => 'number',
48             flex => 1,
49             key => 'id',
50             sortable => true,
51             },
52             {
53             label => trm('Date'),
54             type => 'str',
55             flex => 2
56             key => 'date'
57             },
58             {
59             label => trm('Content'),
60             type => 'str',
61             flex => 8,
62             key => 'date'
63             },
64             ]
65              
66             =cut
67              
68             has tableCfg => sub {
69             croak "the plugin must define its tableCfg property";
70             };
71              
72             =head1 METHODS
73              
74             All the methods of L plus:
75              
76             =cut
77              
78              
79             =head2 getData ('tableData|tableRowCount',tableDataRequest);
80              
81             Return the requested table data and pass other types of request on to the upper levels.
82              
83             =cut
84              
85             sub getData {
86 0     0 1   my $self = shift;
87 0   0       my $type = shift // '';
88 0 0         if ($type eq 'tableData'){
    0          
89 0           return $self->getTableData(@_);
90             }
91             elsif ($type eq 'tableRowCount'){
92 0           return $self->getTableRowCount(@_);
93             }
94             else {
95 0           return $self->SUPER::getData($type,@_);
96             }
97             }
98              
99             =head2 getTableData({formData=>{},firstRow=>{},lastRow=>{},sortColumn=>'key',sortDesc=>true})
100              
101             return data appropriate for the remote table widget
102              
103             =cut
104              
105             sub getTableData {
106 0     0 1   return [{}];
107             }
108              
109             =head2 getTableRowCount({formData=>{}})
110              
111             return the number of rows matching the given formData
112              
113             =cut
114              
115             sub getTableRowCount {
116 0     0 1   return 0;
117             }
118              
119             =head2 makeExportAction(type => 'XLSX', filename => 'export-"now"', label => 'Export')
120              
121             Create export button.
122             The default type is XLSX, also available is CSV.
123              
124             =cut
125              
126             sub makeExportAction {
127 0     0 1   my $self = shift;
128 0           my %args = @_;
129 0   0       my $type = $args{type} // 'XLSX';
130 0   0       my $label = $args{label} // trm("Export %1", $type);
131             my $filename = $args{filename}
132 0   0       // localtime->strftime('export-%Y-%m-%d-%H-%M-%S.').lc($type);
133              
134             return {
135             label => $label,
136             action => 'download',
137             addToContextMenu => true,
138             key => 'export_csv',
139             actionHandler => sub {
140 0     0     my $self = shift;
141 0           my $args = shift;
142 0           my $data = $self->getTableData({
143             formData => $args,
144             firstRow => 0,
145             lastRow => $self->getTableRowCount({ formData=>$args })
146             });
147              
148             # Use the (translated) table headers in row 1.
149             # Or the keys if undefined.
150 0           my $loc = CallBackery::Translate->new(localeRoot=>$self->app->home->child("share"));
151 0   0       $loc->setLocale($self->user->userInfo->{lang} // 'en');
152 0           my $tCfg = $self->tableCfg;
153              
154             my @titles = map {
155 0           $_->{label}
156             ? ((ref $_->{label} eq 'CallBackery::Translate')
157             ? $loc->tra($_->{label}[0])
158             : $_->{label})
159 0 0         : $_->{key};
    0          
160             } @$tCfg;
161              
162 0 0         if ($type eq 'CSV') {
    0          
163 0           my $csv = Text::CSV->new;
164 0           $csv->combine(@titles);
165 0           my $csv_str = $csv->string . "\n";
166 0           for my $record (@$data) {
167             $csv->combine(map {
168 0           my $v = $record->{$_->{key}};
  0            
169 0 0         if ($_->{type} eq 'date') {
170 0           $v= localtime($v/1000)->strftime("%Y-%m-%d %H:%M:%S %z");
171             }
172 0           $v} @$tCfg);
173 0           $csv_str .= $csv->string . "\n";
174             }
175 0           my $asset = Mojo::Asset::Memory->new;
176 0           $asset->add_chunk($csv_str);
177             return {
178 0           asset => $asset,
179             type => 'text/csv',
180             filename => $filename,
181             }
182             }
183             elsif ($type eq 'XLSX') {
184 0 0         open my $xh, '>', \my $xlsx or die "failed to open xlsx fh: $!";
185 0           my $workbook = Excel::Writer::XLSX->new($xh);
186 0           my $worksheet = $workbook->add_worksheet();
187              
188 0           my $col = 0;
189 0           map {$worksheet->write(0, $col, $_); $col++} @titles;
  0            
  0            
190              
191 0           my $row = 2;
192 0           my %date_format;
193 0           for my $record (@$data) {
194 0           $col = 0;
195 0           for my $tc (@$tCfg) {
196 0           my $v = $record->{$tc->{key}};
197 0 0         if ($tc->{type} eq 'date') {
198 0   0       my $fmt = $tc->{format} //'yyyy-mm-dd hh:mm:ss';
199 0   0       $date_format{$fmt} //=
200             $workbook->add_format(num_format => $fmt);
201 0 0         $worksheet->write_date_time($row,$col,localtime($v/1000)->strftime("%Y-%m-%dT%H:%M:%S"),$date_format{$fmt}) if $v;
202             }
203             else {
204 0 0         $worksheet->write($row, $col, $v) if defined $v;
205             }
206 0           $col++}
207 0           $row++;
208             }
209              
210 0           $workbook->close();
211 0           my $asset = Mojo::Asset::Memory->new;
212 0           $asset->add_chunk($xlsx);
213             return {
214 0           asset => $asset,
215             type => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
216             filename => $filename,
217             }
218              
219             }
220             else {
221 0           die mkerror(9999, "unknown export type $type");
222             }
223             }
224 0           };
225             }
226              
227             1;
228             __END__