Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision f74b0dac

Von Sven Schöling vor mehr als 8 Jahren hinzugefügt

  • ID f74b0dac7a8e55cc7a67c4a03a3ee229415b3fbd
  • Vorgänger 621248de
  • Nachfolger a0d9f09a

Csv: mapping support

Unterschiede anzeigen:

SL/Helper/Csv.pm
204 204
      foreach my $h (@{ $h_aref }) {
205 205
        my @names = (
206 206
          keys %{ $self->profile->[$p_num]->{profile} || {} },
207
          keys %{ $self->profile->[$p_num]->{mapping} || {} },
207 208
        );
208 209
        for my $name (@names) {
209 210
          for my $i (0..$#$h) {
......
527 528

  
528 529
  PROFILE       := [ CLASS_PROFILE, CLASS_PROFILE* ]
529 530
  CLASS_PROFILE := {
530
                      profile   => { ACCESSORS },
531
                      profile   => { ACCESSORS+ },
531 532
                      class     => $classname,
532 533
                      row_ident => $row_ident,
534
                      mapping   => { MAPPINGS* },
533 535
                   }
534
  ACCESSORS     := $field => $accessor, ACCESSORS*
536
  ACCESSORS     := $field => $accessor
537
  MAPPINGS      := $alias => $field
535 538

  
536
The C<profile> is a HASHREF which may be used to map header fields to custom
539
The C<ACCESSORS> may be used to map header fields to custom
537 540
accessors. Example:
538 541

  
539
  [
540
    {
541
      profile => {
542
        listprice => 'listprice_as_number',
543
      }
544
    }
545
  ]
542
  profile => {
543
    listprice => 'listprice_as_number',
544
  }
546 545

  
547 546
In this case C<listprice_as_number> will be used to store the values from the
548 547
C<listprice> column.
......
575 574
C<row_ident> is used to determine the correct profile in multiplexed data and
576 575
must be given there. It's not used in non-multiplexed data.
577 576

  
578
Example:
577
If C<mappings> is present, it must contain a hashref that maps strings to known
578
fields. This can be used to add custom profiles for known sources, that don't
579
comply with the expected header identities.
580

  
581
Without strict profiles, mappings can also directly map header fields that
582
should end up in the same accessor.
583

  
584
Mappings can be identical to known fields and will be prefered during lookup,
585
but will not replace the field, meaning that:
586

  
587
  profile => {
588
    name        => 'name',
589
    description => 'description',
590
  }
591
  mapping => {
592
    name        => 'description',
593
    shortname   => 'name',
594
  }
595

  
596
will work as expected, and shortname will not end up in description. This also
597
works with the case insensitive option. Note however that the case insensitive
598
option will not enable true unicode collating.
599

  
600

  
601
Here's a full example:
602

  
579 603
  [
580 604
    {
581 605
      class     => 'SL::DB::Order',
......
584 608
    {
585 609
      class     => 'SL::DB::OrderItem',
586 610
      row_ident => 'I',
587
      profile   => { sellprice => 'sellprice_as_number' }
611
      profile   => { sellprice => 'sellprice_as_number' },
612
      mapping   => { 'Verkaufspreis' => 'sellprice' }
588 613
    },
589 614
  ]
590 615

  
......
601 626

  
602 627
Note that the last entry can be off, but will give an estimate.
603 628

  
629
Error handling is also known to break on new Perl versions and need to be
630
adjusted from time to time due to changes in Text::CSV_XS.
631

  
604 632
=head1 CAVEATS
605 633

  
606 634
=over 4
SL/Helper/Csv/Dispatcher.pm
113 113
  my $i = 0;
114 114
  foreach my $header (@{ $h_aref }) {
115 115
    my $spec = $self->_parse_profile(profile => $csv_profile->[$i]->{profile},
116
                                     mapping => $csv_profile->[$i]->{mapping},
116 117
                                     class   => $csv_profile->[$i]->{class},
117 118
                                     header  => $header);
118 119
    push @specs, $spec;
......
132 133
  my $profile = $params{profile};
133 134
  my $class   = $params{class};
134 135
  my $header  = $params{header};
136
  my $mapping = $params{mapping};
135 137

  
136 138
  my @specs;
137 139

  
138 140
  for my $col (@$header) {
139 141
    next unless $col;
140
    if ($self->_csv->strict_profile) {
141
      if (exists $profile->{$col}) {
142
        push @specs, $self->make_spec($col, $profile->{$col}, $class);
143
      } else {
144
        $self->unknown_column($col, undef);
145
      }
142
    if (exists $mapping->{$col} && $profile->{$mapping->{$col}}) {
143
      push @specs, $self->make_spec($col, $profile->{$mapping->{$col}}, $class);
144
    } elsif (exists $mapping->{$col}) {
145
      push @specs, $self->make_spec($col, $mapping->{$col}, $class);
146
    } elsif (exists $profile->{$col}) {
147
      push @specs, $self->make_spec($col, $profile->{$col}, $class);
146 148
    } else {
147
      if (exists $profile->{$col}) {
148
        push @specs, $self->make_spec($col, $profile->{$col}, $class);
149
      if ($self->_csv->strict_profile) {
150
        $self->unknown_column($col, undef);
149 151
      } else {
150 152
        push @specs, $self->make_spec($col, $col, $class);
151 153
      }
t/helper/csv.t
1
use Test::More tests => 75;
1
use Test::More tests => 84;
2 2

  
3 3
use lib 't';
4 4
use utf8;
......
726 726

  
727 727
#####
728 728

  
729
# Mappings
730
# simple case
731
$csv = SL::Helper::Csv->new(
732
  file   => \<<EOL,
733
description,sellprice,lastcost_as_number,purchaseprice,
734
Kaffee,0.12,'12,2','1,5234'
735
EOL
736
  sep_char => ',',
737
  quote_char => "'",
738
  profile => [
739
    {
740
      profile => { listprice => 'listprice_as_number' },
741
      mapping => { purchaseprice => 'listprice' },
742
      class   => 'SL::DB::Part',
743
    }
744
  ],
745
);
746
ok $csv->parse, 'simple mapping parses';
747
is $csv->get_objects->[0]->listprice, 1.5234, 'simple mapping works';
748

  
749
$csv = SL::Helper::Csv->new(
750
  file   => \<<EOL,
751
description;partnumber;sellprice;purchaseprice;wiener;
752
Kaffee;;0.12;1,221.52;ja wiener
753
Beer;1123245;0.12;1.5234;nein kein wieder
754
EOL
755
  numberformat => '1,000.00',
756
  ignore_unknown_columns => 1,
757
  strict_profile => 1,
758
  profile => [{
759
    profile => { lastcost => 'lastcost_as_number' },
760
    mapping => { purchaseprice => 'lastcost' },
761
    class  => 'SL::DB::Part',
762
  }]
763
);
764
ok $csv->parse, 'strict mapping parses';
765
is $csv->get_objects->[0]->lastcost, 1221.52, 'strict mapping works';
766

  
767
# swapping
768
$csv = SL::Helper::Csv->new(
769
  file   => \<<EOL,
770
description;partnumber;sellprice;lastcost;wiener;
771
Kaffee;1;0.12;1,221.52;ja wiener
772
Beer;1123245;0.12;1.5234;nein kein wieder
773
EOL
774
  numberformat => '1,000.00',
775
  ignore_unknown_columns => 1,
776
  strict_profile => 1,
777
  profile => [{
778
    mapping => { partnumber => 'description', description => 'partnumber' },
779
    class  => 'SL::DB::Part',
780
  }]
781
);
782
ok $csv->parse, 'swapping parses';
783
is $csv->get_objects->[0]->partnumber, 'Kaffee', 'strict mapping works 1';
784
is $csv->get_objects->[0]->description, '1', 'strict mapping works 2';
785

  
786
# case insensitive shit
787
$csv = SL::Helper::Csv->new(
788
  file   => \"Description\nKaffee",        # " # make emacs happy
789
  case_insensitive_header => 1,
790
  profile => [{
791
    mapping => { description => 'description' },
792
    class  => 'SL::DB::Part'
793
  }],
794
);
795
$csv->parse;
796
is $csv->get_objects->[0]->description, 'Kaffee', 'case insensitive mapping without profile works';
797

  
798
# case insensitive shit
799
$csv = SL::Helper::Csv->new(
800
  file   => \"Price\n4,99",        # " # make emacs happy
801
  case_insensitive_header => 1,
802
  profile => [{
803
    profile => { sellprice => 'sellprice_as_number' },
804
    mapping => { price => 'sellprice' },
805
    class  => 'SL::DB::Part',
806
  }],
807
);
808
$csv->parse;
809
is $csv->get_objects->[0]->sellprice, 4.99, 'case insensitive mapping with profile works';
810

  
729 811

  
730 812
# vim: ft=perl
731 813
# set emacs to perl mode

Auch abrufbar als: Unified diff