// report parses life report JSON and package report import ( "encoding/json" "fmt" "html/template" "os" "path" "strconv" "strings" ) type ( // Report represents a life report. It contains summaries and lists on // various areas of a given person's life for the given time period -- // in this case, a month. Report struct { File string Num int Month string Year int `json:"year"` Summary string `json:"summary"` Sections []Section `json:"sections"` } // Section represents an area of life to report on. This contains a // title to describe the area, a set of paragraphs representing a // summary, and a set of details representing a list of more specific // items. Section struct { Title string `json:"title"` Summary []template.HTML `json:"summary"` Details []template.HTML `json:"details"` Rank []template.HTML `json:"rank"` } ) // ParseReport takes a file and generates a life report from it. It // assumes the file is named in the format NNN-month.json where NNN is the // number of the report, usually named with leading zeroes so they're // ordered correctly on your filesystem. func ParseReport(f *os.File) (*Report, error) { var err error r := Report{} // Parse JSON into a Report jsonParser := json.NewDecoder(f) if err = jsonParser.Decode(&r); err != nil { return nil, err } // Determine report number and month from filename in the format // NNN-month.json r.File = path.Base(f.Name()) fd := strings.Split(r.File[:strings.Index(r.File, ".json")], "-") if len(fd) >= 2 { r.Num, err = strconv.Atoi(fd[0]) if err != nil { fmt.Fprintf(os.Stderr, "Unable to parse report number: %v\n", err) r.Num = 1 } r.Month = strings.Title(fd[1]) } else { r.Num = 1 r.Month = "First" } return &r, nil } // ByNum implements sort.Interface for []Report based on the Num field. type ByNum []Report func (n ByNum) Len() int { return len(n) } func (n ByNum) Swap(i, j int) { n[i], n[j] = n[j], n[i] } func (n ByNum) Less(i, j int) bool { return n[i].Num < n[j].Num }