1
0

generator.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. package schedule
  2. import (
  3. "fmt"
  4. "strconv"
  5. "encoding/json"
  6. "github.com/tealeg/xlsx"
  7. )
  8. func NewGenerator(data *Generator) *Generator {
  9. return &Generator{
  10. Day: data.Day,
  11. Debug: data.Debug,
  12. Groups: data.Groups,
  13. Teachers: data.Teachers,
  14. Blocked: make(map[string]bool),
  15. Reserved: Reserved{
  16. Teachers: make(map[string][]bool),
  17. Cabinets: make(map[string][]bool),
  18. },
  19. }
  20. }
  21. func (gen *Generator) NewSchedule(group string) *Schedule {
  22. return &Schedule{
  23. Day: gen.Day,
  24. Group: group,
  25. Table: make([]Row, NUM_TABLES),
  26. }
  27. }
  28. func ReadGroups(filename string) map[string]*Group {
  29. var (
  30. groups = make(map[string]*Group)
  31. groupsList []GroupJSON
  32. )
  33. data := readFile(filename)
  34. err := json.Unmarshal([]byte(data), &groupsList)
  35. if err != nil {
  36. return nil
  37. }
  38. for _, gr := range groupsList {
  39. groups[gr.Name] = &Group{
  40. Name: gr.Name,
  41. Quantity: gr.Quantity,
  42. }
  43. groups[gr.Name].Subjects = make(map[string]*Subject)
  44. for _, sb := range gr.Subjects {
  45. if _, ok := groups[gr.Name].Subjects[sb.Name]; ok {
  46. groups[gr.Name].Subjects[sb.Name].Teacher2 = sb.Teacher
  47. continue
  48. }
  49. groups[gr.Name].Subjects[sb.Name] = &Subject{
  50. Name: sb.Name,
  51. Teacher: sb.Teacher,
  52. IsComputer: sb.IsComputer,
  53. SaveWeek: sb.Lessons.Week,
  54. Theory: sb.Lessons.Theory,
  55. Practice: Subgroup{
  56. A: sb.Lessons.Practice,
  57. B: sb.Lessons.Practice,
  58. },
  59. WeekLessons: Subgroup{
  60. A: sb.Lessons.Week,
  61. B: sb.Lessons.Week,
  62. },
  63. }
  64. }
  65. }
  66. return groups
  67. }
  68. func ReadTeachers(filename string) map[string]*Teacher {
  69. var (
  70. teachers = make(map[string]*Teacher)
  71. teachersList []Teacher
  72. )
  73. data := readFile(filename)
  74. err := json.Unmarshal([]byte(data), &teachersList)
  75. if err != nil {
  76. return nil
  77. }
  78. for _, tc := range teachersList {
  79. teachers[tc.Name] = &Teacher{
  80. Name: tc.Name,
  81. Cabinets: tc.Cabinets,
  82. }
  83. }
  84. return teachers
  85. }
  86. const (
  87. OUTDATA = "output/"
  88. )
  89. func (gen *Generator) Template() [][]*Schedule {
  90. var (
  91. weekLessons = make([][]*Schedule, 7)
  92. generator = new(Generator)
  93. file *xlsx.File
  94. name string
  95. )
  96. unpackJSON(packJSON(gen), generator)
  97. if gen.Debug {
  98. file, name = CreateXLSX(OUTDATA + "template.xlsx")
  99. }
  100. for i := generator.Day; i < generator.Day+7; i++ {
  101. weekLessons[i % 7] = generator.Generate(nil)
  102. if gen.Debug {
  103. generator.WriteXLSX(
  104. file,
  105. name,
  106. weekLessons[i],
  107. int(i),
  108. )
  109. }
  110. }
  111. return weekLessons
  112. }
  113. func (gen *Generator) Generate(template [][]*Schedule) []*Schedule {
  114. var (
  115. list []*Schedule
  116. templt []*Schedule
  117. groups = getGroups(gen.Groups)
  118. )
  119. if template == nil {
  120. templt = nil
  121. } else {
  122. templt = template[gen.Day]
  123. }
  124. for _, group := range groups {
  125. var (
  126. schedule = gen.NewSchedule(group.Name)
  127. subjects = getSubjects(group.Subjects)
  128. countLessons = new(Subgroup)
  129. )
  130. if gen.Day == SUNDAY {
  131. list = append(list, schedule)
  132. for _, subject := range subjects {
  133. saved := gen.Groups[group.Name].Subjects[subject.Name].SaveWeek
  134. gen.Groups[group.Name].Subjects[subject.Name].WeekLessons.A = saved
  135. gen.Groups[group.Name].Subjects[subject.Name].WeekLessons.B = saved
  136. }
  137. continue
  138. }
  139. for _, subject := range subjects {
  140. switch {
  141. case gen.haveTheoreticalLessons(subject):
  142. if gen.Debug {
  143. fmt.Println(group.Name, subject.Name, ": not splited THEORETICAL;")
  144. }
  145. gen.tryGenerate(ALL, THEORETICAL, group, subject, schedule, countLessons, templt)
  146. // Практические пары начинаются только после завершения всех теоретических.
  147. default:
  148. // Если подгруппа неделимая, тогда провести практику в виде полной пары.
  149. // Иначе разделить практику на две подгруппы.
  150. if !gen.withSubgroups(group.Name) {
  151. if gen.Debug {
  152. fmt.Println(group.Name, subject.Name, ": not splited PRACTICAL;")
  153. }
  154. gen.tryGenerate(ALL, PRACTICAL, group, subject, schedule, countLessons, templt)
  155. } else {
  156. switch RandSubgroup() {
  157. case A:
  158. if gen.Debug {
  159. fmt.Println(group.Name, subject.Name, ": splited (A -> B);")
  160. }
  161. gen.tryGenerate(A, PRACTICAL, group, subject, schedule, countLessons, templt)
  162. gen.tryGenerate(B, PRACTICAL, group, subject, schedule, countLessons, templt)
  163. case B:
  164. if gen.Debug {
  165. fmt.Println(group.Name, subject.Name, ": splited (B -> A);")
  166. }
  167. gen.tryGenerate(B, PRACTICAL, group, subject, schedule, countLessons, templt)
  168. gen.tryGenerate(A, PRACTICAL, group, subject, schedule, countLessons, templt)
  169. }
  170. }
  171. }
  172. }
  173. list = append(list, schedule)
  174. }
  175. gen.Reserved.Teachers = make(map[string][]bool)
  176. gen.Reserved.Cabinets = make(map[string][]bool)
  177. gen.Day = (gen.Day + 1) % 7
  178. return sortSchedule(list)
  179. }
  180. func CreateXLSX(filename string) (*xlsx.File, string) {
  181. file := xlsx.NewFile()
  182. _, err := file.AddSheet("Init")
  183. if err != nil {
  184. return nil, ""
  185. }
  186. err = file.Save(filename)
  187. if err != nil {
  188. return nil, ""
  189. }
  190. return file, filename
  191. }
  192. func (gen *Generator) WriteXLSX(file *xlsx.File, filename string, schedule []*Schedule, iter int) error {
  193. const (
  194. colWidth = 30
  195. rowHeight = 30
  196. )
  197. var (
  198. MAXCOL = uint(3)
  199. )
  200. rowsNext := uint(len(schedule)) / MAXCOL
  201. if rowsNext == 0 || uint(len(schedule)) % MAXCOL != 0 {
  202. rowsNext += 1
  203. }
  204. var (
  205. colNum = uint(NUM_TABLES + 2)
  206. row = make([]*xlsx.Row, colNum * rowsNext) // * (rowsNext + 1)
  207. cell *xlsx.Cell
  208. dayN = gen.Day
  209. day = ""
  210. )
  211. if dayN == SUNDAY {
  212. dayN = SATURDAY
  213. } else {
  214. dayN -= 1
  215. }
  216. switch dayN {
  217. case SUNDAY: day = "Sunday"
  218. case MONDAY: day = "Monday"
  219. case TUESDAY: day = "Tuesday"
  220. case WEDNESDAY: day = "Wednesday"
  221. case THURSDAY: day = "Thursday"
  222. case FRIDAY: day = "Friday"
  223. case SATURDAY: day = "Saturday"
  224. }
  225. sheet, err := file.AddSheet(day + "-" + strconv.Itoa(iter))
  226. if err != nil {
  227. return err
  228. }
  229. sheet.SetColWidth(2, int(MAXCOL)*3+1, COL_W)
  230. for r := uint(0); r < rowsNext; r++ {
  231. for i := uint(0); i < colNum; i++ {
  232. row[(r*colNum)+i] = sheet.AddRow() // (r*rowsNext)+
  233. row[(r*colNum)+i].SetHeight(ROW_H)
  234. cell = row[(r*colNum)+i].AddCell()
  235. if i == 0 {
  236. cell.Value = "Пара"
  237. continue
  238. }
  239. cell.Value = strconv.Itoa(int(i-1))
  240. }
  241. }
  242. index := uint(0)
  243. exit: for r := uint(0); r < rowsNext; r++ {
  244. for i := uint(0); i < MAXCOL; i++ {
  245. if uint(len(schedule)) <= index {
  246. break exit
  247. }
  248. savedCell := row[(r*colNum)+0].AddCell()
  249. savedCell.Value = "Группа " + schedule[index].Group
  250. cell = row[(r*colNum)+0].AddCell()
  251. cell = row[(r*colNum)+0].AddCell()
  252. savedCell.Merge(2, 0)
  253. cell = row[(r*colNum)+1].AddCell()
  254. cell.Value = "Предмет"
  255. cell = row[(r*colNum)+1].AddCell()
  256. cell.Value = "Преподаватель"
  257. cell = row[(r*colNum)+1].AddCell()
  258. cell.Value = "Кабинет"
  259. for j, trow := range schedule[index].Table {
  260. cell = row[(r*colNum)+uint(j)+2].AddCell()
  261. if trow.Subject[A] == trow.Subject[B] {
  262. cell.Value = trow.Subject[A]
  263. } else {
  264. if trow.Subject[A] != "" {
  265. cell.Value = trow.Subject[A] + " (A)"
  266. }
  267. if trow.Subject[B] != "" {
  268. cell.Value += "\n" + trow.Subject[B] + " (B)"
  269. }
  270. }
  271. cell = row[(r*colNum)+uint(j)+2].AddCell()
  272. if trow.Teacher[A] == trow.Teacher[B] {
  273. cell.Value = trow.Teacher[A]
  274. } else {
  275. if trow.Teacher[A] != "" {
  276. cell.Value = trow.Teacher[A]
  277. }
  278. if trow.Teacher[B] != "" {
  279. cell.Value += "\n" + trow.Teacher[B]
  280. }
  281. }
  282. sheet.SetColWidth(colWidthForCabinets(int(j)))
  283. cell = row[(r*colNum)+uint(j)+2].AddCell()
  284. if trow.Cabinet[A] == trow.Cabinet[B] {
  285. cell.Value = trow.Cabinet[A]
  286. } else {
  287. if trow.Cabinet[A] != "" {
  288. cell.Value = trow.Cabinet[A]
  289. }
  290. if trow.Cabinet[B] != "" {
  291. cell.Value += "\n" + trow.Cabinet[B]
  292. }
  293. }
  294. }
  295. index++
  296. }
  297. }
  298. err = file.Save(filename)
  299. if err != nil {
  300. return err
  301. }
  302. return nil
  303. }
  304. func RandSubgroup() SubgroupType {
  305. return SubgroupType(random(0, 1))
  306. }
  307. func Load(filename string) *Generator {
  308. var generator = new(Generator)
  309. jsonData := readFile(filename)
  310. err := json.Unmarshal([]byte(jsonData), generator)
  311. if err != nil {
  312. return nil
  313. }
  314. return generator
  315. }
  316. func (gen *Generator) Dump(filename string) error {
  317. return writeJSON(filename, gen)
  318. }