Skip to content

Commit

Permalink
Merge pull request #7 from alanxoc3/dev
Browse files Browse the repository at this point in the history
GH-4 Multiple Sides for cards
  • Loading branch information
alanxoc3 authored Jun 27, 2020
2 parents c9fb153 + c0dc69b commit 23e574b
Show file tree
Hide file tree
Showing 12 changed files with 198 additions and 118 deletions.
15 changes: 2 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,22 +56,11 @@ concards README.md
```

## All Special Tokens
All the special tokens that are a part of concards syntax are below. Just add
"@" signs to escape them!
All the special tokens that are a part of concards syntax are below.
```
@> = Starts a concards block. Starts a question.
<@ = Ends the concards block.
@ = Separates answers.
@@> = "@>"
<@@ = "<@"
@@ = "@"
@@@> = "@@>"
<@@@ = "<@@"
@@@ = "@@"
...
```

## Advanced Usage
Expand All @@ -85,7 +74,7 @@ Here is an example meta-data file:

Here is the same file, but annotated:
```
sha256sum | review timestamp | streak | alg | data
sha256sum cut in half | review timestamp | streak | alg | data
---------------------------------+-----------------------+--------+-----+-----
3dda75cb44ed447186834541475f32e2 | 2019-01-01T00:00:00Z | 0 | sm2 | 2.5
8525b45f883c05eec46b4f7a88e7f7ef | 2020-01-01T00:00:00Z | 0 | sm2 | 2.5
Expand Down
89 changes: 68 additions & 21 deletions core/card.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,67 @@ package core

import (
"strings"
"bufio"
"fmt"

"crypto/sha256"
)

// A card is a list of facts. Usually, but not limited to, Q&A format.
type Card struct {
File string
Facts []string
file string
facts [][]string
}

