-
Notifications
You must be signed in to change notification settings - Fork 107
[WIP] Download annotated notebooks #34
Changes from 37 commits
713b03f
1660c61
b23b692
cc06bd9
f967740
01cf2b4
09d6c6d
9e48489
68ebecd
4038663
48f65d1
ff515f6
2417149
238f38c
5d635b3
6aea236
d78292c
1f257b2
c29be12
94d1be9
b805445
f3d4542
a57cbaa
ba6d0a5
5662904
3b978a1
ac4b5c0
a45c4fd
982d8f2
cb7771f
cbc6c0c
b09390d
2315caa
c6b6aa0
0dfee84
43bc787
f872dfd
0c857d9
ea56392
e434018
9fbfb30
b36b097
964ed36
3ca7ec1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
package shell | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"time" | ||
"os/exec" | ||
"strings" | ||
"archive/zip" | ||
"os" | ||
"io" | ||
"github.com/abiosoft/ishell" | ||
"path/filepath" | ||
"github.com/juruen/rmapi/model" | ||
) | ||
|
||
|
||
func getaCmd(ctx *ShellCtxt) *ishell.Cmd { | ||
return &ishell.Cmd{ | ||
Name: "geta", | ||
Help: "copy remote file to local with annotations. Optional [svg, pdf]", | ||
Completer: createEntryCompleter(ctx), | ||
Func: func(c *ishell.Context) { | ||
// Parse cmd args | ||
if len(c.Args) == 0 { | ||
c.Err(errors.New("missing source file")) | ||
return | ||
} | ||
srcName := c.Args[0] | ||
|
||
// Download document as zip | ||
node, err := ctx.api.Filetree.NodeByPath(srcName, ctx.node) | ||
if err != nil || node.IsDirectory() { | ||
c.Err(errors.New("file doesn't exist")) | ||
return | ||
} | ||
|
||
err = getAnnotatedDocument(ctx, node, "") | ||
if err != nil { | ||
c.Err(err) | ||
return | ||
} | ||
}, | ||
} | ||
} | ||
|
||
|
||
func getAnnotatedDocument(ctx *ShellCtxt, node *model.Node, path string) error { | ||
zipFile := fmt.Sprintf(".%s.zip", node.Name()) | ||
|
||
// Set output name and output file name | ||
output := node.Name() | ||
if(path != ""){ | ||
output = fmt.Sprintf("%s/%s", path, node.Name()) | ||
} | ||
outputFileName := fmt.Sprintf("%s.pdf", output) | ||
|
||
// Parse last modified time of document on api | ||
modifiedClientTime, err := time.Parse(time.RFC3339Nano, node.Document.ModifiedClient) | ||
if err != nil { | ||
// If we could not parse the time correctly, we still continue | ||
// with the execution such that the pdf is downloaded... | ||
fmt.Println(err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you please use the logging facilities instead of standard output/err? Something like:
or in this case, if this is just a warning:
In general, we should try to use the log facilities instead of |
||
fmt.Println("(Warning) Could not parse modified time. Overwrite existing file.") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. |
||
modifiedClientTime = time.Now().Local() | ||
} | ||
|
||
// If document has not changed since last update skip pdf convertion | ||
outputFile, err := os.Stat(outputFileName) | ||
if !os.IsNotExist(err) { | ||
outputFileModTime := outputFile.ModTime() | ||
if(outputFileModTime.Equal(modifiedClientTime)){ | ||
fmt.Println("Nothing changed since last download. Skip. ") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. This looks like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for your comments => fixed all invalid log statements |
||
os.Remove(zipFile) | ||
return nil | ||
} | ||
} | ||
|
||
// Download document if content has changed | ||
err = ctx.api.FetchDocument(node.Document.ID, zipFile) | ||
if err != nil { | ||
return errors.New(fmt.Sprintf("Failed to download file %s with %s", node.Name(), err.Error())) | ||
} | ||
|
||
// Unzip document | ||
tmpFolder := fmt.Sprintf(".%s", node.Document.ID) | ||
_, err = unzip(zipFile, tmpFolder) | ||
if err != nil { | ||
os.Remove(zipFile) | ||
return err | ||
} | ||
|
||
// Convert to pdf | ||
exportPdf := os.Getenv("GOPATH") + "/src/github.com/juruen/rmapi/tools/exportAnnotatedPdf" | ||
rM2svg := os.Getenv("GOPATH") + "/src/github.com/juruen/rmapi/tools/rM2svg" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about we create a file/function where we encapsulate checks to verify if the required tools are installed. We can then use it in https://github.com/juruen/rmapi/blob/master/shell/shell.go#L54 to optinally add these new commands to the shell. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good. I will fix a bug that I found right now and afterwards I will start with this verification |
||
_, err = exec.Command( | ||
"/bin/bash", | ||
exportPdf, | ||
tmpFolder, | ||
node.Document.ID, | ||
output, | ||
rM2svg).CombinedOutput() | ||
|
||
if err != nil { | ||
fmt.Println(err) | ||
} | ||
|
||
if err != nil { | ||
os.Remove(zipFile) | ||
os.RemoveAll(tmpFolder) | ||
return err | ||
} | ||
|
||
// Set creation time | ||
err = os.Chtimes(outputFileName, modifiedClientTime, modifiedClientTime) | ||
if err != nil { | ||
fmt.Println("(Warning) Could not set modified time of pdf file.") | ||
} | ||
|
||
// Cleanup | ||
os.Remove(zipFile) | ||
os.RemoveAll(tmpFolder) | ||
fmt.Println("Ok.") | ||
return nil | ||
} | ||
|
||
// From https://golangcode.com/unzip-files-in-go/ | ||
func unzip(src string, dest string) ([]string, error) { | ||
|
||
var filenames []string | ||
|
||
r, err := zip.OpenReader(src) | ||
if err != nil { | ||
return filenames, err | ||
} | ||
defer r.Close() | ||
|
||
for _, f := range r.File { | ||
|
||
rc, err := f.Open() | ||
if err != nil { | ||
return filenames, err | ||
} | ||
defer rc.Close() | ||
|
||
// Store filename/path for returning and using later on | ||
fpath := filepath.Join(dest, f.Name) | ||
|
||
// Check for ZipSlip. More Info: http://bit.ly/2MsjAWE | ||
if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) { | ||
return filenames, fmt.Errorf("%s: illegal file path", fpath) | ||
} | ||
|
||
filenames = append(filenames, fpath) | ||
|
||
if f.FileInfo().IsDir() { | ||
|
||
// Make Folder | ||
os.MkdirAll(fpath, os.ModePerm) | ||
|
||
} else { | ||
|
||
// Make File | ||
if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil { | ||
return filenames, err | ||
} | ||
|
||
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) | ||
if err != nil { | ||
return filenames, err | ||
} | ||
|
||
_, err = io.Copy(outFile, rc) | ||
|
||
// Close the file without defer to close before next iteration of loop | ||
outFile.Close() | ||
|
||
if err != nil { | ||
return filenames, err | ||
} | ||
|
||
} | ||
} | ||
return filenames, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package shell | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"os" | ||
"path" | ||
|
||
"github.com/abiosoft/ishell" | ||
"github.com/juruen/rmapi/filetree" | ||
"github.com/juruen/rmapi/model" | ||
) | ||
|
||
func mgetaCmd(ctx *ShellCtxt) *ishell.Cmd { | ||
return &ishell.Cmd{ | ||
Name: "mgeta", | ||
Help: "recursively copy remote directory to local with annotations", | ||
Completer: createDirCompleter(ctx), | ||
Func: func(c *ishell.Context) { | ||
if len(c.Args) == 0 { | ||
c.Err(errors.New(("missing source dir"))) | ||
return | ||
} | ||
|
||
srcName := c.Args[0] | ||
|
||
node, err := ctx.api.Filetree.NodeByPath(srcName, ctx.node) | ||
|
||
if err != nil || node.IsFile() { | ||
c.Err(errors.New("directory doesn't exist")) | ||
return | ||
} | ||
|
||
visitor := filetree.FileTreeVistor{ | ||
func(currentNode *model.Node, currentPath []string) bool { | ||
idxDir := 0 | ||
if srcName == "." && len(currentPath) > 0 { | ||
idxDir = 1 | ||
} | ||
|
||
dst := "./" + filetree.BuildPath(currentPath[idxDir:], currentNode.Name()) | ||
dir := path.Dir(dst) | ||
os.MkdirAll(dir, 0766) | ||
|
||
if currentNode.IsDirectory() { | ||
return filetree.ContinueVisiting | ||
} | ||
|
||
c.Printf("downloading [%s]...", dst) | ||
|
||
err = getAnnotatedDocument(ctx, currentNode, fmt.Sprintf("%s", dir)) | ||
|
||
if err == nil { | ||
return filetree.ContinueVisiting | ||
} | ||
|
||
c.Err(errors.New(fmt.Sprintf("Failed to downlaod file %s", currentNode.Name()))) | ||
|
||
return filetree.ContinueVisiting | ||
}, | ||
} | ||
|
||
filetree.WalkTree(node, visitor) | ||
}, | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can drop this line :)