<commit_before>package main

import (
	"errors"
	"fmt"
	"log"
	"os"
	"path/filepath"
)

// Actually processes a file that's in the new folder.
func processTV(folder string, file string, paths Paths, config Config) error {
	inPath := filepath.Join(folder, file)
	log.Println("Processing", file)

	// Parse the title.
	showTitleFromFile, seasonNumber, episodeNumber, err := showSeasonEpisodeFromFile(file)
	if err != nil {

		// Try to guess the season/ep if it's eg `Some TV Show - Episode Name.vob` format.
		guessErr := tvEpisodeGuess(folder, file, paths, config)
		if guessErr == nil {
			// Succeeded in making a guess! Now skip this file because it's been renamed and the user must confirm.
			return nil
		} else {
			log.Println("Couldn't guess the episode", guessErr)
			log.Println("Failed to parse season/episode for", file)
			failedPath := filepath.Join(paths.Failed, file) // Move to 'failed'.
			os.Rename(inPath, failedPath)
			return err
		}
	}

	// Get and save the show data. This has to happen for every episode so we can get the proper title name.

	// Search for the id.
	seriesId := tvdbSearchForSeries(showTitleFromFile)
	if seriesId <= 0 {
		log.Println("Could not find TV show for", showTitleFromFile)
		failedPath := filepath.Join(paths.Failed, file) // Move it to 'failed'.
		os.Rename(inPath, failedPath)
		return errors.New("Could not find TV show")
	}

	// Get show details.
	series, seriesErr := tvdbSeriesDetails(seriesId)
	if seriesErr != nil {
		log.Println("Could not get TV show metadata for", showTitleFromFile)
		failedPath := filepath.Join(paths.Failed, file) // Move it to 'failed'.
		os.Rename(inPath, failedPath)
		return seriesErr
	}

	// Find the season id.
	seasonId := 0
	for _, season := range series.Seasons {
		if season.Season == seasonNumber {
			seasonId = season.TVDBID
		}
	}
	if seasonId <= 0 {
		log.Println("Could not find season number", seasonNumber)
		failedPath := filepath.Join(paths.Failed, file) // Move it to 'failed'.
		os.Rename(inPath, failedPath)
		return errors.New("Season number")
	}

	// Get season details.
	season, seasonErr := tvdbSeasonDetails(seriesId, seasonId, seasonNumber)
	if seasonErr != nil {
		log.Println("Could not get season metadata for", showTitleFromFile)
		failedPath := filepath.Join(paths.Failed, file) // Move it to 'failed'.
		os.Rename(inPath, failedPath)
		return seasonErr
	}

	// Find the episode ID.
	episodeId := 0
	for _, episode := range season.Episodes {
		if episode.Episode == episodeNumber {
			episodeId = episode.TVDBID
		}
	}
	if episodeId <= 0 {
		log.Println("Could not find episode id for ", showTitleFromFile)
		failedPath := filepath.Join(paths.Failed, file) // Move it to 'failed'.
		os.Rename(inPath, failedPath)
		return errors.New("Episode number")
	}

	// Get episode details.
	episode, episodeErr := tvdbEpisodeDetails(seriesId, seasonId, seasonNumber, episodeId)
	if episodeErr != nil {
		log.Println("Could not get episode metadata for", showTitleFromFile)
		failedPath := filepath.Join(paths.Failed, file) // Move it to 'failed'.
		os.Rename(inPath, failedPath)
		return episodeErr
	}

	// Write the details.
	showFolder := filepath.Join(paths.TV, sanitiseForFilesystem(series.Name))
	seasonFolder := filepath.Join(showFolder, tvSeasonFolderNameFor(seasonNumber))
	episodeFolder := filepath.Join(seasonFolder, tvFolderNameFor(seasonNumber, episodeNumber, episode.Name))
	os.MkdirAll(episodeFolder, os.ModePerm)
	// TODO something here
	// ioutil.WriteFile(filepath.Join(showFolder, metadataFilename), tmdbSeriesData, os.ModePerm)
	// ioutil.WriteFile(filepath.Join(seasonFolder, metadataFilename), tmdbSeasonData, os.ModePerm)
	// ioutil.WriteFile(filepath.Join(episodeFolder, metadataFilename), tmdbEpisodeData, os.ModePerm)

	// Get pics if needed.
	getTVImageIfNeeded(series.Poster, showFolder, imageFilename)
	getTVImageIfNeeded(series.Art, showFolder, imageBackdropFilename)
	getTVImageIfNeeded(season.Image, seasonFolder, imageFilename)
	getTVImageIfNeeded(episode.Image, episodeFolder, imageFilename)

	// Convert it.
	outPath := filepath.Join(episodeFolder, hlsFilename)
	convertErr := convertToHLSAppropriately(inPath, outPath, config)

	// Fail! Move it to the failed folder.
	if convertErr != nil {
		switch err := convertErr.(type) {
		case *convertRenamedError:
			log.Println("Failed to convert", file, "; file renamed for user intervention, err:", err)
		default:
			log.Println("Failed to convert", file, "; moving to the Failed folder, err:", err)
			failedPath := filepath.Join(paths.Failed, file) // Move it to 'failed'.
			os.Rename(inPath, failedPath)
		}
		os.RemoveAll(episodeFolder) // Tidy up.
		return errors.New("Couldn't convert " + file)
	}

	// Success!
	// Assumption is that the user ripped their original from their DVD so doesn't care to lose it and would prefer to save the space.
	log.Println("Success! Removing original.")
	os.Remove(inPath)

	// Generate metadata.
	generateMetadata(paths)

	return nil
}

func tvSeasonFolderNameFor(season int) string {
	return fmt.Sprintf("Season %d", season)
}

// Makes the folder name for the given show.
func tvFolderNameFor(season int, episode int, title string) string {
	if title == "" {
		return fmt.Sprintf("S%02dE%02d", season, episode)
	} else {
		return fmt.Sprintf("S%02dE%02d %s", season, episode, sanitiseForFilesystem(title))
	}
}
<commit_msg>Write tvdb metadata<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
)

// Actually processes a file that's in the new folder.
func processTV(folder string, file string, paths Paths, config Config) error {
	inPath := filepath.Join(folder, file)
	log.Println("Processing", file)

	// Parse the title.
	showTitleFromFile, seasonNumber, episodeNumber, err := showSeasonEpisodeFromFile(file)
	if err != nil {

		// Try to guess the season/ep if it's eg `Some TV Show - Episode Name.vob` format.
		guessErr := tvEpisodeGuess(folder, file, paths, config)
		if guessErr == nil {
			// Succeeded in making a guess! Now skip this file because it's been renamed and the user must confirm.
			return nil
		} else {
			log.Println("Couldn't guess the episode", guessErr)
			log.Println("Failed to parse season/episode for", file)
			failedPath := filepath.Join(paths.Failed, file) // Move to 'failed'.
			os.Rename(inPath, failedPath)
			return err
		}
	}

	// Get and save the show data. This has to happen for every episode so we can get the proper title name.

	// Search for the id.
	seriesId := tvdbSearchForSeries(showTitleFromFile)
	if seriesId <= 0 {
		log.Println("Could not find TV show for", showTitleFromFile)
		failedPath := filepath.Join(paths.Failed, file) // Move it to 'failed'.
		os.Rename(inPath, failedPath)
		return errors.New("Could not find TV show")
	}

	// Get show details.
	series, seriesErr := tvdbSeriesDetails(seriesId)
	if seriesErr != nil {
		log.Println("Could not get TV show metadata for", showTitleFromFile)
		failedPath := filepath.Join(paths.Failed, file) // Move it to 'failed'.
		os.Rename(inPath, failedPath)
		return seriesErr
	}

	// Find the season id.
	seasonId := 0
	for _, season := range series.Seasons {
		if season.Season == seasonNumber {
			seasonId = season.TVDBID
		}
	}
	if seasonId <= 0 {
		log.Println("Could not find season number", seasonNumber)
		failedPath := filepath.Join(paths.Failed, file) // Move it to 'failed'.
		os.Rename(inPath, failedPath)
		return errors.New("Season number")
	}

	// Get season details.
	season, seasonErr := tvdbSeasonDetails(seriesId, seasonId, seasonNumber)
	if seasonErr != nil {
		log.Println("Could not get season metadata for", showTitleFromFile)
		failedPath := filepath.Join(paths.Failed, file) // Move it to 'failed'.
		os.Rename(inPath, failedPath)
		return seasonErr
	}

	// Find the episode ID.
	episodeId := 0
	for _, episode := range season.Episodes {
		if episode.Episode == episodeNumber {
			episodeId = episode.TVDBID
		}
	}
	if episodeId <= 0 {
		log.Println("Could not find episode id for ", showTitleFromFile)
		failedPath := filepath.Join(paths.Failed, file) // Move it to 'failed'.
		os.Rename(inPath, failedPath)
		return errors.New("Episode number")
	}

	// Get episode details.
	episode, episodeErr := tvdbEpisodeDetails(seriesId, seasonId, seasonNumber, episodeId)
	if episodeErr != nil {
		log.Println("Could not get episode metadata for", showTitleFromFile)
		failedPath := filepath.Join(paths.Failed, file) // Move it to 'failed'.
		os.Rename(inPath, failedPath)
		return episodeErr
	}

	// Write the details.
	showFolder := filepath.Join(paths.TV, sanitiseForFilesystem(series.Name))
	seasonFolder := filepath.Join(showFolder, tvSeasonFolderNameFor(seasonNumber))
	episodeFolder := filepath.Join(seasonFolder, tvFolderNameFor(seasonNumber, episodeNumber, episode.Name))
	os.MkdirAll(episodeFolder, os.ModePerm)
	seriesData, _ := json.Marshal(series)
	seasonData, _ := json.Marshal(season)
	episodeData, _ := json.Marshal(episode)
	ioutil.WriteFile(filepath.Join(showFolder, metadataFilename), seriesData, os.ModePerm)
	ioutil.WriteFile(filepath.Join(seasonFolder, metadataFilename), seasonData, os.ModePerm)
	ioutil.WriteFile(filepath.Join(episodeFolder, metadataFilename), episodeData, os.ModePerm)

	// Get pics if needed.
	getTVImageIfNeeded(series.Poster, showFolder, imageFilename)
	getTVImageIfNeeded(series.Art, showFolder, imageBackdropFilename)
	getTVImageIfNeeded(season.Image, seasonFolder, imageFilename)
	getTVImageIfNeeded(episode.Image, episodeFolder, imageFilename)

	// Convert it.
	outPath := filepath.Join(episodeFolder, hlsFilename)
	convertErr := convertToHLSAppropriately(inPath, outPath, config)

	// Fail! Move it to the failed folder.
	if convertErr != nil {
		switch err := convertErr.(type) {
		case *convertRenamedError:
			log.Println("Failed to convert", file, "; file renamed for user intervention, err:", err)
		default:
			log.Println("Failed to convert", file, "; moving to the Failed folder, err:", err)
			failedPath := filepath.Join(paths.Failed, file) // Move it to 'failed'.
			os.Rename(inPath, failedPath)
		}
		os.RemoveAll(episodeFolder) // Tidy up.
		return errors.New("Couldn't convert " + file)
	}

	// Success!
	// Assumption is that the user ripped their original from their DVD so doesn't care to lose it and would prefer to save the space.
	log.Println("Success! Removing original.")
	os.Remove(inPath)

	// Generate metadata.
	generateMetadata(paths)

	return nil
}

