Anki-Import

 view release on metacpan or  search on metacpan

lib/Anki/Import.pm  view on Meta::CPAN

# the meat of the matter
# TODO: break up into shorter functions for readability
sub process_note {
  my $note = shift; logd($note, 'note_2b_processed');

  my @fields = ();
  my $new_autotags = 0; # flag raised if autotag line found

  # loop over note fields
  foreach my $field (@$note) {
    my $ws_mode = 0;   # tracks if we are preserving whitespace
    my $field_out = '';

    # loop over lines in field and process accordingly
    my @lines = (''); # can't take a reference to nothing
    foreach my $line (@$field) {
      my $last_line = \$lines[-1]; # just to make it easier to type

      # detect autotags
      logd($line);
      if ($line =~ /^\+\s*$/ && !$ws_mode) {
        push @autotags, split (/\s+/, $$last_line);
        $new_autotags = 1;
      }
      if ($line =~ /^\^\s*$/ && !$ws_mode) {
        @autotags = split (/\s+/, $$last_line);
        $new_autotags = 1;
        next;
      }

      # blanks lines not in non-whitespace mode
      if ($line =~ /^`\s*$/ && !$ws_mode) {
        if ($$last_line && $$last_line !~ /^<br>+$/) {
          $$last_line .= '<br><br>';
        }
        next;
      }

      # enter whitespace mode and adding appropriate HTML
      if ($line =~ /^`{3,3}$/ && !$ws_mode) {
        $ws_mode = 1;

        # add a couple of blank lines to previous line
        if ($$last_line) {
          $$last_line .= '<br><br>';
        }

        $$last_line .= '<div style="text-align: left; font-family: courier; white-space: pre;">';
        next;
      }

      # exit whitespace mode, close out HTML, add blank lines
      if ($line =~ /^`{3,3}$/ && $ws_mode) {
        $ws_mode = 0;
        $$last_line .= "</div><br><br>";
        next;
      }

      # handle lines differently based on if we are preserving whitespace
      if ($ws_mode) {
        # escape characters in preserved text
        if ($line =~ /^`\s*$/) {
          $$last_line .= '<br>';
          next;
        }
        $line =~ s/(?<!\\)`/\\`/g;
        $line =~ s/(?<!\\)\*/\\*/g;
        $line =~ s/(?<!\\)%/\\%/g;
        $$last_line .= $line . "<br>";
      } else {
        push @lines, $line;
      }
    }
    logf('A set of backticks (```) is unmatched or you failed to backtick a'
         . ' blank line inside of a backtick set. Please correct the source'
         . ' file and try again. Run "perldoc Anki::Import" for more help.') if $ws_mode;

    logd($field_out, 'field_out');

    shift @lines if !$lines[0];
    my $field = join ' ', @lines;

    # clean up dangling breaks
    $field =~ s/<br><\/div>/<\/div>/g;

    # handle formatting codes in text, preserve escaped characters

    # preserve angle brackets between backticks
    my $parts = [ split /[^\\]`|^`/, $field, -1];

    my $count = 0;
    foreach my $part (@$parts) {
      $count++;
      next if ($count % 2);  # only substitute on odd number array items
      $part =~ s/</&lt;/g;
    }

    $field = join '`', @$parts;

    # backticked characters
    $field =~ s/(?<!\\)`(.*?)`/<span style="font-family: courier; weight: bold;">$1<\/span>/gm;
    $field =~ s/\\`/`/g;

    # bold
    $field =~ s/(?<!\\)\*(.*?)\*/<span style="weight: bold;">$1<\/span>/gm;
    $field =~ s/\\\*/*/g;

    # unordered lists
    $field =~ s'(?<!\\)%(.*?)%'"<ul><li>" . join ("</li><li>", (split (/,\s*/, $1))) . "</li><\/ul>"'gme;
    $field =~ s/\\%/%/g;

    $field =~ s/(<br>)+$//;
    push @fields, $field;

  }

  # generate tag field
  if (@autotags && !$new_autotags) {

    # get tags from tag field
    my @note_tags = split (/\s+/, $fields[-1]); logd(\@note_tags, 'raw_note_tags');
    my @new_tags = ();

    # add tags from tag field
    foreach my $note_tag (@note_tags) {
      my $in_autotags = grep { $_ eq $note_tag } @autotags;
      push @new_tags, $note_tag unless $in_autotags;
    }

    # add autotags
    foreach my $autotag (@autotags) {
      my $discard_autotag = grep { $_ eq $autotag } @note_tags;
      push @new_tags, $autotag if !$discard_autotag;
    }

    # add combined tags as a field
    logd(\@new_tags, 'new_tags');
    my $new_tags = join (' ', @new_tags);
    $fields[-1] = $new_tags;
  }
  $new_autotags = 0;

  my $out = join ("\t", @fields);

  # create cloze fields
  my $cloze_count = 1;
  # TODO: should probably handle escaped braces just in case
  while ($out =~ /\{\{\{(.*?)}}}/) {
    $out =~ s/\{\{\{(.*?)}}}/{{c${cloze_count}::$1}}/s;
    $cloze_count++;
  }
  logd($out, 'out');

  $out .= "\n";
}

1; # Magic true value
# ABSTRACT: Anki note generation made easy.

__END__

=pod

=head1 NAME

Anki::Import - Anki note generation made easy.

=head1 VERSION

version 0.030

=head1 OVERVIEW

Efficiently generate formatted Anki notes with your
text editor for easy import into Anki.

=head1 SYNOPSIS

    # Step 1: Create the source file

    # Step 2: Run the anki_import command
      supplied by this module...

    # ...from the command line
    anki_import path/to/source_file.txt

    # or

    # ...from within a perl script
    use Anki::Import;
    anki_import('path/to/source_file.txt');

    # Step 3: Import the resultant files into Anki

=head1 DESCRIPTION

Inputting notes into Anki can be a tedious chore. C<Anki::Import> lets you
you generate Anki notes with your favorite text editor (e.g. vim, BBEdit, Atom,
etc.) so you can enter formatted notes into Anki's database more efficiently.

At a minimum, you should have basic familiarity with using your computer's
command line terminal to make use of this program.

=head2 Steps for creating, processing and imorting new notes

=head3 Step 1: Generate the notes with your text editor



( run in 0.741 second using v1.01-cache-2.11-cpan-39bf76dae61 )