diff --git a/job.go b/job.go new file mode 100644 index 0000000..6a05b23 --- /dev/null +++ b/job.go @@ -0,0 +1,156 @@ +package job + +import ( + "fmt" + "os" + "sort" + "strings" + "time" +) + +type StringSlice sort.StringSlice + +type Job struct { + Id uint + Archived bool + Customer string + Name string + Taxa []string + Tags sort.StringSlice + Watching sort.StringSlice + Notified map[string]time.Time +} + +func NewJob(id uint, customer string, name string) *Job { + job := &Job{ + Id: id, + Archived: false, + Customer: customer, + Name: name, + Taxa: sort.StringSlice{}, + Tags: sort.StringSlice{}, + Watching: sort.StringSlice{}, + Notified: map[string]time.Time{}, + } + return job +} + +func union(slices ...sort.StringSlice) sort.StringSlice { + set := map[string]bool{} + for _, slice := range slices { + for _, key := range slice { + set[key] = true + } + } + keys := make(sort.StringSlice, len(set)) + i := 0 + for key, _ := range set { + keys[i] = key + i++ + } + keys.Sort() + return keys +} + +func (job *Job) AddTaxa(taxa ...string) { + job.Taxa = append(job.Taxa, taxa...) +} + +func (job *Job) AddTags(tags ...string) { + job.Tags = union(job.Tags, tags) +} + +func (job *Job) Watch(who ...string) { + job.Watching = union(job.Watching, who) +} + +func (job *Job) Notify(who ...string) { + for _, user := range who { + job.Notified[user] = time.Now() + } +} + +func (job *Job) Shard() string { + upper := job.Id / 1000 + return fmt.Sprintf("%dxxx", upper) +} + +func (job *Job) Path() string { + return fmt.Sprintf("Jobs/%s/%d", job.Shard(), job.Id) +} + +func (job *Job) TaxonomyDirectories() sort.StringSlice { + n := len(job.Taxa) + parts := make([][]string, (n*n+n)/2) + + // e.g. [x,y,z] -> [[x], [x, y], [x, y, z]] + for i := range job.Taxa { + parts[i] = job.Taxa[0 : i+1] + } + + // e.g. [[x], [x, y], [x, y, z]] -> [[y], [y, z]] + [[z]] + k := n + for i := 1; i < n; i++ { + for j := i; j < n; j++ { + parts[k] = job.Taxa[i : j+1] + k++ + } + } + + dirs := make([]string, k) + upper := job.Id / 1000 + for i, part := range parts { + dirs[i] = fmt.Sprintf("Customers/%s/%s/• Jobs/%dxxx", job.Customer, strings.Join(part, "/"), upper) + } + return dirs +} + +func (job *Job) CustomerDirectory() string { + return fmt.Sprintf("Customers/%s/• Jobs/%s", job.Customer, job.Shard()) +} + +func (job *Job) SymlinkDirectories() sort.StringSlice { + customer := sort.StringSlice{job.CustomerDirectory()} + taxonomy := job.TaxonomyDirectories() + + return union(customer, taxonomy) +} + +func (job *Job) AllDirectories() sort.StringSlice { + original := sort.StringSlice{job.Path()} + symlinks := job.SymlinkDirectories() + + return union(original, symlinks) +} + +func (job *Job) Description() string { + return fmt.Sprintf("%d | %s | %s", job.Id, strings.Join(job.Taxa, " ▶ "), job.Name) +} + +func (job *Job) Create(where string) error { + for _, dir := range job.AllDirectories() { + err := os.MkdirAll(where+"/"+dir, 0755) + if err != nil { + return err + } + } + + original := job.Path() + description := job.Description() + for _, dir := range job.SymlinkDirectories() { + err := os.Remove(where + "/" + dir + "/." + description) + if err != nil && !os.IsNotExist(err) { + return err + } + err = os.Symlink(where+"/"+original, where+"/"+dir+"/."+description) + if err != nil { + return err + } + err = os.Rename(where+"/"+dir+"/."+description, where+"/"+dir+"/"+description) + if err != nil { + return err + } + } + + return nil +} \ No newline at end of file