func tvSeasonFolderNameFor(season int) string {
	return fmt.Sprintf("Season %d", season)
}

// Makes the folder name for the given show.
func tvFolderNameFor(season int, episode int, title string) string {
	if title == "" {
		return fmt.Sprintf("S%02dE%02d", season, episode)
	} else {
		return fmt.Sprintf("S%02dE%02d %s", season, episode, sanitiseForFilesystem(title))
	}
}
<|endoftext|>


<commit_before>package tablook

import (
	"errors"

	"github.com/gizak/termui" // for events
	"github.com/mattn/go-runewidth"
	"github.com/nsf/termbox-go"
)

var ErrTermboxAlreadyInitialized = errors.New("tablook: termbox (or termui) already initialized")

const (
	FgColor  = termbox.ColorWhite
	BgColor  = termbox.ColorBlack
	SelColor = termbox.ColorBlue
)

func initTermui() error {
	if termbox.IsInit {
		return ErrTermboxAlreadyInitialized
	}
	return termui.Init()
}

func closeTermui() {
	termui.Close()
}

func (data Tab) Show() {
	if err := initTermui(); err != nil {
		panic(err)
	}
	defer closeTermui()

	width, height := termbox.Size()
	if height < 2 {
		return
	}

	for i := 0; i < height && i < data.rows(); i++ {
		data.drawRow(width, i, i)
	}
	termbox.Flush()

	data.loop()
}

func (data Tab) loop() {
	termui.Handle("/sys/kbd/q", func(termui.Event) {
		termui.StopLoop()
	})
	termui.Handle("/sys/kbd/k", func(termui.Event) {
		if data.selected > 1 {
			data.selected--
			data.redrawTwoRows(data.selected)
		}
	})
	termui.Handle("/sys/kbd/j", func(termui.Event) {
		if data.selected+1 < data.rows() {
			data.selected++
			data.redrawTwoRows(data.selected - 1)
		}
	})
	termui.Loop()
}

func (data Tab) redrawTwoRows(firstIdx int) {
	width, _ := termbox.Size()
	data.drawRow(width, firstIdx, firstIdx)
	data.drawRow(width, firstIdx+1, firstIdx+1)
	termbox.Flush()
}

func (data Tab) drawRow(width, sourceIdx, viewIdx int) {
	column, x := 0, 0
	fg, bg := FgColor, BgColor
	if data.selected == sourceIdx {
		fg = SelColor
	} else if sourceIdx == 0 {
		fg, bg = bg, fg // header
	}

	for x < width && column < data.cols() {
		s := data.records[sourceIdx][column]
		if x+data.maxLen[column] >= width {
			// clap
			s = runewidth.Truncate(s, width-x, "")
			if len(s) == 0 {
				break
			}
		}

		drawString(s, x, viewIdx, fg, bg)
		fg, bg = bg, fg
		x += data.maxLen[column]
		column++
	}
}

func drawString(s string, x, y int, fg, bg termbox.Attribute) {
	for _, ch := range s {
		termbox.SetCell(x, y, ch, fg, bg)
		x += runewidth.RuneWidth(ch)
	}
}
<commit_msg>resize support + arrow keys navigation<commit_after>package tablook

import (
	"errors"

	"github.com/gizak/termui" // for events
	"github.com/mattn/go-runewidth"
	"github.com/nsf/termbox-go"
)

var ErrTermboxAlreadyInitialized = errors.New("tablook: termbox (or termui) already initialized")

const (
	FgColor  = termbox.ColorWhite
	BgColor  = termbox.ColorBlack
	SelColor = termbox.ColorBlue
)

func initTermui() error {
	if termbox.IsInit {
		return ErrTermboxAlreadyInitialized
	}
	return termui.Init()
}

func closeTermui() {
	termui.Close()
}

func (data Tab) Show() {
	if err := initTermui(); err != nil {
		panic(err)
	}
	defer closeTermui()

	data.redraw()
	data.loop()
}

func (data Tab) loop() {
	termui.Handle("/sys/kbd/q", func(termui.Event) {
		termui.StopLoop()
	})
	chRow := func(dir int) func(termui.Event) {
		return func(termui.Event) {
			next := data.selected + dir
			if next != 0 && next < data.rows() {
				data.selected = next
				if dir == 1 {
					next--
				}
				data.redrawTwoRows(next)
			}
		}
	}
	chUp, chDown := chRow(-1), chRow(+1)
	termui.Handle("/sys/kbd/k", chUp)
	termui.Handle("/sys/kbd/j", chDown)
	termui.Handle("/sys/kbd/<down>", chDown)
	termui.Handle("/sys/kbd/<up>", chUp)
	termui.Handle("/sys/wnd/resize", func(termui.Event) {
		data.redraw()
	})
	termui.Loop()
}

func (data Tab) redraw() {
	termbox.Sync()
	termbox.Clear(FgColor, BgColor)
	width, height := termbox.Size()
	if height < 2 {
		drawString("wnd size too small", 0, 0, FgColor, BgColor)
		return
	}

	for i := 0; i < height && i < data.rows(); i++ {
		data.drawRow(width, i, i)
	}
	termbox.Flush()
}

func (data Tab) redrawTwoRows(firstIdx int) {
	width, _ := termbox.Size()
	data.drawRow(width, firstIdx, firstIdx)
	data.drawRow(width, firstIdx+1, firstIdx+1)
	termbox.Flush()
}

func (data Tab) drawRow(width, sourceIdx, viewIdx int) {
	column, x := 0, 0
	fg, bg := FgColor, BgColor
	if data.selected == sourceIdx {
		fg = SelColor
	} else if sourceIdx == 0 {
		fg, bg = bg, fg // header
	}

	for x < width && column < data.cols() {
		s := data.records[sourceIdx][column]
		if x+data.maxLen[column] >= width {
			// clap
			s = runewidth.Truncate(s, width-x, "")
			if len(s) == 0 {
				break
			}
		}

		drawString(s, x, viewIdx, fg, bg)
		fg, bg = bg, fg
		x += data.maxLen[column]
		column++
	}
}

