Skip to content
This repository has been archived by the owner on Aug 18, 2020. It is now read-only.

Commit

Permalink
Merge pull request #81 from dadleyy/example-cleanup
Browse files Browse the repository at this point in the history
support for time.Time, refactoring selection api method names + example app updates
  • Loading branch information
dadleyy committed Mar 16, 2018
2 parents 8407d0a + c4211ee commit 01e292d
Show file tree
Hide file tree
Showing 35 changed files with 840 additions and 295 deletions.
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
dist: trusty
addons:
postgresql: "9.6"
language: go
services:
- postgresql
Expand All @@ -16,7 +18,7 @@ script:
- make test
- make test-example
- make example
- pushd ./examples/library && ./library && popd
- pushd ./examples/library && ./library import ./data/demo-import.json && popd
after_success:
- bash <(curl -s https://codecov.io/bash) -f ./coverage.txt
before_deploy:
Expand Down
7 changes: 3 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ GO=go

COMPILE=$(GO) build
LDFLAGS="-s -w"
BUILD_FLAGS=-x -v -ldflags $(LDFLAGS) -a
BUILD_FLAGS=-x -v -ldflags $(LDFLAGS)

GLIDE=glide
VENDOR_DIR=vendor
Expand All @@ -18,7 +18,7 @@ MAIN=$(wildcard ./marlowc/main.go)
BINDATA=go-bindata

GOVER=gover
GOVER_REPORT=coverage.txt
COVERAGE_REPORT=coverage.txt

LIB_DIR=./marlow
SRC_DIR=./marlowc
Expand Down Expand Up @@ -72,7 +72,7 @@ test: $(GO_SRC) $(VENDOR_DIR) $(INTERCHANGE_OBJ) lint
$(CYCLO) $(CYCLO_FLAGS) $(LIB_SRC)
$(MISSPELL) -error $(LIB_SRC) $(MAIN)
$(GO) list -f $(TEST_LIST_FMT) $(LIB_DIR)/... | xargs -L 1 sh -c
$(GOVER) $(LIB_DIR) $(GOVER_REPORT)
$(GOVER) $(LIB_DIR) $(COVERAGE_REPORT)

$(VENDOR_DIR):
$(GO) get -v -u github.com/modocache/gover
Expand Down Expand Up @@ -106,7 +106,6 @@ clean-example:

clean: clean-example
rm -rf $(COVERAGE_REPORT)
rm -rf $(LINT_RESULT)
rm -rf $(VENDOR_DIR)
rm -rf $(EXE)
rm -rf $(DIST_DIR)
Expand Down
10 changes: 10 additions & 0 deletions examples/library/cli/browse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package cli

import "fmt"
import "github.com/dadleyy/marlow/examples/library/models"

// Browse (TODO) provides an interactive session with the underlying database tables + marlow stores.
func Browse(store *models.Stores, args []string) error {
fmt.Printf("starting library browser...\n")
return nil
}
6 changes: 6 additions & 0 deletions examples/library/cli/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package cli

import "github.com/dadleyy/marlow/examples/library/models"

// Command represents a type that used by the example app cli.
type Command func(*models.Stores, []string) error
226 changes: 226 additions & 0 deletions examples/library/cli/import.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
package cli

import "os"
import "fmt"
import "sync"
import "encoding/json"
import "github.com/dadleyy/marlow/examples/library/models"

type deleteModelList struct {
Books []struct {
Title string `json:"title"`
} `json:"books"`
}

type importModelList struct {
Books []*models.Book `json:"books"`
Authors []*models.Author `json:"authors"`
Genres []*models.Genre `json:"genres"`
BookAuthors []struct {
Author string
Book string
} `json:"book_authors"`
GenreTaxonomy []struct {
Child string `json:"name"`
Parent string `json:"parent"`
} `json:"genre_taxonomy"`
}

type genreImportChild struct {
child models.Genre
parent string
}

type importJSONSource struct {
Imports importModelList `json:"imports"`
Deletions deleteModelList `json:"deletions"`
}

// Import is the used by the example app that uses a json file and the generate store interfaces to populate records.
func Import(stores *models.Stores, args []string) error {
if len(args) < 1 {
return fmt.Errorf("must provide a filename")
}

filename := args[0]

if s, e := os.Stat(filename); e != nil || s.IsDir() {
return fmt.Errorf("filename must exist and be a regular file (given %s)", filename)
}

file, e := os.Open(filename)

if e != nil {
return fmt.Errorf("unable to open import file (e %v)", e)
}

defer file.Close()

decoder := json.NewDecoder(file)
var source importJSONSource

createdRecordIds := struct {
authors []int
}{make([]int, 0, len(source.Imports.Authors))}

if e := decoder.Decode(&source); e != nil {
return fmt.Errorf("unable to decode json (e %v)", e)
}

for _, a := range source.Imports.Authors {
fmt.Printf("importing %s...", a)
id, e := stores.Authors.CreateAuthors(*a)

if e != nil {
fmt.Println()
return fmt.Errorf("failed import on %s (e %v)", a, e)
}

createdRecordIds.authors = append(createdRecordIds.authors, int(id))
fmt.Printf(" %d\n", id)
}

fmt.Printf("updating %d authors w/ imported flag... ")
authorbp := &models.AuthorBlueprint{ID: createdRecordIds.authors}

if _, e := stores.Authors.UpdateAuthorAuthorFlags(models.AuthorImported, authorbp); e != nil {
fmt.Printf("failed\n")
return fmt.Errorf("unable to update authors (e %v)", e)
}

fmt.Printf("success\n")

children := make(chan genreImportChild)
wg := &sync.WaitGroup{}
wg.Add(1)

go func() {
pending := make([]genreImportChild, 0, len(source.Imports.Genres))

for g := range children {
pending = append(pending, g)
}

for _, g := range pending {
matches, e := stores.Genres.SelectGenreIDs(&models.GenreBlueprint{Name: []string{g.parent}})

if e != nil || len(matches) != 1 {
fmt.Printf("unable to create genre %s, cant find parent %s (e %v)", g.child.Name, g.parent, e)
continue
}

if e := g.child.ParentID.Scan(matches[0]); e != nil {
fmt.Printf("unable to create genre %s (e %v)", g.child.Name, e)
continue
}

fmt.Printf("importing %s... ", g.child.Name)
id, e := stores.Genres.CreateGenres(g.child)

if e != nil {
fmt.Printf("unable to create genre %s (e %v)\n", g.child.Name, e)
continue
}

fmt.Printf("%d\n", id)
}

wg.Done()
}()

for _, g := range source.Imports.Genres {
parent := ""
fmt.Printf("importing %s...", g)

for _, t := range source.Imports.GenreTaxonomy {
if t.Child != g.Name {
continue
}

parent = t.Parent
children <- genreImportChild{child: *g, parent: parent}
break
}

if parent != "" {
fmt.Printf("%s was child of %s, delaying creation\n", g.Name, parent)
continue
}

id, e := stores.Genres.CreateGenres(*g)

if e != nil {
return fmt.Errorf("failed import on genre %s (e %v)", g, e)
}

fmt.Printf(" %d\n", id)
}

close(children)
wg.Wait()

for _, b := range source.Imports.Books {
var authorName string

for _, ba := range source.Imports.BookAuthors {
if ba.Book == b.Title {
authorName = ba.Author
}
}

if authorName == "" {
fmt.Printf("skipping book: \"%s\", no author found\n", b.Title)
continue
}

aid, e := stores.Authors.SelectAuthorIDs(&models.AuthorBlueprint{
Name: []string{authorName},
})

if e != nil || len(aid) != 1 {
return fmt.Errorf("failed import on book author lookup - found %d (e %v)", len(aid), e)
}

fmt.Printf("creating book %s... ", b)

b.AuthorID = aid[0]

id, e := stores.Books.CreateBooks(*b)

if e != nil {
fmt.Println()
return fmt.Errorf("failed import on book create (e %v)", e)
}

fmt.Printf("%d\n", id)
}

for _, b := range source.Deletions.Books {
blueprint := &models.BookBlueprint{Title: []string{b.Title}}
_, e := stores.Books.DeleteBooks(blueprint)

if e != nil {
return fmt.Errorf("unable to delete requested books (e %s)", e.Error())
}
}

counts := struct {
authors int
genres int
}{}

counts.authors, e = stores.Authors.CountAuthors(nil)

if e != nil {
return fmt.Errorf("unable to get import summary (e %v)", e)
}

counts.genres, e = stores.Genres.CountGenres(nil)

if e != nil {
return fmt.Errorf("unable to get import summary (e %v)", e)
}

fmt.Println(fmt.Sprintf("import summary: %d authors, %d genres", counts.authors, counts.genres))
return nil
}
54 changes: 54 additions & 0 deletions examples/library/data/demo-import.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"deletions": {
"books": [{
"title": "__testing"
}]
},
"imports": {
"books": [{
"title": "Crime and Punishment",
"year_published": 1866
}, {
"title": "Harry Potter and the Philosopher's Stone",
"year_published": 1998
}, {
"title": "A Time to Kill",
"year_published": 1988
}, {
"title": "The Fountainhead",
"year_published": 1943
}],
"book_authors": [{
"author": "Fyodor Dostoevsky",
"book": "Crime and Punishment"
}],
"authors": [{
"name": "Fyodor Dostoevsky",
"birthday": "1821-11-11T00:00:00Z"
}, {
"name": "John Grisham",
"birthday": "1955-02-08T00:00:00Z"
}, {
"name": "J.K Rowling",
"birthday": "1965-07-31T00:00:00Z"
}, {
"name": "Ayn Rand",
"birthday": "1905-02-02T00:00:00Z"
}],
"genre_taxonomy": [{
"name": "Science Fiction",
"parent": "Fiction"
}],
"genres": [{
"name": "Literature"
}, {
"name": "Fiction"
}, {
"name": "Science Fiction"
}, {
"name": "Drama"
}, {
"name": "Crime"
}]
}
}
3 changes: 2 additions & 1 deletion examples/library/data/postgres.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ drop table if exists genres;
create table genres (
id SERIAL,
name TEXT,
parent_id INTEGER
parent_id INTEGER,
genre_references jsonb
);

drop table if exists multi_auto;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ create table authors (
name TEXT,
university_id INTEGER,
rating REAL NOT NULL DEFAULT '100.00',
flags INTEGER NOT NULL DEFAULT 0
flags INTEGER NOT NULL DEFAULT 0,
birthday Date NOT NULL
);

drop table if exists books;
Expand All @@ -15,5 +16,5 @@ create table books (
title TEXT,
author INTEGER NOT NULL,
series INTEGER,
page_count INTEGER NOT NULL
year_published INTEGER NOT NULL
);
Loading

0 comments on commit 01e292d

Please sign in to comment.