Helper to maintain a file system hierarchy for job folders
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

156 lines
3.2 KiB

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
}