File Coverage

blib/lib/Chart/Kaleido/Plotly.pm
Criterion Covered Total %
statement 69 76 90.7
branch 8 12 66.6
condition n/a
subroutine 20 24 83.3
pod 2 5 40.0
total 99 117 84.6


line stmt bran cond sub pod time code
1             package Chart::Kaleido::Plotly;
2              
3             # ABSTRACT: Export static images of Plotly charts using Kaleido
4              
5 1     1   253268 use 5.010;
  1         7  
6 1     1   5 use strict;
  1         2  
  1         30  
7 1     1   6 use warnings;
  1         2  
  1         47  
8              
9             our $VERSION = '0.014'; # VERSION
10              
11 1     1   566 use Moo;
  1         7299  
  1         4  
12             extends 'Chart::Kaleido';
13              
14 1     1   2029 use File::ShareDir;
  1         26220  
  1         53  
15 1     1   9 use JSON;
  1         2  
  1         10  
16 1     1   653 use MIME::Base64 qw(decode_base64);
  1         617  
  1         58  
17 1     1   7 use Path::Tiny;
  1         2  
  1         54  
18 1     1   462 use Safe::Isa;
  1         553  
  1         133  
19 1     1   636 use Type::Params 1.004000 qw(compile_named_oo);
  1         114519  
  1         10  
20 1     1   844 use Types::Path::Tiny qw(File Path);
  1         40085  
  1         11  
21 1     1   767 use Types::Standard qw(Int Str Num HashRef InstanceOf Optional Undef);
  1         3  
  1         7  
22 1     1   3693 use namespace::autoclean;
  1         17021  
  1         5  