func drawString(s string, x, y int, fg, bg termbox.Attribute) {
	for _, ch := range s {
		termbox.SetCell(x, y, ch, fg, bg)
		x += runewidth.RuneWidth(ch)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/cloudfoundry/cli/plugin"
	"github.com/jberkhahn/v3_beta/commands"
)

type V3Plugin struct {
}

func main() {
	plugin.Start(new(V3Plugin))
}

func (v3plugin *V3Plugin) Run(cliConnection plugin.CliConnection, args []string) {
	if args[0] == "v3-push" {
		commands.Push(cliConnection, args)
	} else if args[0] == "v3-apps" {
		if len(args) == 1 {
			commands.Apps(cliConnection, args)
		} else {
			//print apps help
		}
	} else if args[0] == "v3-processes" {
		if len(args) == 1 {
			commands.Processes(cliConnection, args)
		} else {
			//print processes help
		}
	} else if args[0] == "v3-delete" {
		if len(args) == 2 {
			commands.Delete(cliConnection, args)
		} else {
			//print help
		}
	} else if args[0] == "v3-logs" {
		if len(args) == 2 {
			commands.Logs(cliConnection, args)
		} else {
			//			print help
		}
	} else if args[0] == "v3-tasks" {
		if len(args) == 2 {
			commands.Tasks(cliConnection, args)
		} else {
			//			print help
		}
	} else if args[0] == "v3-run-task" {
		if len(args) == 4 {
			commands.RunTask(cliConnection, args)
		} else {
			//			print help
		}
	} else if args[0] == "v3-cancel-task" {
		if len(args) == 3 {
			commands.CancelTask(cliConnection, args)
		} else {
			fmt.Printf("Wrong number of argument, type `cf %s -h` for help\n", args[0])
		}
	} else if args[0] == "v3-bind-service" {
		if len(args) >= 3 {
			commands.BindService(cliConnection, args)
		} else {
			//			print help
		}
	}
}

func (v3plugin *V3Plugin) GetMetadata() plugin.PluginMetadata {
	return plugin.PluginMetadata{
		Name: "v3_beta",
		Version: plugin.VersionType{
			Major: 0,
			Minor: 4,
			Build: 12,
		},
		Commands: []plugin.Command{
			{
				Name:     "v3-push",
				Alias:    "v3-p",
				HelpText: "pushes current dir as a v3 process",
				UsageDetails: plugin.Usage{
					Usage: "v3-push APPNAME",
					Options: map[string]string{
						"p":  "path to dir or zip to push",
						"di": "path to docker image to push",
					},
				},
			},
			{
				Name:     "v3-apps",
				Alias:    "v3-a",
				HelpText: "displays all v3 apps",
				UsageDetails: plugin.Usage{
					Usage:   "v3-apps",
					Options: map[string]string{},
				},
			},
			{
				Name:     "v3-processes",
				Alias:    "",
				HelpText: "displays all v3 processes",
				UsageDetails: plugin.Usage{
					Usage:   "v3-processes",
					Options: map[string]string{},
				},
			},
			{
				Name:     "v3-delete",
				Alias:    "v3-d",
				HelpText: "delete a v3 app",
				UsageDetails: plugin.Usage{
					Usage:   "v3-delete APPNAME",
					Options: map[string]string{},
				},
			},
			{
				Name:     "v3-logs",
				Alias:    "",
				HelpText: "tail logs for a v3 app",
				UsageDetails: plugin.Usage{
					Usage:   "v3-logs APPNAME",
					Options: map[string]string{},
				},
			},
			{
				Name:     "v3-tasks",
				Alias:    "v3-t",
				HelpText: "list tasks for a v3 app",
				UsageDetails: plugin.Usage{
					Usage:   "v3-tasks APPNAME",
					Options: map[string]string{},
				},
			},
			{
				Name:     "v3-bind-service",
				Alias:    "v3-bs",
				HelpText: "bind a service instance to a v3 app",
				UsageDetails: plugin.Usage{
					Usage: "v3-bind-service APPNAME SERVICEINSTANCE",
					Options: map[string]string{
						"c": "parameters as json",
					},
				},
			},
			{
				Name:     "v3-run-task",
				Alias:    "v3-rt",
				HelpText: "run a task on a v3 app",
				UsageDetails: plugin.Usage{
					Usage:   "v3-run-task APPNAME TASKNAME COMMAND",
					Options: map[string]string{},
				},
			},
			{
				Name:     "v3-cancel-task",
				Alias:    "v3-ct",
				HelpText: "cancel a task on a v3 app",
				UsageDetails: plugin.Usage{
					Usage: "v3-cancel-task APPNAME TASKNAME",
				},
			},
		},
	}

}
<commit_msg>Improve help message for incorrect command usage<commit_after>package main

import (
	"fmt"

	"github.com/cloudfoundry/cli/plugin"
	"github.com/jberkhahn/v3_beta/commands"
)

type V3Plugin struct {
}

func main() {
	plugin.Start(new(V3Plugin))
}

func (v3plugin *V3Plugin) Run(cliConnection plugin.CliConnection, args []string) {
	if args[0] == "v3-push" {
		commands.Push(cliConnection, args)
	} else if args[0] == "v3-apps" {
		if len(args) == 1 {
			commands.Apps(cliConnection, args)
		} else {
			fmt.Printf("Wrong number of argument, type `cf %s -h` for help\n", args[0])
		}
	} else if args[0] == "v3-processes" {
		if len(args) == 1 {
			commands.Processes(cliConnection, args)
		} else {
			fmt.Printf("Wrong number of argument, type `cf %s -h` for help\n", args[0])
		}
	} else if args[0] == "v3-delete" {
		if len(args) == 2 {
			commands.Delete(cliConnection, args)
		} else {
			fmt.Printf("Wrong number of argument, type `cf %s -h` for help\n", args[0])
		}
	} else if args[0] == "v3-logs" {
		if len(args) == 2 {
			commands.Logs(cliConnection, args)
		} else {
			fmt.Printf("Wrong number of argument, type `cf %s -h` for help\n", args[0])
		}
	} else if args[0] == "v3-tasks" {
		if len(args) == 2 {
			commands.Tasks(cliConnection, args)
		} else {
			fmt.Printf("Wrong number of argument, type `cf %s -h` for help\n", args[0])
		}
	} else if args[0] == "v3-run-task" {
		if len(args) == 4 {
			commands.RunTask(cliConnection, args)
		} else {
			fmt.Printf("Wrong number of argument, type `cf %s -h` for help\n", args[0])
		}
	} else if args[0] == "v3-cancel-task" {
		if len(args) == 3 {
			commands.CancelTask(cliConnection, args)
		} else {
			fmt.Printf("Wrong number of argument, type `cf %s -h` for help\n", args[0])
		}
	} else if args[0] == "v3-bind-service" {
		if len(args) >= 3 {
			commands.BindService(cliConnection, args)
		} else {
			fmt.Printf("Wrong number of argument, type `cf %s -h` for help\n", args[0])
		}
	}
}

func (v3plugin *V3Plugin) GetMetadata() plugin.PluginMetadata {
	return plugin.PluginMetadata{
		Name: "v3_beta",
		Version: plugin.VersionType{
			Major: 0,
			Minor: 4,
			Build: 12,
		},
		Commands: []plugin.Command{
			{
				Name:     "v3-push",
				Alias:    "v3-p",
				HelpText: "pushes current dir as a v3 process",
				UsageDetails: plugin.Usage{
					Usage: "v3-push APPNAME",
					Options: map[string]string{
						"p":  "path to dir or zip to push",
						"di": "path to docker image to push",
					},
				},
			},
			{
				Name:     "v3-apps",
				Alias:    "v3-a",
				HelpText: "displays all v3 apps",
				UsageDetails: plugin.Usage{
					Usage:   "v3-apps",
					Options: map[string]string{},
				},
			},
			{
				Name:     "v3-processes",
				Alias:    "",
				HelpText: "displays all v3 processes",
				UsageDetails: plugin.Usage{
					Usage:   "v3-processes",
					Options: map[string]string{},
				},
			},
			{
				Name:     "v3-delete",
				Alias:    "v3-d",
				HelpText: "delete a v3 app",
				UsageDetails: plugin.Usage{
					Usage:   "v3-delete APPNAME",
					Options: map[string]string{},
				},
			},
			{
				Name:     "v3-logs",
				Alias:    "",
				HelpText: "tail logs for a v3 app",
				UsageDetails: plugin.Usage{
					Usage:   "v3-logs APPNAME",
					Options: map[string]string{},
				},
			},
			{
				Name:     "v3-tasks",
				Alias:    "v3-t",
				HelpText: "list tasks for a v3 app",
				UsageDetails: plugin.Usage{
					Usage:   "v3-tasks APPNAME",
					Options: map[string]string{},
				},
			},
			{
				Name:     "v3-bind-service",
				Alias:    "v3-bs",
				HelpText: "bind a service instance to a v3 app",
				UsageDetails: plugin.Usage{
					Usage: "v3-bind-service APPNAME SERVICEINSTANCE",
					Options: map[string]string{
						"c": "parameters as json",
					},
				},
			},
			{
				Name:     "v3-run-task",
				Alias:    "v3-rt",
				HelpText: "run a task on a v3 app",
				UsageDetails: plugin.Usage{
					Usage:   "v3-run-task APPNAME TASKNAME COMMAND",
					Options: map[string]string{},
				},
			},
			{
				Name:     "v3-cancel-task",
				Alias:    "v3-ct",
				HelpText: "cancel a task on a v3 app",
				UsageDetails: plugin.Usage{
					Usage: "v3-cancel-task APPNAME TASKNAME",
				},
			},
		},
	}

}
<|endoftext|>


<commit_before>package gen

import (
	"io/ioutil"

	"github.com/robertkrimen/otto"
	"github.com/spf13/afero"
)

type VM struct {
	*otto.Otto
}

type Export map[string]interface{}

func (e Export) Set(key string, value interface{}) {
	e[key] = value
}

func (e Export) ToValue(vm *otto.Otto) otto.Value {
	o, err := vm.Object(`({})`)
	if err != nil {
		panic(err)
	}
	for key, value := range e {
		o.Set(key, value)
	}
	return o.Value()
}

const (
	modeRead  = "r"
	modeWrite = "w"
)

type fileSys struct {
	afero.Fs
	vm *otto.Otto
}

func (fs fileSys) export() Export {
	e := make(Export)
	e.Set("open", fs.open)
	return e
}

func (fs fileSys) open(call otto.FunctionCall) otto.Value {
	name, err := call.Argument(0).ToString()
	if err != nil {
		Panic(err)
	}
	f, err := fs.Open(name)
	if err != nil {
		Panic(err)
	}
	af := &file{o: f}
	return af.export().ToValue(call.Otto)
}

type file struct {
	o afero.File
}

func (f *file) export() Export {
	e := make(Export)
	e.Set("close", f.close)
	e.Set("read", f.read)
	return e
}

func (f *file) close(call otto.FunctionCall) otto.Value {
	err := f.o.Close()
	if err != nil {
		Panic(err)
	}
	return otto.Value{}
}

func (f *file) read(call otto.FunctionCall) otto.Value {
	b, err := ioutil.ReadAll(f.o)
	if err != nil {
		Panic(err)
	}
	return ToValue(string(b))
}
<commit_msg>Add file opening flags<commit_after>package gen

import (
	"io/ioutil"

	"github.com/robertkrimen/otto"
	"github.com/spf13/afero"
)

type VM struct {
	*otto.Otto
}

type Export map[string]interface{}

func (e Export) Set(key string, value interface{}) {
	e[key] = value
}

func (e Export) ToValue(vm *otto.Otto) otto.Value {
	o, err := vm.Object(`({})`)
	if err != nil {
		panic(err)
	}
	for key, value := range e {
		o.Set(key, value)
	}
	return o.Value()
}

const (
	modeRead     = "r"
	modeWrite    = "w"
	modCreate    = "c"
	modReqaWrite = "r+w"
	modAppend    = "a"
	modTrucc     = "t"
)

type fileSys struct {
	afero.Fs
	vm *otto.Otto
}

func (fs fileSys) export() Export {
	e := make(Export)
	e.Set("open", fs.open)
	return e
}

func (fs fileSys) open(call otto.FunctionCall) otto.Value {
	name, err := call.Argument(0).ToString()
	if err != nil {
		Panic(err)
	}
	f, err := fs.Open(name)
	if err != nil {
		Panic(err)
	}
	af := &file{o: f}
	return af.export().ToValue(call.Otto)
}

type file struct {
	o afero.File
}

func (f *file) export() Export {
	e := make(Export)
	e.Set("close", f.close)
	e.Set("read", f.read)
	return e
}

func (f *file) close(call otto.FunctionCall) otto.Value {
	err := f.o.Close()
	if err != nil {
		Panic(err)
	}
	return otto.Value{}
}

func (f *file) read(call otto.FunctionCall) otto.Value {
	b, err := ioutil.ReadAll(f.o)
	if err != nil {
		Panic(err)
	}
	return ToValue(string(b))
}
<|endoftext|>


<commit_before>package main

import (
	//"github.com/ginuerzh/gosocks5"
	"github.com/golang/glog"
	"github.com/gorilla/websocket"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"
)

type wsConn struct {
	conn *websocket.Conn
	rb   []byte
}

func wsClient(conn net.Conn, host string) (*wsConn, error) {
	c, resp, err := websocket.NewClient(conn, &url.URL{Host: host, Path: "/ws"}, nil, 1024, 1024)
	if err != nil {
		return nil, err
	}
	resp.Body.Close()

	return &wsConn{conn: c}, nil
}

func wsServer(conn *websocket.Conn) *wsConn {
	return &wsConn{
		conn: conn,
	}
}

func (c *wsConn) Read(b []byte) (n int, err error) {
	if len(c.rb) == 0 {
		_, c.rb, err = c.conn.ReadMessage()
	}
	n = copy(b, c.rb)
	c.rb = c.rb[n:]

	//log.Println("ws r:", n)

	return
}

func (c *wsConn) Write(b []byte) (n int, err error) {
	err = c.conn.WriteMessage(websocket.BinaryMessage, b)
	n = len(b)
	//log.Println("ws w:", n)

	return
}

func (c *wsConn) Close() error {
	return c.conn.Close()
}

func (c *wsConn) LocalAddr() net.Addr {
	return c.conn.LocalAddr()
}

func (c *wsConn) RemoteAddr() net.Addr {
	return c.conn.RemoteAddr()
}

func (conn *wsConn) SetDeadline(t time.Time) error {
	if err := conn.SetReadDeadline(t); err != nil {
		return err
	}
	return conn.SetWriteDeadline(t)
}
func (c *wsConn) SetReadDeadline(t time.Time) error {
	return c.conn.SetReadDeadline(t)
}

func (c *wsConn) SetWriteDeadline(t time.Time) error {
	return c.conn.SetWriteDeadline(t)
}

type ws struct {
	upgrader websocket.Upgrader
	arg      Args
}

func NewWs(arg Args) *ws {
	return &ws{
		arg: arg,
		upgrader: websocket.Upgrader{
			ReadBufferSize:  1024,
			WriteBufferSize: 1024,
			CheckOrigin:     func(r *http.Request) bool { return true },
		},
	}
}

func (s *ws) handle(w http.ResponseWriter, r *http.Request) {
	if glog.V(LDEBUG) {
		dump, err := httputil.DumpRequest(r, false)
		if err != nil {
			glog.Infoln(err)
		} else {
			glog.Infoln(string(dump))
		}
	}
	conn, err := s.upgrader.Upgrade(w, r, nil)
	if err != nil {
		glog.V(LERROR).Infoln(err)
		return
	}
	handleConn(wsServer(conn), s.arg)
}

func (s *ws) ListenAndServe() error {
	http.HandleFunc("/ws", s.handle)
	return http.ListenAndServe(s.arg.Addr, nil)
}
<commit_msg>fix websocket scheme<commit_after>package main

import (
	//"github.com/ginuerzh/gosocks5"
	"github.com/golang/glog"
	"github.com/gorilla/websocket"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"
)

type wsConn struct {
	conn *websocket.Conn
	rb   []byte
}

func wsClient(conn net.Conn, host string) (*wsConn, error) {
	c, resp, err := websocket.NewClient(conn, &url.URL{Scheme: "ws", Host: host, Path: "/ws"}, nil, 1024, 1024)
	if err != nil {
		return nil, err
	}
	resp.Body.Close()

	return &wsConn{conn: c}, nil
}

func wsServer(conn *websocket.Conn) *wsConn {
	return &wsConn{
		conn: conn,
	}
}

func (c *wsConn) Read(b []byte) (n int, err error) {
	if len(c.rb) == 0 {
		_, c.rb, err = c.conn.ReadMessage()
	}
	n = copy(b, c.rb)
	c.rb = c.rb[n:]

	//log.Println("ws r:", n)

	return
}

func (c *wsConn) Write(b []byte) (n int, err error) {
	err = c.conn.WriteMessage(websocket.BinaryMessage, b)
	n = len(b)
	//log.Println("ws w:", n)

	return
}

func (c *wsConn) Close() error {
	return c.conn.Close()
}

func (c *wsConn) LocalAddr() net.Addr {
	return c.conn.LocalAddr()
}

func (c *wsConn) RemoteAddr() net.Addr {
	return c.conn.RemoteAddr()
}

func (conn *wsConn) SetDeadline(t time.Time) error {
	if err := conn.SetReadDeadline(t); err != nil {
		return err
	}
	return conn.SetWriteDeadline(t)
}
func (c *wsConn) SetReadDeadline(t time.Time) error {
	return c.conn.SetReadDeadline(t)
}

func (c *wsConn) SetWriteDeadline(t time.Time) error {
	return c.conn.SetWriteDeadline(t)
}

type ws struct {
	upgrader websocket.Upgrader
	arg      Args
}

func NewWs(arg Args) *ws {
	return &ws{
		arg: arg,
		upgrader: websocket.Upgrader{
			ReadBufferSize:  1024,
			WriteBufferSize: 1024,
			CheckOrigin:     func(r *http.Request) bool { return true },
		},
	}
}

func (s *ws) handle(w http.ResponseWriter, r *http.Request) {
	if glog.V(LDEBUG) {
		dump, err := httputil.DumpRequest(r, false)
		if err != nil {
			glog.Infoln(err)
		} else {
			glog.Infoln(string(dump))
		}
	}
	conn, err := s.upgrader.Upgrade(w, r, nil)
	if err != nil {
		glog.V(LERROR).Infoln(err)
		return
	}
	handleConn(wsServer(conn), s.arg)
}

func (s *ws) ListenAndServe() error {
	http.HandleFunc("/ws", s.handle)
	return http.ListenAndServe(s.arg.Addr, nil)
}
<|endoftext|>


<commit_before>// Copyright 2016 The Gosl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ml

import (
	"github.com/cpmech/gosl/io"
	"github.com/cpmech/gosl/la"
	"github.com/cpmech/gosl/plt"
)

// PlotterClass defines a plotter to plot classification data
type PlotterClass struct {

	// input
	data    *Data // x-data
	classes []int // y-data

	// constants
	MgridNpts int // nubmer of poitns for meshgrid (for contours)

	// arguments
	ArgsYclasses  map[int]*plt.A // maps y classes [0, 1, 2, ...] to plot arguments
	ArgsCentroids *plt.A         // args for centroids
}

// NewPlotterClass returns a new ploter
func NewPlotterClass(data *Data, classes []int, nClasses int) (o *PlotterClass) {

	// input
	o = new(PlotterClass)
	o.data = data
	o.classes = classes

	// constants
	o.MgridNpts = 21

	// arguments
	o.ArgsYclasses = make(map[int]*plt.A)
	for k := 0; k < nClasses; k++ {
		o.ArgsYclasses[k] = &plt.A{C: plt.C(k, 0), M: plt.M(k, 2), NoClip: true}
	}
	o.ArgsCentroids = &plt.A{Ls: "None", M: "*", Ms: 10, Mec: "k", NoClip: true}
	return
}

// Data plots data classes
func (o *PlotterClass) Data(iFeature, jFeature int, binary bool) {
	for iSample := 0; iSample < o.data.Nsamples; iSample++ {
		k := o.classes[iSample] % len(o.ArgsYclasses)
		args := o.ArgsYclasses[k]
		ui := o.data.X.Get(iSample, iFeature)
		vi := o.data.X.Get(iSample, jFeature)
		plt.PlotOne(ui, vi, args)
	}
	plt.HideTRborders()
	plt.Gll(io.Sf("$x_{%d}$", iFeature), io.Sf("$x_{%d}$", jFeature), nil)
}

// Centroids plots centroids of classes
func (o *PlotterClass) Centroids(centroids []la.Vector) {
	nClasses := len(centroids)
	for i := 0; i < nClasses; i++ {
		k := i % len(o.ArgsYclasses)
		o.ArgsCentroids.C = o.ArgsYclasses[k].C
		u, v := centroids[i][0], centroids[i][1]
		plt.PlotOne(u, v, o.ArgsCentroids)
		plt.Text(u, v, io.Sf("%d", i), &plt.A{Fsz: 8})
	}
}
<commit_msg>Add indicators of clusters to plots<commit_after>// Copyright 2016 The Gosl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ml

import (
	"github.com/cpmech/gosl/io"
	"github.com/cpmech/gosl/la"
	"github.com/cpmech/gosl/plt"
)

// PlotterClass defines a plotter to plot classification data
type PlotterClass struct {

	// input
	data    *Data // x-data
	classes []int // y-data

	// constants
	MgridNpts int // nubmer of poitns for meshgrid (for contours)

	// arguments
	ArgsYclasses  map[int]*plt.A // maps y classes [0, 1, 2, ...] to plot arguments
	ArgsCentroids *plt.A         // args for centroids
	ArgsCircle1   *plt.A         // args for centroids
	ArgsCircle2   *plt.A         // args for centroids
}

// NewPlotterClass returns a new ploter
func NewPlotterClass(data *Data, classes []int, nClasses int) (o *PlotterClass) {

	// input
	o = new(PlotterClass)
	o.data = data
	o.classes = classes

	// constants
	o.MgridNpts = 21

	// arguments
	o.ArgsYclasses = make(map[int]*plt.A)
	for k := 0; k < nClasses; k++ {
		o.ArgsYclasses[k] = &plt.A{C: plt.C(k, 0), M: plt.M(k, 2), NoClip: true}
	}
	o.ArgsCentroids = &plt.A{Ls: "None", M: "*", Ms: 10, Mec: "k", NoClip: true}
	o.ArgsCircle1 = &plt.A{M: "o", Void: true, Ms: 13, Mec: "k", Mew: 4.4, NoClip: true}
	o.ArgsCircle2 = &plt.A{M: "o", Void: true, Ms: 13, Mec: "w", Mew: 1.3, NoClip: true}
	return
}

// Data plots data classes
func (o *PlotterClass) Data(iFeature, jFeature int, binary bool) {
	for iSample := 0; iSample < o.data.Nsamples; iSample++ {
		k := o.classes[iSample] % len(o.ArgsYclasses)
		args := o.ArgsYclasses[k]
		ui := o.data.X.Get(iSample, iFeature)
		vi := o.data.X.Get(iSample, jFeature)
		plt.PlotOne(ui, vi, args)
	}
	plt.HideTRborders()
	plt.Gll(io.Sf("$x_{%d}$", iFeature), io.Sf("$x_{%d}$", jFeature), nil)
}

// Centroids plots centroids of classes
func (o *PlotterClass) Centroids(centroids []la.Vector) {
	nClasses := len(centroids)
	for i := 0; i < nClasses; i++ {
		k := i % len(o.ArgsYclasses)
		o.ArgsCentroids.C = o.ArgsYclasses[k].C
		u, v := centroids[i][0], centroids[i][1]
		plt.PlotOne(u, v, o.ArgsCentroids)
		plt.PlotOne(u, v, o.ArgsCircle1)
		plt.PlotOne(u, v, o.ArgsCircle2)
		plt.Text(u, v, io.Sf("%d", i), &plt.A{Fsz: 8})
	}
}
<|endoftext|>


<commit_before>package model

import (
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"log"
	"strings"
	"time"

	"golang.org/x/text/encoding/charmap"
)

func ToPlan(uploadReader io.Reader) (*Plan, error) {
	plan, err := decodePlan(uploadReader)
	if err != nil {
		return plan, err
	}

	refine(plan)
	return plan, nil
}

func decodePlan(uploadReader io.Reader) (*Plan, error) {
	encReader := charmap.ISO8859_1.NewDecoder().Reader(uploadReader)

	decoder := xml.NewDecoder(encReader)
	decoder.Entity = xml.HTMLEntity

	if err := moveToNext("font", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for first \"font\": %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	token, err := decoder.RawToken()
	charData, _ := token.(xml.CharData)
	createdString := string(charData[7:])
	loc, _ := time.LoadLocation("Europe/Berlin")
	created, _ := time.ParseInLocation("02.01.2006 15:04", createdString, loc)

	if err = moveToNext("div", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for \"div\" with day of first part: %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	token, err = decoder.RawToken()
	charData, _ = token.(xml.CharData)
	dayString := string(charData)
	dayFormat := "2.1.2006"
	day1, _ := time.ParseInLocation(dayFormat, strings.Split(dayString, " ")[0], loc)

	if err = moveToNext("table", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for first \"table\": %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("table", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for second \"table\": %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("table", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for third \"table\": %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("tr", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for first \"tr\" of the first plan table: %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("tr", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for second \"tr\" of the first plan table: %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("tr", false, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for end of second \"tr\" of the first plan table: %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}

	decoder.RawToken() // Skip xml.CharData.

	firstPartSubstitutions := make([]Substitution, 0, 20)
	token, err = decoder.RawToken()
	_, ok := token.(xml.StartElement)
	for ok && err == nil {
		firstPartSubstitutions = append(firstPartSubstitutions, readSubstitution(decoder))
		decoder.RawToken()
		token, err = decoder.RawToken()
		_, ok = token.(xml.StartElement)
	}

	if err = moveToNext("div", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for \"div\" with day of second part: %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	token, err = decoder.RawToken()
	charData, _ = token.(xml.CharData)
	dayString = string(charData)
	day2, _ := time.ParseInLocation(dayFormat, strings.Split(dayString, " ")[0], loc)

	if err = moveToNext("table", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for fourth \"table\": %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("table", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for fifth \"table\": %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("table", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for sixth \"table\": %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("tr", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for first \"tr\" of the second plan table: %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("tr", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for second \"tr\" of the second plan table: %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("tr", false, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for end of second \"tr\" of the second plan table: %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}

	decoder.RawToken() // Skip xml.CharData.

	secondPartSubstitutions := make([]Substitution, 0, 20)
	token, err = decoder.RawToken()
	_, ok = token.(xml.StartElement)
	for ok && err == nil {
		secondPartSubstitutions = append(secondPartSubstitutions, readSubstitution(decoder))
		decoder.RawToken()
		token, err = decoder.RawToken()
		_, ok = token.(xml.StartElement)
	}

	parts := []Part{
		Part{Day: day1, Substitutions: firstPartSubstitutions},
		Part{Day: day2, Substitutions: secondPartSubstitutions}}
	return &Plan{
		Created: created,
		Parts:   parts}, nil
}

func readSubstitution(decoder *xml.Decoder) Substitution {
	var (
		class             = readInformation(decoder) // Read class.
		periodString      = readInformation(decoder) // Read period(s).
		substTeacherShort = readInformation(decoder) // Read substitution teacher.
		instdTeacherShort = readInformation(decoder) // Read instead teacher.
		instdSubjectShort = readInformation(decoder) // Read instead subject.
		kind              = readInformation(decoder) // Read kind.
		_                 = readInformation(decoder) // Skip "Vtr. von".
		text              = readInformation(decoder) // Read text.
	)
	// Close table row.
	decoder.RawToken()

	return Substitution{
		Class:        class,
		Period:       periodString,
		SubstTeacher: Teacher{Short: substTeacherShort},
		InstdTeacher: Teacher{Short: instdTeacherShort},
		InstdSubject: Subject{Short: instdSubjectShort},
		Kind:         kind,
		Text:         text}
}

func readInformation(decoder *xml.Decoder) string {
	decoder.RawToken()
	token, _ := decoder.RawToken()
	if startElement, ok := token.(xml.StartElement); ok && startElement.Name.Local == "span" {
		token, _ = decoder.RawToken()
		defer decoder.RawToken()
	}
	charData, _ := token.(xml.CharData)
	information := string(charData)
	decoder.RawToken()
	return information
}

func moveToNext(elementName string, se bool, decoder *xml.Decoder) error {
	for {
		token, err := decoder.RawToken()
		if err != nil {
			return err
		}
		if se {
			if startElement, ok := token.(xml.StartElement); ok {
				if startElement.Name.Local == elementName {
					return nil
				}
			}
		} else {
			if endElement, ok := token.(xml.EndElement); ok {
				if endElement.Name.Local == elementName {
					return nil
				}
			}
		}
	}
}

func refine(plan *Plan) {
	const nbsp = "\u00A0"
	for p, part := range plan.Parts {
		for s, substitution := range part.Substitutions {
			if substitution.Period == nbsp {
				plan.Parts[p].Substitutions[s].Period = ""
			}
			if substitution.Class == nbsp {
				plan.Parts[p].Substitutions[s].Class = ""
			}
			if substitution.SubstTeacher.Short == nbsp || substitution.SubstTeacher.Short == "???" ||
				substitution.SubstTeacher.Short == "+" || substitution.SubstTeacher.Short == "---" {
				plan.Parts[p].Substitutions[s].SubstTeacher.Short = ""
			} else {
				plan.Parts[p].Substitutions[s].SubstTeacher.Read()
			}
			if substitution.InstdTeacher.Short == nbsp {
				plan.Parts[p].Substitutions[s].InstdTeacher.Short = ""
			} else {
				plan.Parts[p].Substitutions[s].InstdTeacher.Read()
			}
			if substitution.InstdSubject.Short == nbsp {
				plan.Parts[p].Substitutions[s].InstdSubject.Short = ""
			} else {
				plan.Parts[p].Substitutions[s].InstdSubject.Read()
			}
			if substitution.Kind == nbsp {
				plan.Parts[p].Substitutions[s].Kind = ""
			} else if substitution.Kind == "Statt-Vertretung" {
				plan.Parts[p].Substitutions[s].Kind = "Vertretung"
			}
			if substitution.Text == nbsp {
				plan.Parts[p].Substitutions[s].Text = ""
			}
		}
	}
}
<commit_msg>adding task provider information<commit_after>package model

import (
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"log"
	"regexp"
	"strings"
	"time"

	"golang.org/x/text/encoding/charmap"
)

func ToPlan(uploadReader io.Reader) (*Plan, error) {
	plan, err := decodePlan(uploadReader)
	if err != nil {
		return plan, err
	}

	refine(plan)
	return plan, nil
}

func decodePlan(uploadReader io.Reader) (*Plan, error) {
	encReader := charmap.ISO8859_1.NewDecoder().Reader(uploadReader)

	decoder := xml.NewDecoder(encReader)
	decoder.Entity = xml.HTMLEntity

	if err := moveToNext("font", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for first \"font\": %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	token, err := decoder.RawToken()
	charData, _ := token.(xml.CharData)
	createdString := string(charData[7:])
	loc, _ := time.LoadLocation("Europe/Berlin")
	created, _ := time.ParseInLocation("02.01.2006 15:04", createdString, loc)

	if err = moveToNext("div", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for \"div\" with day of first part: %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	token, err = decoder.RawToken()
	charData, _ = token.(xml.CharData)
	dayString := string(charData)
	dayFormat := "2.1.2006"
	day1, _ := time.ParseInLocation(dayFormat, strings.Split(dayString, " ")[0], loc)

	if err = moveToNext("table", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for first \"table\": %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("table", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for second \"table\": %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("table", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for third \"table\": %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("tr", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for first \"tr\" of the first plan table: %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("tr", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for second \"tr\" of the first plan table: %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("tr", false, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for end of second \"tr\" of the first plan table: %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}

	decoder.RawToken() // Skip xml.CharData.

	firstPartSubstitutions := make([]Substitution, 0, 20)
	token, err = decoder.RawToken()
	_, ok := token.(xml.StartElement)
	for ok && err == nil {
		firstPartSubstitutions = append(firstPartSubstitutions, readSubstitution(decoder))
		decoder.RawToken()
		token, err = decoder.RawToken()
		_, ok = token.(xml.StartElement)
	}

	if err = moveToNext("div", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for \"div\" with day of second part: %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	token, err = decoder.RawToken()
	charData, _ = token.(xml.CharData)
	dayString = string(charData)
	day2, _ := time.ParseInLocation(dayFormat, strings.Split(dayString, " ")[0], loc)

	if err = moveToNext("table", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for fourth \"table\": %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("table", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for fifth \"table\": %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("table", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for sixth \"table\": %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("tr", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for first \"tr\" of the second plan table: %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("tr", true, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for second \"tr\" of the second plan table: %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}
	if err = moveToNext("tr", false, decoder); err != nil {
		err = errors.New(fmt.Sprintf("Error searching for end of second \"tr\" of the second plan table: %v\n", err))
		log.Println(err)
		return &Plan{}, err
	}

	decoder.RawToken() // Skip xml.CharData.

	secondPartSubstitutions := make([]Substitution, 0, 20)
	token, err = decoder.RawToken()
	_, ok = token.(xml.StartElement)
	for ok && err == nil {
		secondPartSubstitutions = append(secondPartSubstitutions, readSubstitution(decoder))
		decoder.RawToken()
		token, err = decoder.RawToken()
		_, ok = token.(xml.StartElement)
	}

	parts := []Part{
		Part{Day: day1, Substitutions: firstPartSubstitutions},
		Part{Day: day2, Substitutions: secondPartSubstitutions}}
	return &Plan{
		Created: created,
		Parts:   parts}, nil
}

func readSubstitution(decoder *xml.Decoder) Substitution {
	var (
		class             = readInformation(decoder) // Read class.
		periodString      = readInformation(decoder) // Read period(s).
		substTeacherShort = readInformation(decoder) // Read substitution teacher.
		instdTeacherShort = readInformation(decoder) // Read instead teacher.
		instdSubjectShort = readInformation(decoder) // Read instead subject.
		kind              = readInformation(decoder) // Read kind.
		_                 = readInformation(decoder) // Skip "Vtr. von".
		text              = readInformation(decoder) // Read text.
	)
	// Close table row.
	decoder.RawToken()

	return Substitution{
		Class:        class,
		Period:       periodString,
		SubstTeacher: Teacher{Short: substTeacherShort},
		InstdTeacher: Teacher{Short: instdTeacherShort},
		InstdSubject: Subject{Short: instdSubjectShort},
		Kind:         kind,
		Text:         text}
}

func readInformation(decoder *xml.Decoder) string {
	decoder.RawToken()
	token, _ := decoder.RawToken()
	if startElement, ok := token.(xml.StartElement); ok && startElement.Name.Local == "span" {
		token, _ = decoder.RawToken()
		defer decoder.RawToken()
	}
	charData, _ := token.(xml.CharData)
	information := string(charData)
	decoder.RawToken()
	return information
}

func moveToNext(elementName string, se bool, decoder *xml.Decoder) error {
	for {
		token, err := decoder.RawToken()
		if err != nil {
			return err
		}
		if se {
			if startElement, ok := token.(xml.StartElement); ok {
				if startElement.Name.Local == elementName {
					return nil
				}
			}
		} else {
			if endElement, ok := token.(xml.EndElement); ok {
				if endElement.Name.Local == elementName {
					return nil
				}
			}
		}
	}
}

func refine(plan *Plan) {
	const nbsp = "\u00A0"
	for p, part := range plan.Parts {
		for s, substitution := range part.Substitutions {
			if substitution.Period == nbsp {
				plan.Parts[p].Substitutions[s].Period = ""
			}
			if substitution.Class == nbsp {
				plan.Parts[p].Substitutions[s].Class = ""
			}
			if substitution.SubstTeacher.Short == nbsp || substitution.SubstTeacher.Short == "???" ||
				substitution.SubstTeacher.Short == "+" || substitution.SubstTeacher.Short == "---" {
				plan.Parts[p].Substitutions[s].SubstTeacher.Short = ""
			} else {
				plan.Parts[p].Substitutions[s].SubstTeacher.Read()
			}
			if substitution.InstdTeacher.Short == nbsp {
				plan.Parts[p].Substitutions[s].InstdTeacher.Short = ""
			} else {
				plan.Parts[p].Substitutions[s].InstdTeacher.Read()
			}
			if substitution.InstdSubject.Short == nbsp {
				plan.Parts[p].Substitutions[s].InstdSubject.Short = ""
			} else {
				plan.Parts[p].Substitutions[s].InstdSubject.Read()
			}
			if substitution.Kind == nbsp {
				plan.Parts[p].Substitutions[s].Kind = ""
			} else if substitution.Kind == "Statt-Vertretung" {
				plan.Parts[p].Substitutions[s].Kind = "Vertretung"
			}
			if substitution.Text == nbsp {
				plan.Parts[p].Substitutions[s].Text = ""
			} else {
				re := regexp.MustCompile("Aufg(\\.|(abe)) [A-Za-z]{2,3}")
				task := re.FindString(substitution.Text)
				if task != "" {
					provider := strings.Trim(task[len(task)-3:], " ")
					taskProvider := Teacher{Short: provider}
					taskProvider.Read()
					plan.Parts[p].Substitutions[s].TaskProvider = taskProvider
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package models

import (
	"database/sql"

	e "github.com/techjanitor/pram-post/errors"
	u "github.com/techjanitor/pram-post/utils"
)

type StickyModel struct {
	Id     uint
	Name   string
	Ib     uint
	Closed bool
}

// Status will return info
func (i *StickyModel) Status() (err error) {

	// Get Database handle
	db, err := u.GetDb()
	if err != nil {
		return
	}

	// Check if favorite is already there
	err = db.QueryRow("SELECT ib_id, thread_title, thread_closed FROM threads WHERE thread_id = ? LIMIT 1", i.Id).Scan(&i.Ib, &i.Name, &i.Closed)
	if err == sql.ErrNoRows {
		return e.ErrNotFound
	} else if err != nil {
		return
	}

	return

}

// Toggle will change the thread status
func (i *StickyModel) Toggle() (err error) {

	// Get Database handle
	db, err := u.GetDb()
	if err != nil {
		return
	}

	ps1, err := db.Prepare("UPDATE threads SET thread_closed = ? WHERE thread_id = ?")
	if err != nil {
		return
	}
	defer ps1.Close()

	_, err = ps1.Exec(!i.Closed, i.Id)
	if err != nil {
		return
	}

	return

}
<commit_msg>add close and sticky threads<commit_after>package models

import (
	"database/sql"

	e "github.com/techjanitor/pram-post/errors"
	u "github.com/techjanitor/pram-post/utils"
)

type CloseModel struct {
	Id     uint
	Name   string
	Ib     uint
	Closed bool
}

// Status will return info
func (i *CloseModel) Status() (err error) {

	// Get Database handle
	db, err := u.GetDb()
	if err != nil {
		return
	}

	// Check if favorite is already there
	err = db.QueryRow("SELECT ib_id, thread_title, thread_closed FROM threads WHERE thread_id = ? LIMIT 1", i.Id).Scan(&i.Ib, &i.Name, &i.Closed)
	if err == sql.ErrNoRows {
		return e.ErrNotFound
	} else if err != nil {
		return
	}

	return

}

// Toggle will change the thread status
func (i *CloseModel) Toggle() (err error) {

	// Get Database handle
	db, err := u.GetDb()
	if err != nil {
		return
	}

	ps1, err := db.Prepare("UPDATE threads SET thread_closed = ? WHERE thread_id = ?")
	if err != nil {
		return
	}
	defer ps1.Close()

	_, err = ps1.Exec(!i.Closed, i.Id)
	if err != nil {
		return
	}

	return

}
<|endoftext|>


<commit_before>package models

import (
	"fmt"
)

type Linkable interface {
	Link() map[string]map[string]interface{}
}

type EntryFields map[string]interface{}

type NewEntry struct {
	Fields EntryFields `json:"fields"`
}

// Validate validates the entry
func (c *NewEntry) Validate() error {
	if c.Fields == nil || len(c.Fields) == 0 {
		return fmt.Errorf("NewEntry.Fields cannot be empty!")
	}

	return nil
}

// Includes are a sub-type used by EntryResults
type Includes struct {
	Entries []*Entry
	Assets  []*Asset
}

// QueryEntriesResult are returned for QueryEntries
type QueryEntriesResult struct {
	Entries  []*Entry
	Includes *Includes

	Pagination *Pagination
	Errors     []error
}

// Entry represent textual content in a space. An entry's data adheres to a
// certain content type.
type Entry struct {
	System `json:"sys"`
	Fields EntryFields `json:"fields"`
}

// Validate will validate the entry. An error is returned if the entry
// is not  valid.
func (c *Entry) Validate() error {
	if c.Space == nil || c.Space.ID == "" {
		return fmt.Errorf("Entry must have a valid Space associated with it!")
	}

	if c.System.ID == "" {
		return fmt.Errorf("Entry.System.ID cannot be empty!")
	}

	if c.Fields == nil || len(c.Fields) == 0 {
		return fmt.Errorf("Entry.Fields cannot be empty!")
	}

	return nil
}

// Link represents a link to the Entry and implements the Linkable interface
func (c *Entry) Link() map[string]map[string]interface{} {
	return map[string]map[string]interface{}{
		"sys": map[string]interface{}{
			"id":       c.ID,
			"linkType": "Entry",
			"type":     LinkType,
		},
	}
}
<commit_msg>Adding JSON struct tags to Includes<commit_after>package models

import (
	"fmt"
)

type Linkable interface {
	Link() map[string]map[string]interface{}
}

type EntryFields map[string]interface{}

type NewEntry struct {
	Fields EntryFields `json:"fields"`
}

// Validate validates the entry
func (c *NewEntry) Validate() error {
	if c.Fields == nil || len(c.Fields) == 0 {
		return fmt.Errorf("NewEntry.Fields cannot be empty!")
	}

	return nil
}

// Includes are a sub-type used by EntryResults
type Includes struct {
	Entries []*Entry `json:"Entry"`
	Assets  []*Asset `json:"Asset"`
}

// QueryEntriesResult are returned for QueryEntries
type QueryEntriesResult struct {
	Entries  []*Entry
	Includes *Includes

	Pagination *Pagination
	Errors     []error
}

// Entry represent textual content in a space. An entry's data adheres to a
// certain content type.
type Entry struct {
	System `json:"sys"`
	Fields EntryFields `json:"fields"`
}

// Validate will validate the entry. An error is returned if the entry
// is not  valid.
func (c *Entry) Validate() error {
	if c.Space == nil || c.Space.ID == "" {
		return fmt.Errorf("Entry must have a valid Space associated with it!")
	}

	if c.System.ID == "" {
		return fmt.Errorf("Entry.System.ID cannot be empty!")
	}

	if c.Fields == nil || len(c.Fields) == 0 {
		return fmt.Errorf("Entry.Fields cannot be empty!")
	}

	return nil
}

// Link represents a link to the Entry and implements the Linkable interface
func (c *Entry) Link() map[string]map[string]interface{} {
	return map[string]map[string]interface{}{
		"sys": map[string]interface{}{
			"id":       c.ID,
			"linkType": "Entry",
			"type":     LinkType,
		},
	}
}
<|endoftext|>


<commit_before>package models

import (
	"database/sql"
	"fmt"
	"github.com/asaskevich/govalidator"
	"golang.org/x/crypto/bcrypt"

	"github.com/techjanitor/pram-post/config"
	e "github.com/techjanitor/pram-post/errors"
	u "github.com/techjanitor/pram-post/utils"
)

// loginmodel contains user name and password
type LoginModel struct {
	Name      string
	Password  string
	Hash      []byte
	Id        uint
	Group     uint
	Confirmed bool
	Locked    bool
	Banned    bool
	Sid       string
}

// Validate will check the provided name and password
func (r *LoginModel) Validate() (err error) {

	// Validate name input
	name := u.Validate{Input: r.Name, Max: config.Settings.Limits.NameMaxLength, Min: config.Settings.Limits.NameMinLength}
	if name.IsEmpty() {
		return e.ErrNameEmpty
	} else if name.MinLength() {
		return e.ErrNameShort
	} else if name.MaxLength() {
		return e.ErrNameLong
	}

	// check if name is alphanumeric
	if !govalidator.IsAlphanumeric(r.Name) {
		return e.ErrNameAlphaNum
	}

	// Validate password input
	password := u.Validate{Input: r.Password, Max: config.Settings.Limits.NameMaxLength, Min: config.Settings.Limits.NameMinLength}
	if password.IsEmpty() {
		return e.ErrPasswordEmpty
	} else if password.MinLength() {
		return e.ErrPasswordShort
	} else if password.MaxLength() {
		return e.ErrPasswordLong
	}

	return

}

// log user in
func (r *LoginModel) Login() (err error) {

	// Get Database handle
	db, err := u.GetDb()
	if err != nil {
		return
	}

	// get hashed password from database
	err = db.QueryRow("select user_id, user_password, usergroup_id, user_confirmed, user_locked, user_banned from users where user_name = ?", r.Name).Scan(&r.Id, &r.Hash, &r.Group, &r.Confirmed, &r.Locked, &r.Banned)
	if err == sql.ErrNoRows {
		return e.ErrInvalidUser
	} else if err != nil {
		return
	}

	fmt.Printf("%s\n%s\n", r.Hash, r.Password)

	// compare provided password to stored hash
	bcrypt.CompareHashAndPassword(r.Hash, []byte(r.Password))
	if err == bcrypt.ErrMismatchedHashAndPassword {
		return e.ErrInvalidPassword
	} else if err != nil {
		return
	}

	// if account is not confirmed
	if !r.Confirmed {
		return e.ErrUserNotConfirmed
	}

	// if locked
	if r.Locked {
		return e.ErrUserLocked
	}

	// if banned
	if r.Banned {
		return e.ErrUserBanned
	}

	// make new session for user
	r.Sid, err = u.NewSession(r.Id)
	if err != nil {
		return
	}

	return

}
<commit_msg>debug login<commit_after>package models

import (
	"database/sql"
	"fmt"
	"github.com/asaskevich/govalidator"
	"golang.org/x/crypto/bcrypt"

	"github.com/techjanitor/pram-post/config"
	e "github.com/techjanitor/pram-post/errors"
	u "github.com/techjanitor/pram-post/utils"
)

// loginmodel contains user name and password
type LoginModel struct {
	Name      string
	Password  string
	Hash      []byte
	Id        uint
	Group     uint
	Confirmed bool
	Locked    bool
	Banned    bool
	Sid       string
}

// Validate will check the provided name and password
func (r *LoginModel) Validate() (err error) {

	// Validate name input
	name := u.Validate{Input: r.Name, Max: config.Settings.Limits.NameMaxLength, Min: config.Settings.Limits.NameMinLength}
	if name.IsEmpty() {
		return e.ErrNameEmpty
	} else if name.MinLength() {
		return e.ErrNameShort
	} else if name.MaxLength() {
		return e.ErrNameLong
	}

	// check if name is alphanumeric
	if !govalidator.IsAlphanumeric(r.Name) {
		return e.ErrNameAlphaNum
	}

	// Validate password input
	password := u.Validate{Input: r.Password, Max: config.Settings.Limits.NameMaxLength, Min: config.Settings.Limits.NameMinLength}
	if password.IsEmpty() {
		return e.ErrPasswordEmpty
	} else if password.MinLength() {
		return e.ErrPasswordShort
	} else if password.MaxLength() {
		return e.ErrPasswordLong
	}

	return

}

// log user in
func (r *LoginModel) Login() (err error) {

	// Get Database handle
	db, err := u.GetDb()
	if err != nil {
		return
	}

	// get hashed password from database
	err = db.QueryRow("select user_id, user_password, usergroup_id, user_confirmed, user_locked, user_banned from users where user_name = ?", r.Name).Scan(&r.Id, &r.Hash, &r.Group, &r.Confirmed, &r.Locked, &r.Banned)
	if err == sql.ErrNoRows {
		return e.ErrInvalidUser
	} else if err != nil {
		return
	}

	// compare provided password to stored hash
	err = bcrypt.CompareHashAndPassword(r.Hash, []byte(r.Password))
	if err == bcrypt.ErrMismatchedHashAndPassword {
		return e.ErrInvalidPassword
	} else if err != nil {
		return
	}

	// if account is not confirmed
	if !r.Confirmed {
		return e.ErrUserNotConfirmed
	}

	// if locked
	if r.Locked {
		return e.ErrUserLocked
	}

	// if banned
	if r.Banned {
		return e.ErrUserBanned
	}

	// make new session for user
	r.Sid, err = u.NewSession(r.Id)
	if err != nil {
		return
	}

	return

}
<|endoftext|>


<commit_before>package models

import (
	"fmt"
	"regexp"
	"time"

	"bitbucket.org/mundipagg/boletoapi/util"
)

// Title título de cobrança de entrada
type Title struct {
	CreateDate     time.Time `json:"createDate,omitempty"`
	ExpireDateTime time.Time `json:"expireDateTime,omitempty"`
	ExpireDate     string    `json:"expireDate,omitempty"`
	AmountInCents  uint64    `json:"amountInCents,omitempty"`
	OurNumber      uint      `json:"ourNumber,omitempty"`
	Instructions   string    `json:"instructions,omitempty"`
	DocumentNumber string    `json:"documentNumber,omitempty"`
}

//ValidateInstructionsLength valida se texto das instruções possui quantidade de caracteres corretos
func (t Title) ValidateInstructionsLength(max int) error {
	if len(t.Instructions) > max {
		return NewErrorResponse("MPInstructions", "Instruções não podem passar de 220 caracteres")
	}
	return nil
}

//ValidateDocumentNumber número do documento
func (t *Title) ValidateDocumentNumber() error {
	re := regexp.MustCompile("(\\D+)")
	ad := re.ReplaceAllString(t.DocumentNumber, "")
	if ad == "" {
		t.DocumentNumber = ad
	} else if len(ad) < 10 {
		t.DocumentNumber = util.PadLeft(ad, "0", 10)
	} else {
		t.DocumentNumber = ad[:10]
	}
	return nil
}

//IsExpireDateValid retorna um erro se a data de expiração for inválida
func (t *Title) IsExpireDateValid() error {
	d, err := parseDate(t.ExpireDate)
	if err != nil {
		return NewErrorResponse("MPExpireDate", fmt.Sprintf("Data em um formato inválido, esperamos AAAA-MM-DD e recebemos %s", t.ExpireDate))
	}
	n, _ := parseDate(time.Now().Format("2006-01-02"))
	t.CreateDate = n
	t.ExpireDateTime = d
	if t.CreateDate.After(t.ExpireDateTime) {
		return NewErrorResponse("MPExpireDate", "Data de expiração não pode ser menor que a data de hoje")
	}
	return nil
}

//IsAmountInCentsValid retorna um erro se o valor em centavos for inválido
func (t *Title) IsAmountInCentsValid() error {
	if t.AmountInCents < 1 {
		return NewErrorResponse("MPAmountInCents", "Valor não pode ser menor do que 1 centavo")
	}
	return nil
}

// GetCreateDate Retorna a data de crição do título
func (t *Title) GetCreateDate() time.Time {
	return t.CreateDate
}

func parseDate(t string) (time.Time, error) {
	date, err := time.Parse("2006-01-02", t)
	if err != nil {
		return time.Now(), err
	}
	return date, nil
}
<commit_msg>:art: Melhora mensagem de erro nas instruções<commit_after>package models

import (
	"fmt"
	"regexp"
	"time"

	"bitbucket.org/mundipagg/boletoapi/util"
)

// Title título de cobrança de entrada
type Title struct {
	CreateDate     time.Time `json:"createDate,omitempty"`
	ExpireDateTime time.Time `json:"expireDateTime,omitempty"`
	ExpireDate     string    `json:"expireDate,omitempty"`
	AmountInCents  uint64    `json:"amountInCents,omitempty"`
	OurNumber      uint      `json:"ourNumber,omitempty"`
	Instructions   string    `json:"instructions,omitempty"`
	DocumentNumber string    `json:"documentNumber,omitempty"`
}

//ValidateInstructionsLength valida se texto das instruções possui quantidade de caracteres corretos
func (t Title) ValidateInstructionsLength(max int) error {
	if len(t.Instructions) > max {
		return NewErrorResponse("MPInstructions", fmt.Sprintf("Instruções não podem passar de %d caracteres", max))
	}
	return nil
}

//ValidateDocumentNumber número do documento
func (t *Title) ValidateDocumentNumber() error {
	re := regexp.MustCompile("(\\D+)")
	ad := re.ReplaceAllString(t.DocumentNumber, "")
	if ad == "" {
		t.DocumentNumber = ad
	} else if len(ad) < 10 {
		t.DocumentNumber = util.PadLeft(ad, "0", 10)
	} else {
		t.DocumentNumber = ad[:10]
	}
	return nil
}

//IsExpireDateValid retorna um erro se a data de expiração for inválida
func (t *Title) IsExpireDateValid() error {
	d, err := parseDate(t.ExpireDate)
	if err != nil {
		return NewErrorResponse("MPExpireDate", fmt.Sprintf("Data em um formato inválido, esperamos AAAA-MM-DD e recebemos %s", t.ExpireDate))
	}
	n, _ := parseDate(time.Now().Format("2006-01-02"))
	t.CreateDate = n
	t.ExpireDateTime = d
	if t.CreateDate.After(t.ExpireDateTime) {
		return NewErrorResponse("MPExpireDate", "Data de expiração não pode ser menor que a data de hoje")
	}
	return nil
}

//IsAmountInCentsValid retorna um erro se o valor em centavos for inválido
func (t *Title) IsAmountInCentsValid() error {
	if t.AmountInCents < 1 {
		return NewErrorResponse("MPAmountInCents", "Valor não pode ser menor do que 1 centavo")
	}
	return nil
}

// GetCreateDate Retorna a data de crição do título
func (t *Title) GetCreateDate() time.Time {
	return t.CreateDate
}

func parseDate(t string) (time.Time, error) {
	date, err := time.Parse("2006-01-02", t)
	if err != nil {
		return time.Now(), err
	}
	return date, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"regexp"
	"strings"
)

type Section struct {
	Level    int
	Text     string
	Title    string
	Children []*Section
}

// read a section of the OpenAPI Specification, dividing it into subsections
func ReadSection(text string, level int) (section *Section, err error) {
	titlePattern := regexp.MustCompile("^" + strings.Repeat("#", level) + " .*$")
	subtitlePattern := regexp.MustCompile("^" + strings.Repeat("#", level+1) + " .*$")

	section = &Section{Level: level}
	lines := strings.Split(string(text), "\n")
	subsection := ""
	for i, line := range lines {
		if i == 0 && titlePattern.Match([]byte(line)) {
			section.Title = line
		} else if subtitlePattern.Match([]byte(line)) {
			if len(subsection) != 0 {
				child, _ := ReadSection(subsection, level+1)
				section.Children = append(section.Children, child)
			}
			subsection = line + "\n"
		} else {
			subsection += line + "\n"
		}
	}
	if len(section.Children) > 0 {
		child, _ := ReadSection(subsection, level+1)
		section.Children = append(section.Children, child)
	}
	// save the text
	section.Text = text
	return
}

// recursively display a section of the specification
func (s *Section) Display(section string) {
	if len(s.Children) == 0 {
		//fmt.Printf("%s\n", s.Text)
	} else {
		for i, child := range s.Children {
			var subsection string
			if section == "" {
				subsection = fmt.Sprintf("%d", i)
			} else {
				subsection = fmt.Sprintf("%s.%d", section, i)
			}
			fmt.Printf("%-12s %s\n", subsection, child.NiceTitle())
			child.Display(subsection)
		}
	}
}

// remove a link from a string, leaving only the text that follows it
// if there is no link, just return the string
func stripLink(input string) (output string) {
	stringPattern := regexp.MustCompile("^(.*)$")
	stringWithLinkPattern := regexp.MustCompile("^<a .*(.*)$")
	if matches := stringWithLinkPattern.FindSubmatch([]byte(input)); matches != nil {
		return string(matches[1])
	} else if matches := stringPattern.FindSubmatch([]byte(input)); matches != nil {
		return string(matches[1])
	} else {
		return input
	}
}

// return a nice-to-display title for a section that removes the opening "###" and any links
func (s *Section) NiceTitle() string {
	titlePattern := regexp.MustCompile("^#+ (.*)$")
	titleWithLinkPattern := regexp.MustCompile("^#+ <a .*(.*)$")
	if matches := titleWithLinkPattern.FindSubmatch([]byte(s.Title)); matches != nil {
		return string(matches[1])
	} else if matches := titlePattern.FindSubmatch([]byte(s.Title)); matches != nil {
		return string(matches[1])
	} else {
		return ""
	}
}

// replace markdown links with their link text (removing the URLs)
func removeMarkdownLinks(input string) (output string) {
	markdownLink := regexp.MustCompile("\\[([^\\]]*)\\]\\(([^\\)]*)\\)") // matches [link title](link url)
	output = string(markdownLink.ReplaceAll([]byte(input), []byte("$1")))
	return
}

// extract the fixed fields from a table in a section
func parseFixedFields(input string) {
	fmt.Printf("- FIXED FIELDS:\n")
	lines := strings.Split(input, "\n")
	for _, line := range lines {
		parts := strings.Split(line, "|")
		if len(parts) > 1 {
			fieldName := strings.Trim(stripLink(parts[0]), " ")
			if fieldName != "Field Name" && fieldName != "---" {
				typeName := parts[1]
				typeName = strings.Trim(typeName, " ")
				typeName = strings.Replace(typeName, "`", "", -1)
				typeName = strings.Replace(typeName, " | ", "|", -1)
				typeName = removeMarkdownLinks(typeName)
				typeName = strings.Replace(typeName, " ", "", -1)
				typeName = strings.Replace(typeName, "Object", "", -1)
				fmt.Printf("%-20s %s\n", fieldName, typeName)
			}
		}
	}
}

// extract the patterned fields from a table in a section
func parsePatternedFields(input string) {
	fmt.Printf("- PATTERNED FIELDS:\n")
	lines := strings.Split(input, "\n")
	for _, line := range lines {
		parts := strings.Split(line, "|")
		if len(parts) > 1 {
			fieldName := strings.Trim(stripLink(parts[0]), " ")
			fieldName = removeMarkdownLinks(fieldName)
			if fieldName != "Field Pattern" && fieldName != "---" {
				typeName := parts[1]
				typeName = strings.Trim(typeName, " ")
				typeName = strings.Replace(typeName, "`", "", -1)
				typeName = strings.Replace(typeName, " | ", "|", -1)
				typeName = removeMarkdownLinks(typeName)
				typeName = strings.Replace(typeName, " ", "", -1)
				typeName = strings.Replace(typeName, "Object", "", -1)
				fmt.Printf("%-20s %s\n", fieldName, typeName)
			}
		}
	}
}

func main() {
	b, err := ioutil.ReadFile("3.0.md")
	if err != nil {
		panic(err)
	}

	// divide the specification into sections
	document, err := ReadSection(string(b), 1)
	document.Display("")

	// read object names and their details
	specification := document.Children[4]
	schema := specification.Children[5]
	anchor := regexp.MustCompile("^####  0 {
				details := section.Children[0].Text
				details = removeMarkdownLinks(details)
				details = strings.Trim(details, " \t\n")
				fmt.Printf("- DESCRIPTION: {%s}\n", details)
			}

			// is the object extendable?
			if strings.Contains(section.Text, "Specification Extensions") {
				fmt.Printf("- EXTENDABLE\n")
			}

			// look for fixed fields
			for _, child := range section.Children {
				if child.NiceTitle() == "Fixed Fields" {
					parseFixedFields(child.Text)
				}
			}

			// look for patterned fields
			for _, child := range section.Children {
				if child.NiceTitle() == "Patterned Fields" {
					parsePatternedFields(child.Text)
				}
			}
		}
	}
}
<commit_msg>Add license header to reader.go<commit_after>// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main

import (
	"fmt"
	"io/ioutil"
	"regexp"
	"strings"
)

type Section struct {
	Level    int
	Text     string
	Title    string
	Children []*Section
}

// read a section of the OpenAPI Specification, dividing it into subsections
func ReadSection(text string, level int) (section *Section, err error) {
	titlePattern := regexp.MustCompile("^" + strings.Repeat("#", level) + " .*$")
	subtitlePattern := regexp.MustCompile("^" + strings.Repeat("#", level+1) + " .*$")

	section = &Section{Level: level}
	lines := strings.Split(string(text), "\n")
	subsection := ""
	for i, line := range lines {
		if i == 0 && titlePattern.Match([]byte(line)) {
			section.Title = line
		} else if subtitlePattern.Match([]byte(line)) {
			if len(subsection) != 0 {
				child, _ := ReadSection(subsection, level+1)
				section.Children = append(section.Children, child)
			}
			subsection = line + "\n"
		} else {
			subsection += line + "\n"
		}
	}
	if len(section.Children) > 0 {
		child, _ := ReadSection(subsection, level+1)
		section.Children = append(section.Children, child)
	}
	// save the text
	section.Text = text
	return
}

// recursively display a section of the specification
func (s *Section) Display(section string) {
	if len(s.Children) == 0 {
		//fmt.Printf("%s\n", s.Text)
	} else {
		for i, child := range s.Children {
			var subsection string
			if section == "" {
				subsection = fmt.Sprintf("%d", i)
			} else {
				subsection = fmt.Sprintf("%s.%d", section, i)
			}
			fmt.Printf("%-12s %s\n", subsection, child.NiceTitle())
			child.Display(subsection)
		}
	}
}

// remove a link from a string, leaving only the text that follows it
// if there is no link, just return the string
func stripLink(input string) (output string) {
	stringPattern := regexp.MustCompile("^(.*)$")
	stringWithLinkPattern := regexp.MustCompile("^<a .*(.*)$")
	if matches := stringWithLinkPattern.FindSubmatch([]byte(input)); matches != nil {
		return string(matches[1])
	} else if matches := stringPattern.FindSubmatch([]byte(input)); matches != nil {
		return string(matches[1])
	} else {
		return input
	}
}

// return a nice-to-display title for a section that removes the opening "###" and any links
func (s *Section) NiceTitle() string {
	titlePattern := regexp.MustCompile("^#+ (.*)$")
	titleWithLinkPattern := regexp.MustCompile("^#+ <a .*(.*)$")
	if matches := titleWithLinkPattern.FindSubmatch([]byte(s.Title)); matches != nil {
		return string(matches[1])
	} else if matches := titlePattern.FindSubmatch([]byte(s.Title)); matches != nil {
		return string(matches[1])
	} else {
		return ""
	}
}

// replace markdown links with their link text (removing the URLs)
func removeMarkdownLinks(input string) (output string) {
	markdownLink := regexp.MustCompile("\\[([^\\]]*)\\]\\(([^\\)]*)\\)") // matches [link title](link url)
	output = string(markdownLink.ReplaceAll([]byte(input), []byte("$1")))
	return
}

// extract the fixed fields from a table in a section
func parseFixedFields(input string) {
	fmt.Printf("- FIXED FIELDS:\n")
	lines := strings.Split(input, "\n")
	for _, line := range lines {
		parts := strings.Split(line, "|")
		if len(parts) > 1 {
			fieldName := strings.Trim(stripLink(parts[0]), " ")
			if fieldName != "Field Name" && fieldName != "---" {
				typeName := parts[1]
				typeName = strings.Trim(typeName, " ")
				typeName = strings.Replace(typeName, "`", "", -1)
				typeName = strings.Replace(typeName, " | ", "|", -1)
				typeName = removeMarkdownLinks(typeName)
				typeName = strings.Replace(typeName, " ", "", -1)
				typeName = strings.Replace(typeName, "Object", "", -1)
				fmt.Printf("%-20s %s\n", fieldName, typeName)
			}
		}
	}
}

// extract the patterned fields from a table in a section
func parsePatternedFields(input string) {
	fmt.Printf("- PATTERNED FIELDS:\n")
	lines := strings.Split(input, "\n")
	for _, line := range lines {
		parts := strings.Split(line, "|")
		if len(parts) > 1 {
			fieldName := strings.Trim(stripLink(parts[0]), " ")
			fieldName = removeMarkdownLinks(fieldName)
			if fieldName != "Field Pattern" && fieldName != "---" {
				typeName := parts[1]
				typeName = strings.Trim(typeName, " ")
				typeName = strings.Replace(typeName, "`", "", -1)
				typeName = strings.Replace(typeName, " | ", "|", -1)
				typeName = removeMarkdownLinks(typeName)
				typeName = strings.Replace(typeName, " ", "", -1)
				typeName = strings.Replace(typeName, "Object", "", -1)
				fmt.Printf("%-20s %s\n", fieldName, typeName)
			}
		}
	}
}

func main() {
	b, err := ioutil.ReadFile("3.0.md")
	if err != nil {
		panic(err)
	}

	// divide the specification into sections
	document, err := ReadSection(string(b), 1)
	document.Display("")

	// read object names and their details
	specification := document.Children[4]
	schema := specification.Children[5]
	anchor := regexp.MustCompile("^####  0 {
				details := section.Children[0].Text
				details = removeMarkdownLinks(details)
				details = strings.Trim(details, " \t\n")
				fmt.Printf("- DESCRIPTION: {%s}\n", details)
			}

			// is the object extendable?
			if strings.Contains(section.Text, "Specification Extensions") {
				fmt.Printf("- EXTENDABLE\n")
			}

			// look for fixed fields
			for _, child := range section.Children {
				if child.NiceTitle() == "Fixed Fields" {
					parseFixedFields(child.Text)
				}
			}

			// look for patterned fields
			for _, child := range section.Children {
				if child.NiceTitle() == "Patterned Fields" {
					parsePatternedFields(child.Text)
				}
			}
		}
	}
}
<|endoftext|>