// Assumes a "cleaned" file string.
func NewCard(facts [][]string, file string) (*Card, error) {
c := Card{}
c.File = file
for _, x := range facts {
if len(x) > 0 {
c.Facts = append(c.Facts, strings.Join(x, " "))
func NewCard(file string, sides string) (*Card, error) {
fact := []string{}
facts := [][]string{}

scanner := bufio.NewScanner(strings.NewReader(sides))
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
t := scanner.Text()
if t == "@" {
if len(fact) > 0 {
facts = append(facts, fact)
fact = []string{}
}
} else if len(t) > 0 {
fact = append(fact, t)
}
}
}

if len(fact) > 0 {
facts = append(facts, fact)
}

if len(c.Facts) > 0 {
return &c, nil
if len(facts) > 0 {
return &Card{file, facts}, nil
} else {
return nil, fmt.Errorf("Question not provided.")
}
}

func (c *Card) HasAnswer() bool {
return len(c.Facts) > 1
func (c *Card) GetSubCards() []*Card {
sub_cards := []*Card{}
question := c.GetQuestion()
answers := c.GetFacts()[1:]
for _, answer := range answers {
if sc, err := NewCard(c.file, answer + " @ " + question); err == nil {
sub_cards = append(sub_cards, sc)
} else {
panic("Error: Sub card was not created due to bad parent card. This is a logic error and should be fixed.")
}
}
return sub_cards
}

func (c *Card) GetQuestion() string {
if len(c.Facts) > 0 {
return c.Facts[0]
} else {
return ""
}
func (c *Card) HasAnswer() bool {
return len(c.facts) > 1
}

func (c *Card) String() string {
return strings.Join(c.Facts, " @ ")
return strings.Join(c.GetFacts(), " @ ")
}

func (c *Card) Hash() [sha256.Size]byte {
Expand All @@ -53,3 +72,31 @@ func (c *Card) Hash() [sha256.Size]byte {
func (c *Card) HashStr() string {
return fmt.Sprintf("%x", c.Hash())[:32]
}

func (c *Card) Len() int {
return len(c.facts)
}

func (c *Card) GetFact(i int) string {
if len(c.facts) > i {
return strings.Join(c.facts[i], " ")
} else {
return ""
}
}

func (c *Card) GetQuestion() string {
return c.GetFact(0)
}

func (c *Card) GetFacts() []string {
facts := []string{}
for i, _ := range c.facts {
facts = append(facts, c.GetFact(i))
}
return facts
}

func (c *Card) GetFile() string {
return c.file
}
63 changes: 37 additions & 26 deletions core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,11 @@ import "fmt"
const fullSum = "c6cd355e32654cb4ba506b529ff32288971420ead2e36fdc69e802e9e7510315"
const halfSum = "c6cd355e32654cb4ba506b529ff32288"

var f1 = [][]string{
{"hello", "there"},
{"i'm", "a", "beard"},
}

var f2 = [][]string{
{"hello"},
}

var f3 = [][]string{
{"i'm", "um"},
{"hello"},
}

var f4 = [][]string{
{"alan", "the", "great"},
{"sy", "shoe", "yu"},
}
var f1 = "hello there @ i'm a beard"
var f2 = "hello"
var f3 = "i'm um @ hello"
var f4 = "alan the great @ sy shoe yu"
var f5 = "a @ b @ c @ d e @ f @ @ g"

func TestMeta(t *testing.T) {
a := NewMeta("2020-01-01T00:00:00Z", "0", "sm2", []string{"2.5"})
Expand All @@ -42,7 +29,7 @@ func TestParse(t *testing.T) {
}

func TestCard(t *testing.T) {
c, err := NewCard(f1, "")
c, err := NewCard("", f1)
if err != nil { t.FailNow() }

txt := c.String()
Expand All @@ -57,7 +44,7 @@ func TestCard(t *testing.T) {

func TestDeck(t *testing.T) {
d := NewDeck()
d.AddFacts(f1, "afile")
d.AddCardFromSides("afile", f1, false)
if d.GetCard(0).GetQuestion() != "hello there" { t.Fail() }
if !d.GetCard(0).HasAnswer() { t.Fail() }
if d.GetMeta(0) != nil { t.Fail() }
Expand All @@ -73,9 +60,9 @@ func TestDeck(t *testing.T) {
d.Forget(0)
if d.GetMeta(0) != nil { t.Fail() }
if !d.GetCard(0).HasAnswer() { t.Fail() }
d.AddFacts(f1, "nofile")
d.AddCardFromSides("nofile", f1, false)
if d.Len() != 1 { t.Fail() }
if d.GetCard(0).File != "afile" { t.Fail() }
if d.GetCard(0).GetFile() != "afile" { t.Fail() }
d.FilterOutFile("nofile")
if d.Len() != 1 { t.Fail() }
d.FilterOutFile("afile")
Expand All @@ -86,10 +73,10 @@ func TestDeck(t *testing.T) {

func TestDeckMove(t *testing.T) {
d := NewDeck()
d.AddFacts(f1, "afile")
d.AddFacts(f2, "afile")
d.AddFacts(f3, "afile")
d.AddFacts(f4, "afile")
d.AddCardFromSides("afile", f1, false)
d.AddCardFromSides("afile", f2, false)
d.AddCardFromSides("afile", f3, false)
d.AddCardFromSides("afile", f4, false)
d.TopToEnd()
if d.TopCard().GetQuestion() != "hello" { panic("Bad moves") }
if d.Len() != 4 { panic("Bad len") }
Expand All @@ -104,3 +91,27 @@ func TestDeckMove(t *testing.T) {
d.TopToEnd()
if d.TopCard().GetQuestion() != "hello there" { panic("Bad moves") }
}

func TestAddSubCards(t *testing.T) {
d := NewDeck()
d.AddCardFromSides("dat-file", f5, true)

if d.TopCard().GetQuestion() != "a" { panic("Subcards were inserted before the parent card.") }
if d.Len() != 6 { panic("Wrong number of sub cards inserted.") }
d.DelTop()
if d.TopCard().GetQuestion() != "b" { panic("Second card should be the first sub card.") }
if d.TopCard().GetFact(1) != "a" { panic("Answer isn't the parent card.") }
if d.Len() != 5 { panic("Delete didn't work.") }

if d.GetCard(1).GetQuestion() != "c" { panic("Sub cards not inserted in the correct order.") }
if d.GetCard(1).GetFact(1) != "a" { panic("Sub card doesn't have parent as the answer.") }

if d.GetCard(2).GetQuestion() != "d e" { panic("Sub cards not inserted in the correct order.") }
if d.GetCard(2).GetFact(1) != "a" { panic("Sub card doesn't have parent as the answer.") }

if d.GetCard(3).GetQuestion() != "f" { panic("Sub cards not inserted in the correct order.") }
if d.GetCard(3).GetFact(1) != "a" { panic("Sub card doesn't have parent as the answer.") }

if d.GetCard(4).GetQuestion() != "g" { panic("Sub cards not inserted in the correct order.") }
if d.GetCard(4).GetFact(1) != "a" { panic("Sub card doesn't have parent as the answer.") }
}
21 changes: 16 additions & 5 deletions core/deck.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ func (d *Deck) AddCard(c *Card) error {
}
}

// TODO: Error handling here.
func (d *Deck) InsertCard(c *Card, i int) error {
hash := c.HashStr()
_, exists := d.Cmap[hash]
Expand All @@ -71,11 +70,23 @@ func (d *Deck) InsertCard(c *Card, i int) error {
}
}

func (d *Deck) AddFacts(facts [][]string, file string) error {
if c, err := NewCard(facts, file); err == nil {
return d.AddCard(c)
func (d *Deck) AddCardFromSides(file string, sides string, include_sides bool) []error {
errors := []error{}
if c, create_err := NewCard(file, sides); create_err == nil {
cards := []*Card{c}
if include_sides {
cards = append(cards, c.GetSubCards()...)
}

for _, c := range cards {
if add_err := d.AddCard(c); add_err != nil {
errors = append(errors, add_err)
}
}
} else {
errors = append(errors, create_err)
}
return nil
return errors
}

func (d *Deck) AddMeta(h string, m *Meta) {
Expand Down
4 changes: 2 additions & 2 deletions core/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func (d *Deck) FilterNumber(param int) {
func (d *Deck) FileIntersection(path string, other_deck *Deck) {
d.filter(func(i int) bool {
_, contains := other_deck.Cmap[d.refs[i]]
return d.GetCard(i).File == path && !contains
return d.GetCard(i).GetFile() == path && !contains
})
}

Expand All @@ -35,7 +35,7 @@ func (d *Deck) OuterLeftJoin(other_deck *Deck) {

func (d *Deck) FilterOutFile(path string) {
d.filter(func(i int) bool {
return d.GetCard(i).File == path
return d.GetCard(i).GetFile() == path
})
}

Expand Down
27 changes: 17 additions & 10 deletions file/argparse.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Config struct {
IsReview bool
IsMemorize bool
IsDone bool
IsSides bool
IsPrint bool
IsStream bool

Expand Down Expand Up @@ -57,28 +58,33 @@ func GenConfig() *Config {
parser := argparse.NewParser("concards", "Concards is a simple CLI based SRS flashcard app.")

// Create flags
f_version := parser.Flag("V", "version", &argparse.Options{Help: "Concards build information."})
f_review := parser.Flag("r", "review", &argparse.Options{Help: "Show cards available to be reviewed"})
f_memorize := parser.Flag("m", "memorize", &argparse.Options{Help: "Show cards available to be memorized"})
f_done := parser.Flag("d", "done", &argparse.Options{Help: "Show cards not available to be reviewed or memorized"})
f_print := parser.Flag("p", "print", &argparse.Options{Help: "Prints all cards, one line per card"})
f_number := parser.Int("n", "number", &argparse.Options{Default: 0, Help: "Limit the number of cards in the program to \"#\""})
f_editor := parser.String("E", "editor", &argparse.Options{Default: getDefaultEditor(), Help: "Which editor to use. Defaults to \"$EDITOR\""})
f_meta := parser.String("M", "meta", &argparse.Options{Default: getDefaultMeta(), Help: "Path to meta file. Defaults to \"$CONCARDS_META\" or \"~/.concards-meta\""})
f_version := parser.Flag("V", "version", &argparse.Options{Help: "Concards build information."})
f_review := parser.Flag("r", "review", &argparse.Options{Help: "Show cards available to be reviewed."})
f_memorize := parser.Flag("m", "memorize", &argparse.Options{Help: "Show cards available to be memorized."})
f_done := parser.Flag("d", "done", &argparse.Options{Help: "Show cards not available to be reviewed or memorized."})
f_sides := parser.Flag("s", "sides", &argparse.Options{Help: "Add cards for all sides."})
f_print := parser.Flag("p", "print", &argparse.Options{Help: "Prints all cards, one line per card."})
f_number := parser.Int("n", "number", &argparse.Options{Default: 0, Help: "How many cards to review."})
f_editor := parser.String("E", "editor", &argparse.Options{Default: getDefaultEditor(), Help: "Which editor to use. Defaults to \"$EDITOR\""})
f_meta := parser.String("M", "meta", &argparse.Options{Default: getDefaultMeta(), Help: "Path to meta file. Defaults to \"$CONCARDS_META\" or \"~/.concards-meta\""})

parser.HelpFunc = func(c *argparse.Command, msg interface{}) string {
var help string
help += fmt.Sprintf("%s\n\nUsage:\n %s [OPTIONS]... [FILE|FOLDER]...\n\nOptions:\n", c.GetDescription(), c.GetName())

for _, arg := range c.GetArgs() {
help += fmt.Sprintf(" -%s --%-9s %s.\n", arg.GetSname(), arg.GetLname(), arg.GetOpts().Help)
if arg.IsFlag() {
help += fmt.Sprintf(" -%s --%-9s %s.\n", arg.GetSname(), arg.GetLname(), arg.GetOpts().Help)
} else {
help += fmt.Sprintf(" -%s --%-9s %s.\n", arg.GetSname(), arg.GetLname() + " " + arg.GetSname(), arg.GetOpts().Help)
}
}

return help
}

// Parse input
files, err := parser.Parse(os.Args)
files, err := parser.ParseReturnArguments(os.Args)
if err != nil {
fmt.Print(parser.Help(nil))
os.Exit(1)
Expand All @@ -94,6 +100,7 @@ func GenConfig() *Config {
c.IsReview = *f_review
c.IsMemorize = *f_memorize
c.IsDone = *f_done
c.IsSides = *f_sides
c.IsPrint = *f_print
c.IsStream = false

Expand Down
Loading

0 comments on commit 23e574b

Please sign in to comment.