This repository has been archived by the owner on Aug 18, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #81 from dadleyy/example-cleanup
support for time.Time, refactoring selection api method names + example app updates
- Loading branch information
Showing
35 changed files
with
840 additions
and
295 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
}] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.