PageRenderTime 27ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/data/data.go

https://bitbucket.org/nexneo/samay
Go | 254 lines | 203 code | 45 blank | 6 comment | 26 complexity | af0deadb5ec0864bef66cfba9b01434f MD5 | raw file
  1. package data
  2. import (
  3. "errors"
  4. "fmt"
  5. "os"
  6. "regexp"
  7. "sort"
  8. "strings"
  9. "time"
  10. "github.com/golang/protobuf/proto"
  11. "github.com/nexneo/samay/util"
  12. )
  13. var (
  14. _ = fmt.Errorf
  15. _ = errors.New
  16. )
  17. // Types
  18. type Persistable interface {
  19. locator
  20. proto.Message
  21. prepareForSave() error
  22. }
  23. type Destroyable interface {
  24. locator
  25. cleanupAfterRemove() error
  26. }
  27. type locator interface {
  28. Location() string
  29. }
  30. // Project
  31. func CreateProject(name string) *Project {
  32. project := new(Project)
  33. project.Name = proto.String(name)
  34. return project
  35. }
  36. func (p *Project) prepareForSave() error {
  37. return DB.MkProjectDir(p)
  38. }
  39. func (p *Project) cleanupAfterRemove() error {
  40. return os.RemoveAll(DB.ProjectDirPath(p))
  41. }
  42. func (p *Project) Location() string {
  43. return DB.ProjectDirPath(p) + "/project.db"
  44. }
  45. func (p *Project) GetShaFromName() string {
  46. if p.GetName() != "" {
  47. return util.SHA1(p.GetName())
  48. }
  49. // Then try if Sha is set, from directory name.
  50. if sha := p.GetSha(); sha != "" {
  51. return sha
  52. }
  53. // mostly like Project name is not set, use default.
  54. return util.SHA1("")
  55. }
  56. type byEndTime []*Entry
  57. func (f byEndTime) Len() int { return len(f) }
  58. func (f byEndTime) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
  59. func (f byEndTime) Less(i, j int) bool {
  60. return *f[i].Ended > *f[j].Ended
  61. }
  62. func (p *Project) Entries() (entries []*Entry) {
  63. files, err := util.ReadDir(DB.ProjectDirPath(p) + "/entries")
  64. if err != nil {
  65. return
  66. }
  67. for _, file := range files {
  68. entry := new(Entry)
  69. entry.Project = p
  70. if file.Name() == ".DS_Store" {
  71. continue
  72. }
  73. entry.Id = proto.String(file.Name())
  74. if err = Load(entry); err == nil {
  75. entry.GetEnded()
  76. entries = append(entries, entry)
  77. } else {
  78. fmt.Println("Failed:", file.Name(), ":", err)
  79. }
  80. }
  81. sort.Sort(byEndTime(entries))
  82. return
  83. }
  84. // Timer
  85. func CreateTimer(project *Project) *Timer {
  86. timer := new(Timer)
  87. timer.Project = project
  88. timer.Start()
  89. return timer
  90. }
  91. func GetTimer(project *Project) *Timer {
  92. timer := new(Timer)
  93. timer.Project = project
  94. err := Load(timer)
  95. if err != nil {
  96. timer = CreateTimer(project)
  97. timer.Started = proto.Int64(0)
  98. }
  99. return timer
  100. }
  101. func (t *Timer) prepareForSave() error {
  102. return DB.MkProjectDir(t.GetProject())
  103. }
  104. func (p *Timer) cleanupAfterRemove() error {
  105. return nil
  106. }
  107. func (t *Timer) Location() string {
  108. return DB.ProjectDirPath(t.GetProject()) + "/timer.db"
  109. }
  110. func (t *Timer) Start() {
  111. t.Started = proto.Int64(time.Now().Unix())
  112. }
  113. func (t *Timer) Stop(e *Entry) error {
  114. stopped := time.Now()
  115. s, err := t.StartedTime()
  116. if err != nil {
  117. return err
  118. }
  119. e.Project = t.GetProject()
  120. e.Started = t.Started
  121. e.Ended = proto.Int64(stopped.Unix())
  122. e.Duration = proto.Int64(stopped.Sub(*s).Nanoseconds())
  123. if err = Save(e); err != nil {
  124. return err
  125. }
  126. if err = Destroy(t); err != nil {
  127. return err
  128. }
  129. return nil
  130. }
  131. func (t *Timer) StartedTime() (*time.Time, error) {
  132. return util.TimestampToTime(t.Started)
  133. }
  134. func (t *Timer) Duration() time.Duration {
  135. v, _ := t.StartedTime()
  136. return time.Now().Sub(*v)
  137. }
  138. // Entry
  139. func (project *Project) CreateEntry(content string, billable bool) *Entry {
  140. content = strings.Trim(content, " \n\t\r")
  141. tags := make([]string, 0, 20)
  142. tagsFinder := regexp.MustCompile("\\B#(\\w\\w+)")
  143. for _, v := range tagsFinder.FindAllStringSubmatch(content, 20) {
  144. if len(v) > 1 {
  145. tags = append(tags, v[1])
  146. }
  147. }
  148. e := Entry{
  149. Project: project,
  150. Tags: tags,
  151. Content: proto.String(content),
  152. Billable: proto.Bool(billable),
  153. }
  154. id, _ := util.UUID()
  155. e.Id = proto.String(id)
  156. return &e
  157. }
  158. func (project *Project) CreateEntryWithDuration(
  159. content string,
  160. duration time.Duration,
  161. billable bool) *Entry {
  162. e := project.CreateEntry(content, billable)
  163. endTime := time.Now()
  164. startTime := endTime.Add(-duration)
  165. e.Started = proto.Int64(startTime.Unix())
  166. e.Ended = proto.Int64(endTime.Unix())
  167. e.Duration = proto.Int64(duration.Nanoseconds())
  168. return e
  169. }
  170. func (project *Project) StopTimer(c string, bill bool) (err error) {
  171. if yes, timer := project.OnClock(); yes {
  172. entry := project.CreateEntry(c, bill)
  173. if err = timer.Stop(entry); err == nil {
  174. fmt.Printf("%.2f mins\n", entry.Minutes())
  175. }
  176. }
  177. return
  178. }
  179. func (project *Project) StartTimer() (err error) {
  180. Save(project)
  181. timer := CreateTimer(project)
  182. err = Save(timer)
  183. return
  184. }
  185. func (e *Entry) prepareForSave() error {
  186. if err := os.MkdirAll(DB.EntryDirPath(e), 0755); !os.IsExist(err) {
  187. return err
  188. }
  189. return nil
  190. }
  191. func (p *Entry) cleanupAfterRemove() error {
  192. return nil
  193. }
  194. func (e *Entry) Location() string {
  195. return DB.EntryDirPath(e) + "/" + e.GetId()
  196. }
  197. func (e *Entry) StartedTime() (*time.Time, error) {
  198. return util.TimestampToTime(e.Started)
  199. }
  200. func (e *Entry) EndedTime() (*time.Time, error) {
  201. return util.TimestampToTime(e.Ended)
  202. }
  203. func (e *Entry) Minutes() float64 {
  204. return time.Duration(*e.Duration).Minutes()
  205. }
  206. func (e *Entry) HoursMins() hoursMins {
  207. d := time.Duration(*e.Duration)
  208. return hoursMinsFromDuration(d)
  209. }