package schedule import ( "fmt" "strconv" "encoding/json" "github.com/tealeg/xlsx" ) func NewGenerator(data *Generator) *Generator { return &Generator{ Day: data.Day, Debug: data.Debug, Groups: data.Groups, Teachers: data.Teachers, Blocked: make(map[string]bool), Reserved: Reserved{ Teachers: make(map[string][]bool), Cabinets: make(map[string][]bool), }, } } func (gen *Generator) NewSchedule(group string) *Schedule { return &Schedule{ Day: gen.Day, Group: group, Table: make([]Row, NUM_TABLES), } } func ReadGroups(filename string) map[string]*Group { var ( groups = make(map[string]*Group) groupsList []GroupJSON ) data := readFile(filename) err := json.Unmarshal([]byte(data), &groupsList) if err != nil { return nil } for _, gr := range groupsList { groups[gr.Name] = &Group{ Name: gr.Name, Quantity: gr.Quantity, } groups[gr.Name].Subjects = make(map[string]*Subject) for _, sb := range gr.Subjects { if _, ok := groups[gr.Name].Subjects[sb.Name]; ok { groups[gr.Name].Subjects[sb.Name].Teacher2 = sb.Teacher continue } groups[gr.Name].Subjects[sb.Name] = &Subject{ Name: sb.Name, Teacher: sb.Teacher, IsComputer: sb.IsComputer, SaveWeek: sb.Lessons.Week, Theory: sb.Lessons.Theory, Practice: Subgroup{ A: sb.Lessons.Practice, B: sb.Lessons.Practice, }, WeekLessons: Subgroup{ A: sb.Lessons.Week, B: sb.Lessons.Week, }, } } } return groups } func ReadTeachers(filename string) map[string]*Teacher { var ( teachers = make(map[string]*Teacher) teachersList []Teacher ) data := readFile(filename) err := json.Unmarshal([]byte(data), &teachersList) if err != nil { return nil } for _, tc := range teachersList { teachers[tc.Name] = &Teacher{ Name: tc.Name, Cabinets: tc.Cabinets, } } return teachers } const ( OUTDATA = "output/" ) func (gen *Generator) Template() [][]*Schedule { var ( weekLessons = make([][]*Schedule, 7) generator = new(Generator) file *xlsx.File name string ) unpackJSON(packJSON(gen), generator) if gen.Debug { file, name = CreateXLSX(OUTDATA + "template.xlsx") } for i := generator.Day; i < generator.Day+7; i++ { weekLessons[i % 7] = generator.Generate(nil) if gen.Debug { generator.WriteXLSX( file, name, weekLessons[i], int(i), ) } } return weekLessons } func (gen *Generator) Generate(template [][]*Schedule) []*Schedule { var ( list []*Schedule templt []*Schedule groups = getGroups(gen.Groups) ) if template == nil { templt = nil } else { templt = template[gen.Day] } for _, group := range groups { var ( schedule = gen.NewSchedule(group.Name) subjects = getSubjects(group.Subjects) countLessons = new(Subgroup) ) if gen.Day == SUNDAY { list = append(list, schedule) for _, subject := range subjects { saved := gen.Groups[group.Name].Subjects[subject.Name].SaveWeek gen.Groups[group.Name].Subjects[subject.Name].WeekLessons.A = saved gen.Groups[group.Name].Subjects[subject.Name].WeekLessons.B = saved } continue } for _, subject := range subjects { switch { case gen.haveTheoreticalLessons(subject): if gen.Debug { fmt.Println(group.Name, subject.Name, ": not splited THEORETICAL;") } gen.tryGenerate(ALL, THEORETICAL, group, subject, schedule, countLessons, templt) // Практические пары начинаются только после завершения всех теоретических. default: // Если подгруппа неделимая, тогда провести практику в виде полной пары. // Иначе разделить практику на две подгруппы. if !gen.withSubgroups(group.Name) { if gen.Debug { fmt.Println(group.Name, subject.Name, ": not splited PRACTICAL;") } gen.tryGenerate(ALL, PRACTICAL, group, subject, schedule, countLessons, templt) } else { switch RandSubgroup() { case A: if gen.Debug { fmt.Println(group.Name, subject.Name, ": splited (A -> B);") } gen.tryGenerate(A, PRACTICAL, group, subject, schedule, countLessons, templt) gen.tryGenerate(B, PRACTICAL, group, subject, schedule, countLessons, templt) case B: if gen.Debug { fmt.Println(group.Name, subject.Name, ": splited (B -> A);") } gen.tryGenerate(B, PRACTICAL, group, subject, schedule, countLessons, templt) gen.tryGenerate(A, PRACTICAL, group, subject, schedule, countLessons, templt) } } } } list = append(list, schedule) } gen.Reserved.Teachers = make(map[string][]bool) gen.Reserved.Cabinets = make(map[string][]bool) gen.Day = (gen.Day + 1) % 7 return sortSchedule(list) } func CreateXLSX(filename string) (*xlsx.File, string) { file := xlsx.NewFile() _, err := file.AddSheet("Init") if err != nil { return nil, "" } err = file.Save(filename) if err != nil { return nil, "" } return file, filename } func (gen *Generator) WriteXLSX(file *xlsx.File, filename string, schedule []*Schedule, iter int) error { const ( colWidth = 30 rowHeight = 30 ) var ( MAXCOL = uint(3) ) rowsNext := uint(len(schedule)) / MAXCOL if rowsNext == 0 || uint(len(schedule)) % MAXCOL != 0 { rowsNext += 1 } var ( colNum = uint(NUM_TABLES + 2) row = make([]*xlsx.Row, colNum * rowsNext) // * (rowsNext + 1) cell *xlsx.Cell dayN = gen.Day day = "" ) if dayN == SUNDAY { dayN = SATURDAY } else { dayN -= 1 } switch dayN { case SUNDAY: day = "Sunday" case MONDAY: day = "Monday" case TUESDAY: day = "Tuesday" case WEDNESDAY: day = "Wednesday" case THURSDAY: day = "Thursday" case FRIDAY: day = "Friday" case SATURDAY: day = "Saturday" } sheet, err := file.AddSheet(day + "-" + strconv.Itoa(iter)) if err != nil { return err } sheet.SetColWidth(2, int(MAXCOL)*3+1, COL_W) for r := uint(0); r < rowsNext; r++ { for i := uint(0); i < colNum; i++ { row[(r*colNum)+i] = sheet.AddRow() // (r*rowsNext)+ row[(r*colNum)+i].SetHeight(ROW_H) cell = row[(r*colNum)+i].AddCell() if i == 0 { cell.Value = "Пара" continue } cell.Value = strconv.Itoa(int(i-1)) } } index := uint(0) exit: for r := uint(0); r < rowsNext; r++ { for i := uint(0); i < MAXCOL; i++ { if uint(len(schedule)) <= index { break exit } savedCell := row[(r*colNum)+0].AddCell() savedCell.Value = "Группа " + schedule[index].Group cell = row[(r*colNum)+0].AddCell() cell = row[(r*colNum)+0].AddCell() savedCell.Merge(2, 0) cell = row[(r*colNum)+1].AddCell() cell.Value = "Предмет" cell = row[(r*colNum)+1].AddCell() cell.Value = "Преподаватель" cell = row[(r*colNum)+1].AddCell() cell.Value = "Кабинет" for j, trow := range schedule[index].Table { cell = row[(r*colNum)+uint(j)+2].AddCell() if trow.Subject[A] == trow.Subject[B] { cell.Value = trow.Subject[A] } else { if trow.Subject[A] != "" { cell.Value = trow.Subject[A] + " (A)" } if trow.Subject[B] != "" { cell.Value += "\n" + trow.Subject[B] + " (B)" } } cell = row[(r*colNum)+uint(j)+2].AddCell() if trow.Teacher[A] == trow.Teacher[B] { cell.Value = trow.Teacher[A] } else { if trow.Teacher[A] != "" { cell.Value = trow.Teacher[A] } if trow.Teacher[B] != "" { cell.Value += "\n" + trow.Teacher[B] } } sheet.SetColWidth(colWidthForCabinets(int(j))) cell = row[(r*colNum)+uint(j)+2].AddCell() if trow.Cabinet[A] == trow.Cabinet[B] { cell.Value = trow.Cabinet[A] } else { if trow.Cabinet[A] != "" { cell.Value = trow.Cabinet[A] } if trow.Cabinet[B] != "" { cell.Value += "\n" + trow.Cabinet[B] } } } index++ } } err = file.Save(filename) if err != nil { return err } return nil } func RandSubgroup() SubgroupType { return SubgroupType(random(0, 1)) } func Load(filename string) *Generator { var generator = new(Generator) jsonData := readFile(filename) err := json.Unmarshal([]byte(jsonData), generator) if err != nil { return nil } return generator } func (gen *Generator) Dump(filename string) error { return writeJSON(filename, gen) }