rename home to remote

This commit is contained in:
Ross
2022-02-24 21:50:37 +00:00
parent 7807e3b1e8
commit 9e873ac5f7
11 changed files with 806 additions and 145 deletions
View File
+95
View File
@@ -0,0 +1,95 @@
"Name","Madalina Drumea","Layla Bell","Alex wood","Max Ireland","Seren Peters","Charles Finan","Paul Ward","Delilah Trimmer","Tom Welsh","Kyaw Tint","Maththew O'Brien","Max Finzel","Zainab Sharaf","Jean Skumar","Michael Tomek","Dhuvresh Patel","Nicolas Dziadulewicz","Vilim Kalamar","Georgina Edwards","Jenn Haw fong","Paul Jenkins","Amoolya Mannava","Rebecca Murphy","Ross Kruger","Fiona Lyall","Wijhdan Abusrewil","Andrew MacCormick","Joel Lim","Mark Haley","Alexander Sanchez - cabello","Chong Yew Ng","Matthew Thorley","Sayed Hashim Alqarooni","Harriet Conley","Jon Skinner","Alexander Wijnberg","Cameron Bullock","Alan Eccles","Christian Greer","Hannah Lewis","Amina Odeh","Sarath Vennam","Mo Babsail","Richard Chaytor","Sherafghan PROC"
"Site","barnstaple","exeter","exeter","exeter","exeter","exeter","exeter","exeter","exeter","plymouth","plymouth","plymouth","plymouth","plymouth","plymouth","plymouth","plymouth","plymouth","plymouth","plymouth","plymouth","plymouth","plymouth","plymouth","plymouth_twilights","torbay","torbay","torbay","torbay","torbay","torbay","torbay","torbay","torbay","torbay twilights","truro","truro","truro","truro","truro","truro","truro","truro","truro","truro no proc"
"Grade","3","2","3","3","3","4","4","5","5","2","2","2","2","3","3","3","3","3","4","4","4","5","5","5","6","2","2","2","2","3","4","4","4","5","6","2","2","3","3","3","4","4","5","5","5"
"FTE","80","100","100","100","100","100","100","80","100","100","100","100","100","60","80","100","100","100","80","100","100","100","100","100","60","100","100","100","100","100","100","100","100","60","100","100","100","80","100","100","100","100","100","100","100"
"Week 1 Day Mon","","","","","","exeter_twilight","","","","","night_weekday","","","","","","night_weekday","","","","","plymouth_twilight","","","","","","","torbay_twilight","","","","","","","","","","","","","night_weekday","","","truro_twilight"
"Week 1 Day Tue","","","","","","","","exeter_twilight","","","night_weekday","","plymouth_twilight","","","","night_weekday","","","","","","","","","","","","","","","","","torbay_twilight","","","","","","","","night_weekday","","","truro_twilight"
"Week 1 Day Wed","","","","","","exeter_twilight","","","","","night_weekday","","plymouth_twilight","","","","night_weekday","","","","","","","","","","","","torbay_twilight","","","","","","","","","","","","","night_weekday","truro_twilight","",""
"Week 1 Day Thu","","","","","exeter_twilight","","","","","","night_weekday","","","","","","night_weekday","","","","plymouth_twilight","","","","","","","torbay_twilight","","","","","","","","","","","","","","night_weekday","truro_twilight","",""
"Week 1 Day Fri","","","night_weekend","weekend_exeter","","","","","","","","","","plymouth_twilight","","night_weekend","","","","","","","","","","","","","","","","","","weekend_torbay","","","","night_weekend","","","weekend_truro","","","",""
"Week 1 Day Sat","","","night_weekend","weekend_exeter","","","","","","","","","weekend_plymouth1","","weekend_plymouth2","night_weekend","","","","","","","","","","","","","","","","","","weekend_torbay","","","","night_weekend","","","weekend_truro","","","",""
"Week 1 Day Sun","","","night_weekend","weekend_exeter","","","","","","","","","weekend_plymouth1","","weekend_plymouth2","night_weekend","","","","","","","","","","","","","","","","","","weekend_torbay","","","","night_weekend","","","weekend_truro","","","",""
"Week 2 Day Mon","","","","","","","exeter_twilight","","","","","","","","","","","plymouth_twilight","night_weekday","","","","","","","","night_weekday","night_weekday","torbay_twilight","","","","","","","","","","","","","truro_twilight","","",""
"Week 2 Day Tue","","","","","","","exeter_twilight","","","","","","","","","","plymouth_twilight","","night_weekday","","","","","","","","night_weekday","night_weekday","torbay_twilight","","","","","","","","","","","","","","truro_twilight","",""
"Week 2 Day Wed","","","exeter_twilight","","","","","","","","","","","","","","","","night_weekday","","plymouth_twilight","","","","","","night_weekday","night_weekday","torbay_twilight","","","","","","","","","","","","","","","truro_twilight",""
"Week 2 Day Thu","","","exeter_twilight","","","","","","","","","","","","","","","","night_weekday","","","","","","plymouth_twilight","","night_weekday","night_weekday","torbay_twilight","","","","","","","","truro_twilight","","","","","","","",""
"Week 2 Day Fri","","","","","","night_weekend","weekend_exeter","","","","","","","","","plymouth_twilight","","night_weekend","","","","","","","","","","","","","weekend_torbay","","night_weekend","","","","","","","","","weekend_truro","","",""
"Week 2 Day Sat","","","","","","night_weekend","weekend_exeter","","","","weekend_plymouth2","","","","","","","night_weekend","","","","","weekend_plymouth1","","","","","","","","weekend_torbay","","night_weekend","","","","","","","","","weekend_truro","","",""
"Week 2 Day Sun","","","","","","night_weekend","weekend_exeter","","","","weekend_plymouth2","","","","","","","night_weekend","","","","","weekend_plymouth1","","","","","","","","weekend_torbay","","night_weekend","","","","","","","","","weekend_truro","","",""
"Week 3 Day Mon","","","exeter_twilight","","","","","","","night_weekday","","","","","plymouth_twilight","night_weekday","","","","","","","","","","","","torbay_twilight","","","","","","","","night_weekday","","","","truro_twilight","","","","",""
"Week 3 Day Tue","","","exeter_twilight","","","","","","","night_weekday","","plymouth_twilight","","","","night_weekday","","","","","","","","","","","","torbay_twilight","","","","","","","","night_weekday","","","truro_twilight","","","","","",""
"Week 3 Day Wed","","","","","","exeter_twilight","","","","night_weekday","","","plymouth_twilight","","","night_weekday","","","","","","","","","","","","torbay_twilight","","","","","","","","night_weekday","","","","","","","truro_twilight","",""
"Week 3 Day Thu","","","","","exeter_twilight","","","","","night_weekday","","","","","","night_weekday","","","plymouth_twilight","","","","","","","","","torbay_twilight","","","","","","","","night_weekday","","","","","","","truro_twilight","",""
"Week 3 Day Fri","","weekend_exeter","","","","","","","","","","","","","","","","plymouth_twilight","","night_weekend","","night_weekend","","","","","","","night_weekend","weekend_torbay","","","","","","","","","","","","","","","weekend_truro"
"Week 3 Day Sat","","weekend_exeter","","","","","","","","","","","","weekend_plymouth2","weekend_plymouth1","","","","","night_weekend","","night_weekend","","","","","","","night_weekend","weekend_torbay","","","","","","","","","","","","","","","weekend_truro"
"Week 3 Day Sun","","weekend_exeter","","","","","","","","","","","","weekend_plymouth2","weekend_plymouth1","","","","","night_weekend","","night_weekend","","","","","","","night_weekend","weekend_torbay","","","","","","","","","","","","","","","weekend_truro"
"Week 4 Day Mon","","","","","","exeter_twilight","","night_weekday","","","","","","","","","","","","","","","plymouth_twilight","","","","","","","","","","night_weekday","","torbay_twilight","truro_twilight","","","","night_weekday","","","","",""
"Week 4 Day Tue","","","exeter_twilight","","","","","night_weekday","","","","","","","","plymouth_twilight","","","","","","","","","","","","","","","","torbay_twilight","night_weekday","","","","","","","night_weekday","","truro_twilight","","",""
"Week 4 Day Wed","","","","exeter_twilight","","","","night_weekday","","","","","","","","plymouth_twilight","","","","","","","","","","","","","torbay_twilight","","","","night_weekday","","","","","","","night_weekday","","","","","truro_twilight"
"Week 4 Day Thu","","","","","","","","night_weekday","exeter_twilight","plymouth_twilight","","","","","","","","","","","","","","","","","","","","","","","night_weekday","","torbay_twilight","","","","","night_weekday","","","","","truro_twilight"
"Week 4 Day Fri","","","","","weekend_exeter","","","","","","","","plymouth_twilight","","","","","","","","","","","night_weekend","","","weekend_torbay","","","","","","","","","weekend_truro","","","night_weekend","","","night_weekend","","",""
"Week 4 Day Sat","","","","","weekend_exeter","","","","","","weekend_plymouth1","","","","","","","","","","","","weekend_plymouth2","night_weekend","","","weekend_torbay","","","","","","","","","weekend_truro","","","night_weekend","","","night_weekend","","",""
"Week 4 Day Sun","","","","","weekend_exeter","","","","","","weekend_plymouth1","","","","","","","","","","","","weekend_plymouth2","night_weekend","","","weekend_torbay","","","","","","","","","weekend_truro","","","night_weekend","","","night_weekend","","",""
"Week 5 Day Mon","","exeter_twilight","","","","","night_weekday","","","plymouth_twilight","","","","","","","","","","","","","","","","","","","night_weekday","","","","torbay_twilight","night_weekday","","","","truro_twilight","","","","","","",""
"Week 5 Day Tue","","","","","","","night_weekday","","exeter_twilight","","","","","","","","","","","","plymouth_twilight","","","","","","","","night_weekday","","","","torbay_twilight","night_weekday","","","","","","","","","truro_twilight","",""
"Week 5 Day Wed","","","","","","","night_weekday","","exeter_twilight","","","","","","","","","","","","plymouth_twilight","","","","","","","","night_weekday","","","","torbay_twilight","night_weekday","","","","","","","","","","truro_twilight",""
"Week 5 Day Thu","","","","","exeter_twilight","","night_weekday","","","","","","","","","","","","","","","plymouth_twilight","","","","","","","night_weekday","","","","torbay_twilight","night_weekday","","","","","truro_twilight","","","","","",""
"Week 5 Day Fri","","","","night_weekend","","","","weekend_exeter","","","","","","","","","","","","","","","","plymouth_twilight","","","","night_weekend","","weekend_torbay","night_weekend","","","","","","","","","","weekend_truro","","","",""
"Week 5 Day Sat","","","","night_weekend","","","","weekend_exeter","","","","","","","weekend_plymouth2","","","","weekend_plymouth1","","","","","","","","","night_weekend","","weekend_torbay","night_weekend","","","","","","","","","","weekend_truro","","","",""
"Week 5 Day Sun","","","","night_weekend","","","","weekend_exeter","","","","","","","weekend_plymouth2","","","","weekend_plymouth1","","","","","","","","","night_weekend","","weekend_torbay","night_weekend","","","","","","","","","","weekend_truro","","","",""
"Week 6 Day Mon","","night_weekday","","","exeter_twilight","","","","night_weekday","","","","","","","","","","","","","plymouth_twilight","night_weekday","","","","torbay_twilight","","","","","","","","","","","truro_twilight","","","","","","",""
"Week 6 Day Tue","","night_weekday","","","","","exeter_twilight","","night_weekday","","","","","","","","","","","plymouth_twilight","","","night_weekday","","","","torbay_twilight","","","","","","","","","","truro_twilight","","","","","","","",""
"Week 6 Day Wed","","night_weekday","","","","","exeter_twilight","","night_weekday","","","","","","","","","","","","","","night_weekday","plymouth_twilight","","","torbay_twilight","","","","","","","","","truro_twilight","","","","","","","","",""
"Week 6 Day Thu","","night_weekday","","","","","","exeter_twilight","night_weekday","","","","","","","","","","","","","plymouth_twilight","night_weekday","","","","torbay_twilight","","","","","","","","","","","","","","","","","truro_twilight",""
"Week 6 Day Fri","night_weekend","","","","","weekend_exeter","","","","","night_weekend","plymouth_twilight","","","","","plymouth_bank_holidays","","","","night_weekend","","","","","","","","","","","","","weekend_torbay","","","weekend_truro","","","","","","","",""
"Week 6 Day Sat","night_weekend","","","","","weekend_exeter","","","","weekend_plymouth2","night_weekend","","","","","","","","","","night_weekend","","","weekend_plymouth1","","","","","","","","","","weekend_torbay","","","weekend_truro","","","","","","","",""
"Week 6 Day Sun","night_weekend","","","","","weekend_exeter","","","","weekend_plymouth2","night_weekend","","","","","","","","","","night_weekend","","","weekend_plymouth1","","","","","","","","","","weekend_torbay","","","weekend_truro","","","","","","","",""
"Week 7 Day Mon","","","exeter_twilight","","night_weekday","","","","","","","","","","","","","plymouth_bank_holidays","","plymouth_twilight","","","","","","","torbay_twilight","","","night_weekday","night_weekday","","","","","","","truro_twilight","","","","","","",""
"Week 7 Day Tue","","","","","night_weekday","","exeter_twilight","","","","","","","","","plymouth_twilight","","","","","","","","","","","","","","night_weekday","night_weekday","","","","torbay_twilight","","","truro_twilight","","","","","","",""
"Week 7 Day Wed","","","","","night_weekday","exeter_twilight","","","","","","","","","","","","plymouth_twilight","","","","","","","","","torbay_twilight","","","night_weekday","night_weekday","","","","","","","","","","truro_twilight","","","",""
"Week 7 Day Thu","","exeter_twilight","","","night_weekday","","","","","","plymouth_twilight","","","","","","","","","","","","","","","","torbay_twilight","","","night_weekday","night_weekday","","","","","","","","truro_twilight","","","","","",""
"Week 7 Day Fri","","","","","","","","","weekend_exeter","","","night_weekend","","","","","","","","","","night_weekend","","plymouth_twilight","","","","","weekend_torbay","","","","","","","","","","","weekend_truro","","","","night_weekend",""
"Week 7 Day Sat","","","","","","","","","weekend_exeter","","","night_weekend","","","","","weekend_plymouth1","","","weekend_plymouth2","","night_weekend","","","","","","","weekend_torbay","","","","","","","","","","","weekend_truro","","","","night_weekend",""
"Week 7 Day Sun","","","","","","","","","weekend_exeter","","","night_weekend","","","","","weekend_plymouth1","","","weekend_plymouth2","","night_weekend","","","","","","","weekend_torbay","","","","","","","","","","","weekend_truro","","","","night_weekend",""
"Week 8 Day Mon","night_weekday","","","exeter_twilight","","night_weekday","","","","","","","","night_weekday","plymouth_twilight","","","","","","","","","","","","","","","","torbay_twilight","","","","","","","","truro_twilight","","","","","",""
"Week 8 Day Tue","night_weekday","","","exeter_twilight","","night_weekday","","","","","","","","night_weekday","","","","","","","","","","plymouth_twilight","","","","","","","torbay_twilight","","","","","","","truro_twilight","","","","","","",""
"Week 8 Day Wed","night_weekday","","","","","night_weekday","","","exeter_twilight","","","","","night_weekday","","","","","","","","","","plymouth_twilight","","","","torbay_twilight","","","","","","","","","","","","","truro_twilight","","","",""
"Week 8 Day Thu","night_weekday","","","","exeter_twilight","night_weekday","","","","","","plymouth_twilight","","night_weekday","","","","","","","","","","","","","","torbay_twilight","","","","","","","","","","","","","truro_twilight","","","",""
"Week 8 Day Fri","","weekend_exeter","","","","","","night_weekend","","","night_weekend","plymouth_twilight","night_weekend","","","","","","","","","","","","","","","","","","","weekend_torbay","","","","","weekend_truro","","","","","","","",""
"Week 8 Day Sat","","weekend_exeter","","","","","","night_weekend","","","night_weekend","","night_weekend","","","","","","","","weekend_plymouth1","","","weekend_plymouth2","","","","","","","","weekend_torbay","","","","","weekend_truro","","","","","","","",""
"Week 8 Day Sun","","weekend_exeter","","","","","","night_weekend","","","night_weekend","","night_weekend","","","","","","","","weekend_plymouth1","","","weekend_plymouth2","","","","","","","","weekend_torbay","","","","","weekend_truro","","","","","","","",""
"Week 9 Day Mon","","","night_weekday","night_weekday","exeter_twilight","","","","","","","plymouth_twilight","","","night_weekday","","","","","","","","","","plymouth_bank_holidays","torbay_twilight","","","","","","","","","","","","","","truro_twilight","","","","",""
"Week 9 Day Tue","","","night_weekday","night_weekday","","","","","exeter_twilight","plymouth_twilight","","","","","night_weekday","","","","","","","","","","","","","","","","torbay_twilight","","","","","","","","","","truro_twilight","","","",""
"Week 9 Day Wed","","","night_weekday","night_weekday","","","","","exeter_twilight","","","","","","night_weekday","","","","","","","","","plymouth_twilight","","","","","","","torbay_twilight","","","","","","truro_twilight","","","","","","","",""
"Week 9 Day Thu","","","night_weekday","night_weekday","exeter_twilight","","","","","","","","","","night_weekday","","","","","","plymouth_twilight","","","","","","","","","","torbay_twilight","","","","","","","","","","","","","truro_twilight",""
"Week 9 Day Fri","","","","","","","weekend_exeter","","","night_weekend","","","","","","","","plymouth_twilight","","","","","","","","weekend_torbay","night_weekend","","","","","","","","","","","","","weekend_truro","night_weekend","","","",""
"Week 9 Day Sat","","","","","","","weekend_exeter","","","night_weekend","","","","","","","weekend_plymouth1","","weekend_plymouth2","","","","","","","weekend_torbay","night_weekend","","","","","","","","","","","","","weekend_truro","night_weekend","","","",""
"Week 9 Day Sun","","","","","","","weekend_exeter","","","night_weekend","","","","","","","weekend_plymouth1","","weekend_plymouth2","","","","","","","weekend_torbay","night_weekend","","","","","","","","","","","","","weekend_truro","night_weekend","","","",""
"Week 10 Day Mon","","","","","","","","exeter_twilight","","","","","night_weekday","","","","","plymouth_twilight","","","","","night_weekday","","","","","","","","torbay_twilight","night_weekday","","","","","","","","","","","truro_twilight","",""
"Week 10 Day Tue","","","exeter_twilight","","","","","","","","","","night_weekday","","","","","","","plymouth_twilight","","","night_weekday","","","","","","","","","night_weekday","","","torbay_twilight","truro_twilight","","","","","","","","",""
"Week 10 Day Wed","","exeter_twilight","","","","","","","","plymouth_twilight","","","night_weekday","","","","","","","","","","night_weekday","","","","","","","","","night_weekday","","","torbay_twilight","","","","","","","truro_twilight","","",""
"Week 10 Day Thu","","exeter_twilight","","","","","","","","","","","night_weekday","","","","","","","","","plymouth_twilight","night_weekday","","","","","","","","","night_weekday","","","torbay_twilight","","","","","","","","truro_twilight","",""
"Week 10 Day Fri","","","weekend_exeter","","","","","","","plymouth_twilight","","","","","","night_weekend","","","","","","","","","","","","","","","weekend_torbay","","","","","night_weekend","night_weekend","weekend_truro","","","","","","",""
"Week 10 Day Sat","","","weekend_exeter","","","","","","","","","weekend_plymouth1","","","","night_weekend","","weekend_plymouth2","","","","","","","","","","","","","weekend_torbay","","","","","night_weekend","night_weekend","weekend_truro","","","","","","",""
"Week 10 Day Sun","","","weekend_exeter","","","","","","","","","weekend_plymouth1","","","","night_weekend","","weekend_plymouth2","","","","","","","","","","","","","weekend_torbay","","","","","night_weekend","night_weekend","weekend_truro","","","","","","",""
"Week 11 Day Mon","","","","","","","exeter_twilight","","","","","","","","","","","","","","","plymouth_twilight","","night_weekday","","","","","","night_weekday","","torbay_twilight","","","","","","","night_weekday","","truro_twilight","","","",""
"Week 11 Day Tue","","exeter_twilight","","","","","","","","","","","","","plymouth_twilight","","","","","","","","","night_weekday","","","","","","night_weekday","","torbay_twilight","","","","","","","night_weekday","","","","","","truro_twilight"
"Week 11 Day Wed","","","exeter_twilight","","","","","","","","","","","","","","","","","plymouth_twilight","","","","night_weekday","","","","","","night_weekday","","torbay_twilight","","","","","","","night_weekday","","","truro_twilight","","",""
"Week 11 Day Thu","","exeter_twilight","","","","","","","","","","plymouth_twilight","","","","","","","","","","","","night_weekday","","","","","","night_weekday","","torbay_twilight","","","","","","","night_weekday","","","truro_twilight","","",""
"Week 11 Day Fri","","","","","weekend_exeter","","","","","","","","","","","","night_weekend","","","","night_weekend","plymouth_twilight","","","","","","weekend_torbay","","","","","","","","","","","","night_weekend","","","","weekend_truro",""
"Week 11 Day Sat","","","","","weekend_exeter","","","","","","weekend_plymouth2","","","","","","night_weekend","","weekend_plymouth1","","night_weekend","","","","","","","weekend_torbay","","","","","","","","","","","","night_weekend","","","","weekend_truro",""
"Week 11 Day Sun","","","","","weekend_exeter","","","","","","weekend_plymouth2","","","","","","night_weekend","","weekend_plymouth1","","night_weekend","","","","","","","weekend_torbay","","","","","","","","","","","","night_weekend","","","","weekend_truro",""
"Week 12 Day Mon","","","","","","","","exeter_twilight","","plymouth_twilight","","","","","","","","","","","","","","","","night_weekday","","","","torbay_twilight","","","","","","","","","truro_twilight","","night_weekday","","night_weekday","",""
"Week 12 Day Tue","","","","exeter_twilight","","","","","","","","","","","","","","plymouth_twilight","","","","","","","","night_weekday","","","","torbay_twilight","","","","","","","","truro_twilight","","","night_weekday","","night_weekday","",""
"Week 12 Day Wed","","","","","","","","","exeter_twilight","","","","","","","","","","plymouth_twilight","","","","","","","night_weekday","","","","torbay_twilight","","","","","","","","","","truro_twilight","night_weekday","","night_weekday","",""
"Week 12 Day Thu","","","","","","","","","exeter_twilight","","","","","","","","","","","","","","","","plymouth_twilight","night_weekday","","","","torbay_twilight","","","","","","","","","","","night_weekday","","night_weekday","","truro_twilight"
"Week 12 Day Fri","","night_weekend","","weekend_exeter","","","","","","","","night_weekend","","","","","","","","plymouth_twilight","","","","","","","","","","","","night_weekend","weekend_torbay","","","","","","weekend_truro","","","","","",""
"Week 12 Day Sat","","night_weekend","","weekend_exeter","","","","","","","","night_weekend","","weekend_plymouth1","","","","","","","","","weekend_plymouth2","","","","","","","","","night_weekend","weekend_torbay","","","","","","weekend_truro","","","","","",""
"Week 12 Day Sun","","night_weekend","","weekend_exeter","","","","","","","","night_weekend","","weekend_plymouth1","","","","","","","","","weekend_plymouth2","","","","","","","","","night_weekend","weekend_torbay","","","","","","weekend_truro","","","","","",""
"Week 13 Day Mon","","","","","","","exeter_twilight","","","","","","","","","","","","","plymouth_twilight","","","","","","torbay_twilight","night_weekday","","","","","","","","","","night_weekday","","","","","","truro_twilight","night_weekday",""
"Week 13 Day Tue","","","","","","","exeter_twilight","","","","","","","","","","","","","plymouth_twilight","","","","","","torbay_twilight","night_weekday","","","","","","","","","","night_weekday","","","","truro_twilight","","","night_weekday",""
"Week 13 Day Wed","","","","exeter_twilight","","","","","","","","plymouth_twilight","","","","","","","","","","","","","","","night_weekday","","","","","","torbay_twilight","","","","night_weekday","","","","","","","night_weekday","truro_twilight"
"Week 13 Day Thu","","exeter_twilight","","","","","","","","","","","","","","","","","","","plymouth_bank_holidays","","plymouth_twilight","","","","night_weekday","","","","","","torbay_twilight","","","","night_weekday","","","","","truro_twilight","","night_weekday",""
"Week 13 Day Fri","","","","","","weekend_exeter","","","night_weekend","","","","","","","","","night_weekend","plymouth_bank_holidays","night_weekend","","","plymouth_twilight","","","weekend_torbay","","","","","","","","","","","","","","","","","weekend_truro","",""
"Week 13 Day Sat","","","","","","weekend_exeter","","","night_weekend","","","","","","","weekend_plymouth2","","night_weekend","","night_weekend","","weekend_plymouth1","","","","weekend_torbay","","","","","","","","","","","","","","","","","weekend_truro","",""
"Week 13 Day Sun","","","","","","weekend_exeter","","","night_weekend","","","","","","","weekend_plymouth2","","night_weekend","","night_weekend","","weekend_plymouth1","","","","weekend_torbay","","","","","","","","","","","","","","","","","weekend_truro","",""
1 Name Madalina Drumea Layla Bell Alex wood Max Ireland Seren Peters Charles Finan Paul Ward Delilah Trimmer Tom Welsh Kyaw Tint Maththew O'Brien Max Finzel Zainab Sharaf Jean Skumar Michael Tomek Dhuvresh Patel Nicolas Dziadulewicz Vilim Kalamar Georgina Edwards Jenn Haw fong Paul Jenkins Amoolya Mannava Rebecca Murphy Ross Kruger Fiona Lyall Wijhdan Abusrewil Andrew MacCormick Joel Lim Mark Haley Alexander Sanchez - cabello Chong Yew Ng Matthew Thorley Sayed Hashim Alqarooni Harriet Conley Jon Skinner Alexander Wijnberg Cameron Bullock Alan Eccles Christian Greer Hannah Lewis Amina Odeh Sarath Vennam Mo Babsail Richard Chaytor Sherafghan PROC
2 Site barnstaple exeter exeter exeter exeter exeter exeter exeter exeter plymouth plymouth plymouth plymouth plymouth plymouth plymouth plymouth plymouth plymouth plymouth plymouth plymouth plymouth plymouth plymouth_twilights torbay torbay torbay torbay torbay torbay torbay torbay torbay torbay twilights truro truro truro truro truro truro truro truro truro truro no proc
3 Grade 3 2 3 3 3 4 4 5 5 2 2 2 2 3 3 3 3 3 4 4 4 5 5 5 6 2 2 2 2 3 4 4 4 5 6 2 2 3 3 3 4 4 5 5 5
4 FTE 80 100 100 100 100 100 100 80 100 100 100 100 100 60 80 100 100 100 80 100 100 100 100 100 60 100 100 100 100 100 100 100 100 60 100 100 100 80 100 100 100 100 100 100 100
5 Week 1 Day Mon exeter_twilight night_weekday night_weekday plymouth_twilight torbay_twilight night_weekday truro_twilight
6 Week 1 Day Tue exeter_twilight night_weekday plymouth_twilight night_weekday torbay_twilight night_weekday truro_twilight
7 Week 1 Day Wed exeter_twilight night_weekday plymouth_twilight night_weekday torbay_twilight night_weekday truro_twilight
8 Week 1 Day Thu exeter_twilight night_weekday night_weekday plymouth_twilight torbay_twilight night_weekday truro_twilight
9 Week 1 Day Fri night_weekend weekend_exeter plymouth_twilight night_weekend weekend_torbay night_weekend weekend_truro
10 Week 1 Day Sat night_weekend weekend_exeter weekend_plymouth1 weekend_plymouth2 night_weekend weekend_torbay night_weekend weekend_truro
11 Week 1 Day Sun night_weekend weekend_exeter weekend_plymouth1 weekend_plymouth2 night_weekend weekend_torbay night_weekend weekend_truro
12 Week 2 Day Mon exeter_twilight plymouth_twilight night_weekday night_weekday night_weekday torbay_twilight truro_twilight
13 Week 2 Day Tue exeter_twilight plymouth_twilight night_weekday night_weekday night_weekday torbay_twilight truro_twilight
14 Week 2 Day Wed exeter_twilight night_weekday plymouth_twilight night_weekday night_weekday torbay_twilight truro_twilight
15 Week 2 Day Thu exeter_twilight night_weekday plymouth_twilight night_weekday night_weekday torbay_twilight truro_twilight
16 Week 2 Day Fri night_weekend weekend_exeter plymouth_twilight night_weekend weekend_torbay night_weekend weekend_truro
17 Week 2 Day Sat night_weekend weekend_exeter weekend_plymouth2 night_weekend weekend_plymouth1 weekend_torbay night_weekend weekend_truro
18 Week 2 Day Sun night_weekend weekend_exeter weekend_plymouth2 night_weekend weekend_plymouth1 weekend_torbay night_weekend weekend_truro
19 Week 3 Day Mon exeter_twilight night_weekday plymouth_twilight night_weekday torbay_twilight night_weekday truro_twilight
20 Week 3 Day Tue exeter_twilight night_weekday plymouth_twilight night_weekday torbay_twilight night_weekday truro_twilight
21 Week 3 Day Wed exeter_twilight night_weekday plymouth_twilight night_weekday torbay_twilight night_weekday truro_twilight
22 Week 3 Day Thu exeter_twilight night_weekday night_weekday plymouth_twilight torbay_twilight night_weekday truro_twilight
23 Week 3 Day Fri weekend_exeter plymouth_twilight night_weekend night_weekend night_weekend weekend_torbay weekend_truro
24 Week 3 Day Sat weekend_exeter weekend_plymouth2 weekend_plymouth1 night_weekend night_weekend night_weekend weekend_torbay weekend_truro
25 Week 3 Day Sun weekend_exeter weekend_plymouth2 weekend_plymouth1 night_weekend night_weekend night_weekend weekend_torbay weekend_truro
26 Week 4 Day Mon exeter_twilight night_weekday plymouth_twilight night_weekday torbay_twilight truro_twilight night_weekday
27 Week 4 Day Tue exeter_twilight night_weekday plymouth_twilight torbay_twilight night_weekday night_weekday truro_twilight
28 Week 4 Day Wed exeter_twilight night_weekday plymouth_twilight torbay_twilight night_weekday night_weekday truro_twilight
29 Week 4 Day Thu night_weekday exeter_twilight plymouth_twilight night_weekday torbay_twilight night_weekday truro_twilight
30 Week 4 Day Fri weekend_exeter plymouth_twilight night_weekend weekend_torbay weekend_truro night_weekend night_weekend
31 Week 4 Day Sat weekend_exeter weekend_plymouth1 weekend_plymouth2 night_weekend weekend_torbay weekend_truro night_weekend night_weekend
32 Week 4 Day Sun weekend_exeter weekend_plymouth1 weekend_plymouth2 night_weekend weekend_torbay weekend_truro night_weekend night_weekend
33 Week 5 Day Mon exeter_twilight night_weekday plymouth_twilight night_weekday torbay_twilight night_weekday truro_twilight
34 Week 5 Day Tue night_weekday exeter_twilight plymouth_twilight night_weekday torbay_twilight night_weekday truro_twilight
35 Week 5 Day Wed night_weekday exeter_twilight plymouth_twilight night_weekday torbay_twilight night_weekday truro_twilight
36 Week 5 Day Thu exeter_twilight night_weekday plymouth_twilight night_weekday torbay_twilight night_weekday truro_twilight
37 Week 5 Day Fri night_weekend weekend_exeter plymouth_twilight night_weekend weekend_torbay night_weekend weekend_truro
38 Week 5 Day Sat night_weekend weekend_exeter weekend_plymouth2 weekend_plymouth1 night_weekend weekend_torbay night_weekend weekend_truro
39 Week 5 Day Sun night_weekend weekend_exeter weekend_plymouth2 weekend_plymouth1 night_weekend weekend_torbay night_weekend weekend_truro
40 Week 6 Day Mon night_weekday exeter_twilight night_weekday plymouth_twilight night_weekday torbay_twilight truro_twilight
41 Week 6 Day Tue night_weekday exeter_twilight night_weekday plymouth_twilight night_weekday torbay_twilight truro_twilight
42 Week 6 Day Wed night_weekday exeter_twilight night_weekday night_weekday plymouth_twilight torbay_twilight truro_twilight
43 Week 6 Day Thu night_weekday exeter_twilight night_weekday plymouth_twilight night_weekday torbay_twilight truro_twilight
44 Week 6 Day Fri night_weekend weekend_exeter night_weekend plymouth_twilight plymouth_bank_holidays night_weekend weekend_torbay weekend_truro
45 Week 6 Day Sat night_weekend weekend_exeter weekend_plymouth2 night_weekend night_weekend weekend_plymouth1 weekend_torbay weekend_truro
46 Week 6 Day Sun night_weekend weekend_exeter weekend_plymouth2 night_weekend night_weekend weekend_plymouth1 weekend_torbay weekend_truro
47 Week 7 Day Mon exeter_twilight night_weekday plymouth_bank_holidays plymouth_twilight torbay_twilight night_weekday night_weekday truro_twilight
48 Week 7 Day Tue night_weekday exeter_twilight plymouth_twilight night_weekday night_weekday torbay_twilight truro_twilight
49 Week 7 Day Wed night_weekday exeter_twilight plymouth_twilight torbay_twilight night_weekday night_weekday truro_twilight
50 Week 7 Day Thu exeter_twilight night_weekday plymouth_twilight torbay_twilight night_weekday night_weekday truro_twilight
51 Week 7 Day Fri weekend_exeter night_weekend night_weekend plymouth_twilight weekend_torbay weekend_truro night_weekend
52 Week 7 Day Sat weekend_exeter night_weekend weekend_plymouth1 weekend_plymouth2 night_weekend weekend_torbay weekend_truro night_weekend
53 Week 7 Day Sun weekend_exeter night_weekend weekend_plymouth1 weekend_plymouth2 night_weekend weekend_torbay weekend_truro night_weekend
54 Week 8 Day Mon night_weekday exeter_twilight night_weekday night_weekday plymouth_twilight torbay_twilight truro_twilight
55 Week 8 Day Tue night_weekday exeter_twilight night_weekday night_weekday plymouth_twilight torbay_twilight truro_twilight
56 Week 8 Day Wed night_weekday night_weekday exeter_twilight night_weekday plymouth_twilight torbay_twilight truro_twilight
57 Week 8 Day Thu night_weekday exeter_twilight night_weekday plymouth_twilight night_weekday torbay_twilight truro_twilight
58 Week 8 Day Fri weekend_exeter night_weekend night_weekend plymouth_twilight night_weekend weekend_torbay weekend_truro
59 Week 8 Day Sat weekend_exeter night_weekend night_weekend night_weekend weekend_plymouth1 weekend_plymouth2 weekend_torbay weekend_truro
60 Week 8 Day Sun weekend_exeter night_weekend night_weekend night_weekend weekend_plymouth1 weekend_plymouth2 weekend_torbay weekend_truro
61 Week 9 Day Mon night_weekday night_weekday exeter_twilight plymouth_twilight night_weekday plymouth_bank_holidays torbay_twilight truro_twilight
62 Week 9 Day Tue night_weekday night_weekday exeter_twilight plymouth_twilight night_weekday torbay_twilight truro_twilight
63 Week 9 Day Wed night_weekday night_weekday exeter_twilight night_weekday plymouth_twilight torbay_twilight truro_twilight
64 Week 9 Day Thu night_weekday night_weekday exeter_twilight night_weekday plymouth_twilight torbay_twilight truro_twilight
65 Week 9 Day Fri weekend_exeter night_weekend plymouth_twilight weekend_torbay night_weekend weekend_truro night_weekend
66 Week 9 Day Sat weekend_exeter night_weekend weekend_plymouth1 weekend_plymouth2 weekend_torbay night_weekend weekend_truro night_weekend
67 Week 9 Day Sun weekend_exeter night_weekend weekend_plymouth1 weekend_plymouth2 weekend_torbay night_weekend weekend_truro night_weekend
68 Week 10 Day Mon exeter_twilight night_weekday plymouth_twilight night_weekday torbay_twilight night_weekday truro_twilight
69 Week 10 Day Tue exeter_twilight night_weekday plymouth_twilight night_weekday night_weekday torbay_twilight truro_twilight
70 Week 10 Day Wed exeter_twilight plymouth_twilight night_weekday night_weekday night_weekday torbay_twilight truro_twilight
71 Week 10 Day Thu exeter_twilight night_weekday plymouth_twilight night_weekday night_weekday torbay_twilight truro_twilight
72 Week 10 Day Fri weekend_exeter plymouth_twilight night_weekend weekend_torbay night_weekend night_weekend weekend_truro
73 Week 10 Day Sat weekend_exeter weekend_plymouth1 night_weekend weekend_plymouth2 weekend_torbay night_weekend night_weekend weekend_truro
74 Week 10 Day Sun weekend_exeter weekend_plymouth1 night_weekend weekend_plymouth2 weekend_torbay night_weekend night_weekend weekend_truro
75 Week 11 Day Mon exeter_twilight plymouth_twilight night_weekday night_weekday torbay_twilight night_weekday truro_twilight
76 Week 11 Day Tue exeter_twilight plymouth_twilight night_weekday night_weekday torbay_twilight night_weekday truro_twilight
77 Week 11 Day Wed exeter_twilight plymouth_twilight night_weekday night_weekday torbay_twilight night_weekday truro_twilight
78 Week 11 Day Thu exeter_twilight plymouth_twilight night_weekday night_weekday torbay_twilight night_weekday truro_twilight
79 Week 11 Day Fri weekend_exeter night_weekend night_weekend plymouth_twilight weekend_torbay night_weekend weekend_truro
80 Week 11 Day Sat weekend_exeter weekend_plymouth2 night_weekend weekend_plymouth1 night_weekend weekend_torbay night_weekend weekend_truro
81 Week 11 Day Sun weekend_exeter weekend_plymouth2 night_weekend weekend_plymouth1 night_weekend weekend_torbay night_weekend weekend_truro
82 Week 12 Day Mon exeter_twilight plymouth_twilight night_weekday torbay_twilight truro_twilight night_weekday night_weekday
83 Week 12 Day Tue exeter_twilight plymouth_twilight night_weekday torbay_twilight truro_twilight night_weekday night_weekday
84 Week 12 Day Wed exeter_twilight plymouth_twilight night_weekday torbay_twilight truro_twilight night_weekday night_weekday
85 Week 12 Day Thu exeter_twilight plymouth_twilight night_weekday torbay_twilight night_weekday night_weekday truro_twilight
86 Week 12 Day Fri night_weekend weekend_exeter night_weekend plymouth_twilight night_weekend weekend_torbay weekend_truro
87 Week 12 Day Sat night_weekend weekend_exeter night_weekend weekend_plymouth1 weekend_plymouth2 night_weekend weekend_torbay weekend_truro
88 Week 12 Day Sun night_weekend weekend_exeter night_weekend weekend_plymouth1 weekend_plymouth2 night_weekend weekend_torbay weekend_truro
89 Week 13 Day Mon exeter_twilight plymouth_twilight torbay_twilight night_weekday night_weekday truro_twilight night_weekday
90 Week 13 Day Tue exeter_twilight plymouth_twilight torbay_twilight night_weekday night_weekday truro_twilight night_weekday
91 Week 13 Day Wed exeter_twilight plymouth_twilight night_weekday torbay_twilight night_weekday night_weekday truro_twilight
92 Week 13 Day Thu exeter_twilight plymouth_bank_holidays plymouth_twilight night_weekday torbay_twilight night_weekday truro_twilight night_weekday
93 Week 13 Day Fri weekend_exeter night_weekend night_weekend plymouth_bank_holidays night_weekend plymouth_twilight weekend_torbay weekend_truro
94 Week 13 Day Sat weekend_exeter night_weekend weekend_plymouth2 night_weekend night_weekend weekend_plymouth1 weekend_torbay weekend_truro
95 Week 13 Day Sun weekend_exeter night_weekend weekend_plymouth2 night_weekend night_weekend weekend_plymouth1 weekend_torbay weekend_truro
+11
View File
@@ -85,6 +85,11 @@ th {
}
th.bank-holiday {
color: darkblue;
}
/* th.worker {
max-width: 100%;
width: 200px;
@@ -224,4 +229,10 @@ summary h2 {
.derriford-night.shift-highlight {
background-color: yellow;
}
.large-shift-diff::before {
content: "*";
color: red;
}
+24 -12
View File
@@ -104,19 +104,24 @@ function generateExtra() {
// Also 1 day for a whole weekend
days_lost = days_lost + whole_weekends_worked
//days_lost = days_lost + whole_weekends_worked;
//console.log(shift_counts);
// Highlight non working days
nwds = JSON.parse(worker_td.attr("data-nwds"));
nwds.forEach(element => {
// Restrict to date ranges
start_date = new Date(Date.parse(element[1]))
end_date = new Date(Date.parse(element[2]))
$(shift_tds).filter(`.${element[0]}`).each(
(n, td_el) => {
td_date = new Date(Date.parse(td_el.dataset.date));
nwds = worker_td.attr("data-nwds");
if (td_date >= start_date && td_date <= end_date) {
$(td_el).addClass("nwd");
}
console.log(nwds);
}
if (nwds != "None") {
nwds.split(", ").forEach((nwd) => {
console.log(worker, nwd, shift_tds);
$(shift_tds).filter(`.${nwd}`).addClass("nwd");
})
}
)
});
workers[worker] = {
"site": worker_td.attr("data-site"),
@@ -145,14 +150,20 @@ function generateExtra() {
shift_diff = JSON.parse(worker_td.attr("data-shift-diff"));
summed_shift_diff = Object.values(shift_diff).reduce((a, b) => a + b);
worker_td.after(`<div class='worker-summary auto-generated'>
<span>Total shifts: ${total_shifts}, </span>
<span>Weekends: ${weekends_worked}, </span>
<span>Bank holidays: ${bank_holidays_worked}, </span>
<span>Summed shift diff: ${summed_shift_diff}, </span>
<span class='shift-diff-span'>Summed shift diff: ${summed_shift_diff}, </span>
</div>`)
if (Math.abs(summed_shift_diff) >= 1) {
worker_td.addClass("large-shift-diff")
$(tr).find(".shift-diff-span").addClass("large-shift-diff")
}
})
sites = {};
@@ -344,7 +355,8 @@ function viewWorker(worker) {
dlg = $(`<div class='dialog' title='${ds.worker}'>
Site: ${ds.site}</br>
FTE: ${ds.fte} (${ds.fte_adj})</br>
End date: ${ds.end_date}
End date: ${ds.end_date}</br>
Non working days: ${ds.nwds}
</div>`)
$("body").append(dlg)
+1 -1
View File
@@ -323,7 +323,7 @@ else:
oop,
not_available_to_work=leave_requests,
work_requests=work_requests,
home_site=site_pref,
remote_site=site_pref,
previous_shifts=previous_shifts,
pair=pair,
shift_balance_extra=w["shift_balance_extra"],
+97 -104
View File
@@ -10,7 +10,7 @@ import datetime
from pyomo.environ import *
from pyomo.opt import SolverFactory
from .workers import Worker
from rota.workers import Worker
import json
@@ -100,10 +100,14 @@ class SingleShift(object):
self.workers_required = workers_required
self.rota_on_nwds = rota_on_nwds
self.assign_as_block = assign_as_block
self.force_as_block = force_as_block
self.force_as_block_unless_nwd = force_as_block_unless_nwd
if force_as_block_unless_nwd:
self.assign_as_block = True
self.assign_as_block = assign_as_block
self.constraint_options = {}
self.constraints = []
for c in constraints:
@@ -207,9 +211,11 @@ class RotaBuilder(object):
"max_weekends": 100,
"max_shifts_per_week": 7,
"max_shifts_per_month": 40,
# The following will cause an unsolvable problem if a shift
# is forced as a block and spans the time peroid
"prevent_monday_after_full_weekends": [],
"prevent_monday_and_tuesday_after_full_weekends": [],
"prevent_fridays_before_full_weekends": [],
"prevent_fridays_before_full_weekends": [],
"prevent_thursdays_before_full_weekends": [],
# This may be better defined on the shift?
"require_presence_at_site_overnight": [],
@@ -804,9 +810,9 @@ class RotaBuilder(object):
# rule=nightShiftDerrifordRule,
# )
def presenceAtHomeSite(model, week, shift, required_site, required_number):
def presenceAtRemoteSite(model, week, shift, required_site, required_number):
required_site_workers = [
w for w in self.workers if required_site == w.home_site
w for w in self.workers if required_site == w.remote_site
]
return (
@@ -817,20 +823,20 @@ class RotaBuilder(object):
>= required_number
)
for shift in self.get_shifts_with_constraint("require_home_site_presence"):
for shift in self.get_shifts_with_constraint("require_remote_site_presence"):
site, required_number = shift.constraint_options[
"require_home_site_presence"
"require_remote_site_presence"
]
# self.model.require_presence_at_site_overnight_rule = Constraint(
setattr(
self.model,
f"require_home_site_presence_{shift.name}",
f"require_remote_site_presence_{shift.name}",
Constraint(
[week for week in self.weeks],
[shift.name],
[site],
[required_number],
rule=presenceAtHomeSite,
rule=presenceAtRemoteSite,
),
)
@@ -905,74 +911,35 @@ class RotaBuilder(object):
)
)
if shift.force_as_block:
if shift.force_as_block_unless_nwd:
workers = self.get_workers_for_shift(shift)
# Get workers who have a nwd on the shift
nwd_workers = []
full_workers = []
if shift.force_as_block_unless_nwd:
workers = self.get_workers_for_shift(shift)
# Get workers who have a nwd on the shift
nwd_workers = []
full_workers = []
for w in workers:
if w.nwd is not None:
l = []
# This should take into account dates!
for nwd, d1, d2 in w.nwd:
if nwd in shift.shift_days:
l.append(w)
if l:
nwd_workers.append(w)
else:
full_workers.append(w)
for w in workers:
if w.nwd:
l = []
# This should take into account dates!
for nwd, start_nwd_date, end_nwd_date in w.nwd:
if nwd in shift.shift_days:
if start_nwd_date > self.get_week_start_date(week):
continue
if end_nwd_date < self.get_week_start_date(week):
continue
l.append(w)
if l:
nwd_workers.append(w)
else:
full_workers.append(w)
if not nwd_workers: # Just do the usual
self.model.constraints.add(
sum(
self.model.blocks_worker_shift_assigned[
worker.id, week, shift.name
]
for worker in self.workers
)
<= 1
)
else:
self.model.constraints.add(
sum(
self.model.blocks_worker_shift_assigned[
worker.id, week, shift.name
]
for worker in full_workers
)
<= 1
)
full_workers.append(w)
self.model.constraints.add(
sum(
self.model.blocks_worker_shift_assigned[
worker.id, week, shift.name
]
for worker in workers
)
<= 2
)
print(nwd_workers)
# self.model.constraints.add(
# self.model.worker_block_shift_split[week, shift.name] <= 2 - sum(self.model.blocks_worker_shift_assigned[worker.id,
# week,
# shift.name]
# for worker in workers))
#
# self.model.constraints.add(
# sum(self.model.works[w.id, week, day,shift.name]
# for w in nwd_workers for day in shift.shift_days) - sum(self.model.works[w.id, week, day,shift.name]
# for w in full_workers for day in shift.shift_days) >= self.model.worker_block_shift_split[week, shift.name] )
else:
if not nwd_workers: # Just do the usual
self.model.constraints.add(
sum(
self.model.blocks_worker_shift_assigned[
@@ -983,6 +950,28 @@ class RotaBuilder(object):
<= shift.workers_required
)
else:
self.model.constraints.add(
sum(
self.model.blocks_worker_shift_assigned[
worker.id, week, shift.name
]
for worker in full_workers
)
<= shift.workers_required + 1
)
elif shift.force_as_block:
self.model.constraints.add(
sum(
self.model.blocks_worker_shift_assigned[
worker.id, week, shift.name
]
for worker in self.workers
)
<= shift.workers_required
)
# Most of our constraints apply per worker
for worker in self.workers:
@@ -1355,7 +1344,7 @@ class RotaBuilder(object):
for n, start, end in worker.nwd:
if not shift.rota_on_nwds and day == n:
# print(start, self.week_day_date_map[(week, day)], end)
if start <= self.week_day_date_map[(week, day)] <= end:
if start <= self.week_day_date_map[(week, day)] < end:
self.model.constraints.add(
0
== self.model.works[
@@ -1405,16 +1394,7 @@ class RotaBuilder(object):
)
)
# TODO: generic implementation
# for shift in self.get_shifts_with_constraint("max_2_shifts_per_week"):
# self.model.constraints.add(
# 2
# >= sum(
# self.model.works[worker.id, week, day, shift.name]
# for day in self.days
# )
# )
# TODO: consider excluding shifts that span relevant period
if (
worker.site
in self.constraint_options["prevent_monday_after_full_weekends"]
@@ -1463,21 +1443,18 @@ class RotaBuilder(object):
for shift in self.get_shift_names_by_week_day(
week, "Sun"
)
# for shift in self.get_shifts()
)
+ sum(
self.model.works[worker.id, week + 1, "Mon", shift]
for shift in self.get_shift_names_by_week_day(
week + 1, "Mon"
)
# for shift in self.get_shifts()
)
+ sum(
self.model.works[worker.id, week + 1, "Tue", shift]
for shift in self.get_shift_names_by_week_day(
week + 1, "Tue"
)
# for shift in self.get_shifts()
)
)
if (
@@ -1488,18 +1465,21 @@ class RotaBuilder(object):
full_weekend_count
>= sum(
self.model.works[worker.id, week, "Sat", shift.name]
for shift in self.get_shifts()
if "night" not in shift.constraints
for shift in self.get_shift_names_by_week_day(
week, "Sat"
)
)
+ sum(
self.model.works[worker.id, week, "Sun", shift.name]
for shift in self.get_shifts()
if "night" not in shift.constraints
for shift in self.get_shift_names_by_week_day(
week, "Sat"
)
)
+ sum(
self.model.works[worker.id, week, "Fri", shift.name]
for shift in self.get_shifts()
if "night" not in shift.constraints
for shift in self.get_shift_names_by_week_day(
week, "Sat"
)
)
)
if (
@@ -1510,18 +1490,21 @@ class RotaBuilder(object):
full_weekend_count
>= sum(
self.model.works[worker.id, week, "Sat", shift.name]
for shift in self.get_shifts()
if "night" not in shift.constraints
for shift in self.get_shift_names_by_week_day(
week, "Sat"
)
)
+ sum(
self.model.works[worker.id, week, "Sun", shift.name]
for shift in self.get_shifts()
if "night" not in shift.constraints
for shift in self.get_shift_names_by_week_day(
week, "Sun"
)
)
+ sum(
self.model.works[worker.id, week, "Thu", shift.name]
for shift in self.get_shifts()
if "night" not in shift.constraints
for shift in self.get_shift_names_by_week_day(
week, "Thu"
)
)
)
@@ -2243,7 +2226,7 @@ class RotaBuilder(object):
return [shift.name for shift in self.get_shifts() if shift.assign_as_block]
def shifts_to_force_as_blocks(self):
return [shift.name for shift in self.get_shifts() if shift.force_as_block]
return [shift.name for shift in self.get_shifts() if (shift.force_as_block or shift.force_as_block_unless_nwd)]
def shifts_to_assign_or_force_as_blocks(self):
s = self.shifts_to_assign_as_blocks()
@@ -2293,6 +2276,9 @@ class RotaBuilder(object):
if self.week_day_date_map[(week, day)] in bank_holiday_map
]
def get_week_start_date(self, week: int):
return self.start_date + datetime.timedelta(weeks=week)
# RESULTS
def export_rota_to_html(self, filename: str = "rota"):
output_file = Path("output", f"{filename}.html")
@@ -2300,7 +2286,7 @@ class RotaBuilder(object):
f.write(self.get_worker_timetable_html(True))
def export_rota_to_csv(self, filename: str = "rota"):
output_file = Path("output", f"{filename}.html")
output_file = Path("output", f"{filename}.csv")
works = self.model.works
with open(output_file, "w", newline="") as f:
wr = csv.writer(f, quoting=csv.QUOTE_ALL)
@@ -2436,7 +2422,12 @@ class RotaBuilder(object):
n = 0
for week, day in self.get_week_day_combinations():
d = self.start_date + datetime.timedelta(n)
date_row.append(f"<th title='{d}'>Week {week}: {day}</th>")
th_class = ""
if d in bank_holiday_map:
th_class = "bank-holiday"
date_row.append(f"<th title='{d}' class='{th_class}' data-date='{d}'>Week {week}: {day}</th>")
n = n + 1
timetable.append(f"<tr class='data-row'>{''.join(date_row)}</tr>")
@@ -2453,11 +2444,12 @@ class RotaBuilder(object):
current_site = worker.site
shifts = []
if worker.nwd is not None:
# TODO: limit to dates
nwds = ", ".join([i[0] for i in worker.nwd])
else:
nwds = None
nwds = json.dumps(worker.nwd, default=str)
#if worker.nwd:
# # TODO: limit to dates
# nwds = ", ".join([i[0] for i in worker.nwd])
#else:
# nwds = None
worker_targets = json.dumps(worker.shift_target_number)
@@ -2501,6 +2493,7 @@ class RotaBuilder(object):
bank_holiday = ""
if d in bank_holiday_map:
title = f"{title} [Bank Holiday]"
css_class = " ".join((css_class, "bank-holiday"))
bank_holiday = f" data-bank-holiday='{bank_holiday_map[d]}'"
@@ -2536,7 +2529,7 @@ class RotaBuilder(object):
data-shift-balance-extra='{shift_balance_extra}'
data-bank-holiday-extra='{bank_holiday_extra}'
data-shift-diff='{shift_diff}'
data-shift-diff_summed='{shift_diff_summed}'
data-shift-diff-summed='{shift_diff_summed}'
>
<span class='name' title='{name}'>{name}</span> ({grade}) [{fte}]</td>""".format(
site=worker.site,
+23 -10
View File
@@ -1,26 +1,33 @@
import datetime
from collections import defaultdict
from typing import List
# from shifts import RotaBuilder, days, sites
#from .shifts import RotaBuilder, days, sites
import uuid
#from rota.shifts import RotaBuilder
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from rota.shifts import RotaBuilder
class Worker:
def __init__(
self,
Rota, # Aim to remove (workers should be independent of rotas)
Rota: "RotaBuilder", # Aim to remove (workers should be independent of rotas)
name: str,
site: str,
grade: int,
id=None,
fte: int = 100,
nwd=None,
end_date=None,
nwd: list[str] | list[str, datetime.datetime, datetime.datetime]=[],
end_date: datetime.datetime | None =None,
oop=None,
not_available_to_work=None,
pref_not_to_work=None,
work_requests=None,
home_site: str = "plymouth", # We set a default proc_site
remote_site: str = "plymouth", # We set a default proc_site
previous_shifts: dict = {},
shift_balance_extra: dict = {},
bank_holiday_extra: int = 0,
@@ -38,14 +45,14 @@ class Worker:
# rules is easier.
self.grade = grade
self.fte = fte
self.nwd = nwd
self.nwd = []
self.proportion_rota_to_work = 1
self.pair = pair
self.shift_balance_extra = shift_balance_extra
self.bank_holiday_extra = bank_holiday_extra
self.home_site = home_site
if home_site == "plymouth" or home_site == "":
self.remote_site = remote_site
if remote_site == "plymouth" or remote_site == "":
self.night_at_derriford = 1
else:
self.night_at_derriford = 0
@@ -155,7 +162,13 @@ class Worker:
if self.fte_adj > 100:
raise ValueError("{} : fte_ajd = {}".format(self.name, self.fte_adj))
# print(self.name, self.nwd)
assert type(nwd) == list, f"nwd must be a list: {nwd=}"
for item in nwd:
match item:
case (nwd_day, start_date, end_date):
self.nwd.append((nwd_day, start_date, end_date))
case nwd_day:
self.nwd.append((nwd_day, Rota.start_date, Rota.rota_end_date))
def __lt__(self, other) -> bool:
return (self.site, self.grade, self.fte_adj, self.name) < (
@@ -172,7 +185,7 @@ class Worker:
self.name,
(self.site, self.grade, self.fte_adj),
nwd,
self.home_site,
self.remote_site,
self.night_at_derriford,
)
+5 -5
View File
@@ -3,8 +3,9 @@ from pyomo.opt import SolverFactory
from pyomo.opt.results import SolverStatus
from shifts import SingleShift, RotaBuilder, days
from workers import Worker
from rota.shifts import SingleShift, RotaBuilder, days
from rota.workers import Worker
import datetime
@@ -261,9 +262,8 @@ else:
if fte == 0 or fte == "0" or fte == "":
continue
nwds = None
nwds = []
if nwd:
nwds = []
for i in nwd.split(","):
nwd_start_date = Rota.start_date
nwd_end_date = Rota.rota_end_date
@@ -327,7 +327,7 @@ else:
oop,
not_available_to_work=leave_requests,
work_requests=work_requests,
home_site=site_pref,
remote_site=site_pref,
previous_shifts=previous_shifts,
pair=pair,
shift_balance_extra=w["shift_balance_extra"],
+405
View File
@@ -0,0 +1,405 @@
from copy import deepcopy
from black import main
import pytest
from rota.shifts import NoWorkers, RotaBuilder, SingleShift, days
import datetime
from rota.workers import Worker
def weeks_from_list(lst):
"""Yield successive n-sized chunks from a lst."""
for i in range(0, len(lst), 7):
yield lst[i : i + 7]
class TestDemoRota:
def test_nwd(self):
# Set up rota
weeks_to_rota = 10
start_date = datetime.date(2022, 3, 7)
self.Rota = RotaBuilder(
start_date,
weeks_to_rota=weeks_to_rota,
)
# Add a few workers
worker1 = Worker(self.Rota, "worker1", "group1", 1, nwd=["Mon"])
worker2 = Worker(self.Rota, "worker2", "group1", 1, nwd=["Fri"])
self.Rota.add_workers((worker1, worker2))
# Add a weekday and weekend shift
self.Rota.add_shifts(
SingleShift(
("group1",),
"d",
12.5,
days[:5],
balance_offset=10,
workers_required=1,
),
SingleShift(
("group1",),
"w",
12.5,
days[5:],
workers_required=2,
),
)
self.Rota.build_and_solve(options={"ratio": 0.00})
self.Rota.export_rota_to_html("nwd")
assert self.Rota.results.solver.status == "ok"
assert self.Rota.results.solver.termination_condition == "optimal"
for worker in self.Rota.workers:
shifts = self.Rota.get_worker_shift_list(worker)
# Convert shift to a string representation
shifts_string = "".join([i if i != "" else "-" for i in shifts])
if worker.name == "worker1":
assert "wwd" not in shifts_string
if worker.name == "worker2":
assert "dww" not in shifts_string
def test_nwd_simple_fail(self):
# Set up rota
weeks_to_rota = 10
start_date = datetime.date(2022, 3, 7)
self.Rota = RotaBuilder(
start_date,
weeks_to_rota=weeks_to_rota,
)
# Add a few workers
worker1 = Worker(self.Rota, "worker1", "group1", 1, nwd=["Mon"])
worker2 = Worker(self.Rota, "worker2", "group1", 1, nwd=["Mon"])
self.Rota.add_workers((worker1, worker2))
# Add a weekday and weekend shift
self.Rota.add_shifts(
SingleShift(
("group1",),
"d",
12.5,
days[:5],
balance_offset=10,
workers_required=1,
),
)
self.Rota.build_and_solve(options={"ratio": 0.00})
self.Rota.export_rota_to_html("nwd")
assert self.Rota.results.solver.status == "warning"
def test_nwd_partial_rota(self):
# Set up rota
weeks_to_rota = 10
start_date = datetime.date(2022, 3, 7)
self.Rota = RotaBuilder(
start_date,
weeks_to_rota=weeks_to_rota,
)
d = start_date + datetime.timedelta(weeks=5)
# Add a few workers
worker1 = Worker(
self.Rota, "worker1", "group1", 1, nwd=[("Mon", start_date, d)]
)
worker2 = Worker(
self.Rota, "worker2", "group1", 1, nwd=[("Mon", d, self.Rota.rota_end_date)]
)
self.Rota.add_workers((worker1, worker2))
# Add a weekday and weekend shift
self.Rota.add_shifts(
SingleShift(
("group1",),
"d",
12.5,
days[:5],
balance_offset=10,
workers_required=1,
),
)
self.Rota.build_and_solve(options={"ratio": 0.00})
self.Rota.export_rota_to_html("nwd")
assert self.Rota.results.solver.status == "ok"
def test_nwd_partial_overlay(self):
# Set up rota
weeks_to_rota = 10
start_date = datetime.date(2022, 3, 7)
self.Rota = RotaBuilder(
start_date,
weeks_to_rota=weeks_to_rota,
)
d = start_date + datetime.timedelta(weeks=5)
d2 = start_date + datetime.timedelta(weeks=6)
# Add a few workers
worker1 = Worker(
self.Rota, "worker1", "group1", 1, nwd=[("Mon", start_date, d2)]
)
worker2 = Worker(
self.Rota, "worker2", "group1", 1, nwd=[("Mon", d, self.Rota.rota_end_date)]
)
self.Rota.add_workers((worker1, worker2))
# Add a weekday and weekend shift
self.Rota.add_shifts(
SingleShift(
("group1",),
"d",
12.5,
days[:5],
balance_offset=10,
workers_required=1,
),
)
self.Rota.build_and_solve(options={"ratio": 0.00})
assert self.Rota.results.solver.status == "warning"
def test_nwd_force_as_block(self):
# Set up rota
weeks_to_rota = 10
start_date = datetime.date(2022, 3, 7)
self.Rota = RotaBuilder(
start_date,
weeks_to_rota=weeks_to_rota,
)
d = start_date + datetime.timedelta(weeks=4)
worker1 = Worker(
self.Rota, "worker1", "group1", 1, nwd=[("Mon", start_date, d)]
)
worker2 = Worker(
self.Rota,
"worker2",
"group1",
1,
)
worker3 = Worker(
self.Rota,
"worker3",
"group1",
1,
)
worker4 = Worker(
self.Rota,
"worker4",
"group1",
1,
)
self.Rota.add_workers((worker1, worker2, worker3, worker4))
# Add a weekday and weekend shift
self.Rota.add_shifts(
SingleShift(
("group1",),
"d",
12.5,
days[:5],
balance_offset=10,
workers_required=2,
force_as_block_unless_nwd=True,
assign_as_block=True,
),
)
self.Rota.build_and_solve(options={"ratio": 0.00})
self.Rota.export_rota_to_html("nwd")
assert self.Rota.results.solver.status == "ok"
for worker in self.Rota.workers:
shifts = self.Rota.get_worker_shift_list(worker)
# Convert shift to a string representation
shifts_string = "".join([i if i != "" else "-" for i in shifts])
for week in weeks_from_list(shifts_string):
assert week in ("-------", "ddddd--")
assert shifts_string.count("d") == 25
def test_nwd_force_as_block_force_split(self):
# Set up rota
weeks_to_rota = 10
start_date = datetime.date(2022, 3, 7)
self.Rota = RotaBuilder(
start_date,
weeks_to_rota=weeks_to_rota,
)
d = start_date + datetime.timedelta(weeks=5)
worker1 = Worker(
self.Rota, "worker1", "group1", 1, nwd=[("Mon", start_date, d)]
)
worker2 = Worker(
self.Rota,
"worker2",
"group1",
1,
)
worker3 = Worker(
self.Rota,
"worker3",
"group1",
1,
)
worker4 = Worker(
self.Rota,
"worker4",
"group1",
1,
)
self.Rota.add_workers((worker1, worker2, worker3, worker4))
# Add a weekday and weekend shift
self.Rota.add_shifts(
SingleShift(
("group1",),
"d",
12.5,
days[:5],
balance_offset=10,
workers_required=2,
force_as_block_unless_nwd=True,
assign_as_block=True,
),
SingleShift(
("group1",),
"w",
12.5,
days[5:],
balance_offset=10,
workers_required=4,
force_as_block_unless_nwd=True,
assign_as_block=True,
),
)
self.Rota.build_and_solve(options={"ratio": 0.00})
self.Rota.export_rota_to_html("nwd")
assert self.Rota.results.solver.status == "ok"
for worker in self.Rota.workers:
shifts = self.Rota.get_worker_shift_list(worker)
# Convert shift to a string representation
shifts_string = "".join([i if i != "" else "-" for i in shifts])
for week in weeks_from_list(shifts_string[7 * 5 :]):
assert week in ("-----ww", "dddddww")
if worker.name == "worker1":
assert shifts_string[: 7 * 5].count("ww-") == 4
assert shifts_string.count("d") == 25
assert shifts_string.count("w") == 20
def test_nwd_testing(self):
# Set up rota
weeks_to_rota = 10
start_date = datetime.date(2022, 3, 7)
self.Rota = RotaBuilder(
start_date,
weeks_to_rota=weeks_to_rota,
)
d = start_date + datetime.timedelta(weeks=5)
worker1 = Worker(
self.Rota,
"worker1",
"group1",
1,
nwd=[
("Mon", d, self.Rota.rota_end_date),
("Tue", d, self.Rota.rota_end_date),
("Wed", d, self.Rota.rota_end_date),
("Thu", d, self.Rota.rota_end_date),
("Fri", d, self.Rota.rota_end_date),
],
)
worker2 = Worker(
self.Rota,
"worker2",
"group1",
1,
)
self.Rota.add_workers((worker1, worker2))
# Add a weekday and weekend shift
self.Rota.add_shifts(
SingleShift(
("group1",),
"d",
12.5,
days[:5],
balance_offset=10,
workers_required=1,
force_as_block_unless_nwd=True,
assign_as_block=True,
),
SingleShift(
("group1",),
"w",
12.5,
days[5:],
balance_offset=10,
workers_required=2,
force_as_block_unless_nwd=True,
assign_as_block=True,
),
)
self.Rota.build_and_solve(options={"ratio": 0.00})
self.Rota.export_rota_to_html("nwd")
assert self.Rota.results.solver.status == "ok"
for worker in self.Rota.workers:
shifts = self.Rota.get_worker_shift_list(worker)
# Convert shift to a string representation
shifts_string = "".join([i if i != "" else "-" for i in shifts])
for week in weeks_from_list(shifts_string[7 * 5 :]):
if worker.name == "worker1":
assert week == "-----ww"
else:
assert week == "dddddww"
for week in weeks_from_list(shifts_string[:7 * 5 ]):
if worker.name == "worker1":
assert week == "dddddww"
else:
assert week == "-----ww"
+13 -13
View File
@@ -49,7 +49,7 @@ class TestDemoRota:
"weekday",
12.5,
days[:5],
balance_offset=10,
balance_offset=1,
workers_required=1,
# We use a different balance weighting for each shift as otherwise
# the shifts can be treated equally (and no target diff)
@@ -60,7 +60,7 @@ class TestDemoRota:
"weekend",
12.5,
days[5:],
balance_offset=40,
balance_offset=4,
# balance_weighting=0.5
),
)
@@ -595,12 +595,12 @@ class TestLimitConstraints:
)
# Rota.constraint_options["max_shifts_per_week"] = 5
# Rota.constraint_options["max_shifts_per_month"] = 20
worker1 = Worker(Rota, "worker1", "group1", 2, home_site="group1")
worker2 = Worker(Rota, "worker2", "group1", 2, home_site="group1")
worker3 = Worker(Rota, "worker3", "group1", 2, home_site="group1")
worker4 = Worker(Rota, "worker4", "group3", 3, home_site="group2")
worker5 = Worker(Rota, "worker5", "group3", 3, home_site="group2")
worker6 = Worker(Rota, "worker6", "group3", 3, home_site="group2")
worker1 = Worker(Rota, "worker1", "group1", 2, remote_site="group1")
worker2 = Worker(Rota, "worker2", "group1", 2, remote_site="group1")
worker3 = Worker(Rota, "worker3", "group1", 2, remote_site="group1")
worker4 = Worker(Rota, "worker4", "group3", 3, remote_site="group2")
worker5 = Worker(Rota, "worker5", "group3", 3, remote_site="group2")
worker6 = Worker(Rota, "worker6", "group3", 3, remote_site="group2")
Rota.add_workers((worker1, worker2, worker3, worker4))
Rota.add_workers((worker5, worker6))
@@ -853,7 +853,7 @@ class TestLimitConstraints:
with pytest.raises(ValueError):
self.Rota.build_and_solve(options={"ratio": 0.1})
def test_constraint_require_home_site_presence(self):
def test_constraint_require_remote_site_presence(self):
self.Rota.shifts = []
self.Rota.add_shifts(
SingleShift(
@@ -864,7 +864,7 @@ class TestLimitConstraints:
balance_offset=40,
workers_required=2,
force_as_block=True,
constraints=[("require_home_site_presence", ("group1", 2))],
constraints=[("require_remote_site_presence", ("group1", 2))],
),
)
self.Rota.build_and_solve(options={"ratio": 0.00})
@@ -887,7 +887,7 @@ class TestLimitConstraints:
assert self.Rota.results.solver.status == "ok"
assert self.Rota.results.solver.termination_condition == "optimal"
def test_constraint_require_home_site_presence2(self):
def test_constraint_require_remote_site_presence2(self):
self.Rota.shifts = []
self.Rota.add_shifts(
SingleShift(
@@ -898,7 +898,7 @@ class TestLimitConstraints:
balance_offset=40,
workers_required=3,
force_as_block=True,
constraints=[("require_home_site_presence", ("group2", 2))],
constraints=[("require_remote_site_presence", ("group2", 2))],
),
)
self.Rota.build_and_solve(options={"ratio": 0.00})
@@ -1058,4 +1058,4 @@ class TestNightUnavailable:
if __name__ == "__main__":
t = TestLimitConstraints()
t.test_constraint_require_home_site_presence()
t.test_constraint_require_remote_site_presence()
+132
View File
@@ -7,8 +7,140 @@ import datetime
from rota.workers import Worker
def weeks_from_list(lst):
"""Yield successive n-sized chunks from a lst."""
for i in range(0, len(lst), 7):
yield lst[i : i + 7]
class TestDemoRota:
def test_balance_weekend(self):
# Set up rota
weeks_to_rota = 10
start_date = datetime.date(2022, 3, 7)
self.Rota = RotaBuilder(
start_date,
weeks_to_rota=weeks_to_rota,
)
self.Rota.constraint_options["balance_weekends"] = True
# Add a few workers
worker1 = Worker(self.Rota, "worker1", "group1", 1)
worker2 = Worker(self.Rota, "worker2", "group1", 1)
worker3 = Worker(self.Rota, "worker3", "group1", 1)
worker4 = Worker(self.Rota, "worker4", "group1", 1)
self.Rota.add_workers((worker1, worker2, worker3, worker4))
# Add a weekday and weekend shift
self.Rota.add_shifts(
SingleShift(
("group1",),
"a",
12.5,
days,
balance_offset=1,
workers_required=1,
),
SingleShift(
("group1",),
"b",
12.5,
days[5:],
balance_offset=0,
workers_required=1
),
)
self.Rota.build_and_solve(options={"ratio": 0.00})
self.Rota.export_rota_to_html("balance_weekend")
assert self.Rota.results.solver.status == "ok"
assert self.Rota.results.solver.termination_condition == "optimal"
for worker in self.Rota.workers:
shifts = self.Rota.get_worker_shift_list(worker)
# Convert shift to a string representation
shifts_string = "".join([i if i != "" else "-" for i in shifts])
assert shifts_string.count("b") == 5
assert shifts_string.count("a") in (17,18)
weekend_count = 0
for week in weeks_from_list(shifts_string):
if week[5:] == "--":
continue
weekend_count = weekend_count + 1
assert weekend_count == 5 == self.Rota.model.worker_weekend_count[worker.id].value
def test_balance_weekend2(self):
# Set up rota
weeks_to_rota = 10
start_date = datetime.date(2022, 3, 7)
self.Rota = RotaBuilder(
start_date,
weeks_to_rota=weeks_to_rota,
)
self.Rota.constraint_options["balance_weekends"] = True
# Add a few workers
worker1 = Worker(self.Rota, "worker1", "group1", 1)
worker2 = Worker(self.Rota, "worker2", "group1", 1)
worker3 = Worker(self.Rota, "worker3", "group1", 1)
worker4 = Worker(self.Rota, "worker4", "group1", 1)
worker5 = Worker(self.Rota, "worker5", "group1", 1)
self.Rota.add_workers((worker1, worker2, worker3, worker4, worker5))
# Add a weekday and weekend shift
self.Rota.add_shifts(
SingleShift(
("group1",),
"a",
12.5,
days,
balance_offset=5,
workers_required=1,
force_as_block=True
),
SingleShift(
("group1",),
"b",
12.5,
days[5:],
balance_offset=0,
workers_required=2
),
)
self.Rota.build_and_solve(options={"ratio": 0.00})
self.Rota.export_rota_to_html("balance_weekend2")
assert self.Rota.results.solver.status == "ok"
assert self.Rota.results.solver.termination_condition == "optimal"
for worker in self.Rota.workers:
shifts = self.Rota.get_worker_shift_list(worker)
# Convert shift to a string representation
shifts_string = "".join([i if i != "" else "-" for i in shifts])
assert shifts_string.count("b") == 8
assert shifts_string.count("a") == 14
weekend_count = 0
for week in weeks_from_list(shifts_string):
if week[5:] == "--":
continue
weekend_count = weekend_count + 1
assert weekend_count == 6 == self.Rota.model.worker_weekend_count[worker.id].value
def test_post_mon_tue(self):
# Set up rota
weeks_to_rota = 10