23              
24              
25             my @text_formats = qw(svg json eps);
26              
27              
28             my $default_plotlyjs = sub {
29             my $plotlyjs;
30             eval {
31             $plotlyjs = File::ShareDir::dist_file( 'Chart-Plotly',
32             'plotly.js/plotly.min.js' );
33             };
34             return $plotlyjs;
35             };
36              
37             has plotlyjs => (
38             is => 'ro',
39             isa => ( Str->plus_coercions( Undef, $default_plotlyjs ) | Undef ),
40             default => $default_plotlyjs,
41             coerce => 1,
42             );
43              
44             has [qw(mathjax topojson)] => (
45             is => 'ro',
46             isa => ( Str | Undef ),
47             coerce => 1,
48             );
49              
50             has mapbox_access_token => (
51             is => 'ro',
52             isa => ( Str | Undef ),
53             );
54              
55              
56             my $PositiveInt = Int->where( sub { $_ > 0 } );
57              
58             has default_format => (
59             is => 'ro',
60             isa => Str,
61             default => 'png',
62             );
63              
64             has default_width => (
65             is => 'ro',
66             isa => $PositiveInt,
67             default => 700,
68             );
69              
70             has default_height => (
71             is => 'ro',
72             isa => $PositiveInt,
73             default => 500,
74             );
75              
76             has '+base_args' => (
77             default => sub {
78             my $self = shift;
79             [ "plotly", @{ $self->_default_chromium_args }, "--no-sandbox" ];
80             }
81             );
82              
83 3     3 0 26 sub all_formats { [qw(png jpg jpeg webp svg pdf eps json)] }
84 0     0 0 0 sub scope_name { 'plotly' }
85 5     5 0 34 sub scope_flags { [qw(plotlyjs mathjax topojson mapbox_access_token)] }
86              
87              
88             sub transform {
89 3     3 1 2942976 my $self = shift;
90             state $check = compile_named_oo(
91             #<<< no perltidy
92             plot => ( HashRef | InstanceOf["Chart::Plotly::Plot"] ),
93 1     1   44 format => Optional[Str], { default => sub { $self->default_format } },
94 1     1   18 width => $PositiveInt, { default => sub { $self->default_width } },
95 1     1   30 height => $PositiveInt, { default => sub { $self->default_height} },
96 3         13 scale => Num, { default => 1 },
97             #>>>
98             );
99 3         8826 my $arg = $check->(@_);
100 3 100       114 my $plot =
101             $arg->plot->$_isa('Chart::Plotly::Plot')
102             ? $arg->plot->TO_JSON
103             : $arg->plot;
104 3         271 my $format = lc( $arg->format );
105              
106 3 50       9 unless ( grep { $_ eq $format } @{ $self->all_formats } ) {
  24         56  
  3         25  
107             die "Invalid format '$format'. Supported formats: "
108 0         0 . join( ' ', @{ $self->all_formats } );
  0         0  
109             }
110              
111 3         29 my $data = {
112             format => $format,
113             width => $arg->width,
114             height => $arg->height,
115             scale => $arg->scale,
116             data => $plot,
117             };
118              
119 3     0   46 local *PDL::TO_JSON = sub { $_[0]->unpdl };
  0         0  
120 3         28 my $resp = $self->do_transform($data);
121 3 50       17 if ( $resp->{code} != 0 ) {
122 0         0 die $resp->{message};
123             }
124 3         25 my $img = $resp->{result};
125 3 100       18 return ( grep { $_ eq $format } @text_formats )
  9         590  
126             ? $img
127             : decode_base64($img);
128             }
129              
130              
131             sub save {
132 2     2 1 4250 my $self = shift;
133             state $check = compile_named_oo(
134             #<<< no perltidy
135             file => Path,
136             plot => ( HashRef | InstanceOf["Chart::Plotly::Plot"] ),
137             format => Optional[Str],
138 0     0   0 width => $PositiveInt, { default => sub { $self->default_width } },
139 0     0   0 height => $PositiveInt, { default => sub { $self->default_height} },
140 2         11 scale => Num, { default => 1 },
141             #>>>
142             );
143 2         59364 my $arg = $check->(@_);
144 2         73 my $format = $arg->format;
145 2         8 my $file = $arg->file;
146 2 50       17 unless ($format) {
147 2 50       13 if ( $file =~ /\.([^\.]+)$/ ) {
148 2         51 $format = $1;
149             }
150             }
151 2         13 $format = lc($format);
152              
153 2         23 my $img = $self->transform(
154             plot => $arg->plot,
155             format => $format,
156             width => $arg->width,
157             height => $arg->height,
158             scale => $arg->scale
159             );
160 2         36 path($file)->append_raw( { truncate => 1 }, $img );
161             }
162              
163             1;
164              
165             __END__
166              
167             =pod
168              
169             =encoding UTF-8
170              
171             =head1 NAME
172              
173             Chart::Kaleido::Plotly - Export static images of Plotly charts using Kaleido
174              
175             =head1 VERSION
176              
177             version 0.014
178              
179             =head1 SYNOPSIS
180              
181             use Chart::Kaleido::Plotly;
182             use JSON;
183              
184             my $kaleido = Chart::Kaleido::Plotly->new();
185              
186             # convert a hashref
187             my $data = decode_json(<<'END_OF_TEXT');
188             { "data": [{"y": [1,2,1]}] }
189             END_OF_TEXT
190             $kaleido->save( file => "foo.png", plot => $data,
191             width => 1024, height => 768 );
192              
193             # convert a Chart::Plotly::Plot object
194             use Chart::Plotly::Plot;
195             my $plot = Chart::Plotly::Plot->new(
196             traces => [
197             Chart::Plotly::Trace::Scatter->new( x => [ 1 .. 5 ], y => [ 1 .. 5 ] )
198             ]
199             );
200             $kaleido->save( file => "foo.png", plot => $plot,
201             width => 1024, height => 768 );
202              
203             =head1 DESCRIPTION
204              
205             This class wraps the "plotly" scope of plotly's kaleido command.
206              
207             =head1 ATTRIBUTES
208              
209             =head2 timeout
210              
211             =head2 plotlyjs
212              
213             Path to plotly js file.
214             Default value is plotly js bundled with L<Chart::Ploly>.
215              
216             =head2 mathjax
217              
218             =head2 topojson
219              
220             =head2 mapbox_access_token
221              
222             =head2 default_format
223              
224             Default is "png".
225              
226             =head2 default_width
227              
228             Default is 700.
229              
230             =head2 default_height
231              
232             Default is 500.
233              
234             =head1 METHODS
235              
236             =head2 transform
237              
238             transform(( HashRef | InstanceOf["Chart::Plotly::Plot"] ) :$plot,
239             Str :$format=$self->default_format,
240             PositiveInt :$width=$self->default_width,
241             PositiveInt :$height=$self->default_height,
242             Num :$scale=1)
243              
244             Returns raw image data.
245              
246             =head2 save
247              
248             save(:$file,
249             ( HashRef | InstanceOf["Chart::Plotly::Plot"] ) :$plot,
250             Optional[Str] :$format,
251             PositiveInt :$width=$self->default_width,
252             PositiveInt :$height=$self->default_height,
253             Num :$scale=1)
254              
255             Save static image to file.
256              
257             =head1 SEE ALSO
258              
259             L<https://github.com/plotly/Kaleido>
260              
261             L<Chart::Plotly>,
262             L<Chart::Kaleido>,
263             L<Alien::Plotly::Kaleido>
264              
265             =head1 AUTHOR
266              
267             Stephan Loyd <sloyd@cpan.org>
268              
269             =head1 COPYRIGHT AND LICENSE
270              
271             This software is copyright (c) 2020-2023 by Stephan Loyd.
272              
273             This is free software; you can redistribute it and/or modify it under
274             the same terms as the Perl 5 programming language system itself.
275              
276             =cut