This repository has been archived by the owner on Dec 28, 2022. It is now read-only.
/
womanager.py
218 lines (175 loc) · 7.8 KB
/
womanager.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# -*- coding: utf-8 -*-
"""
This program make work schedule from 08/01/2013 with these simple rules:
- 3 workers (Jane, Jack, Joe)
- 22 working days a month (even Satruday, Sunday)
- every third weekend is free (Friday, Saturday)
- if all the three people have to work, posssibly be that on Friday
- the remaining 6-7 freedays should be distributed evenly,
no 5 consecutive freedays next to each other, max 2
- every day is a workday with minimum 2 people
- dec. 24, 25, 26 and jan. 1 CLOSED
"""
import calendar
import datetime
from PyQt4.QtGui import QApplication, QMainWindow, QTableWidgetItem, QColor, QDialog, QFontDatabase, QMessageBox
from PyQt4.QtCore import Qt
import sys
import mainwindow
import helpwindow
import about
FRIDAY_RED = QColor(255, 155, 103)
FREE_GREEN = QColor(168, 255, 171)
def month_days_num(year, month):
return calendar.monthrange(year, month)[1]
def get_next_month(year, month):
""" http://stackoverflow.com/a/4131007/720077 """
plus_year, new_month = divmod(month + 1, 12)
if new_month == 0:
new_month = 12
plus_year -= 1
return year + plus_year, new_month
class Employee(object):
def __init__(self, name, first_free_day, working_days_num=22):
self.name = name
self.working_days_num = working_days_num
self.first_free_day = first_free_day
def count_free_days(self, year, month):
"""Count free weekend days (every third weekend), based on the fixed first schedule."""
self.free_days = []
if self.first_free_day.year == year and self.first_free_day.month == month:
self.free_days.append(self.first_free_day.day)
days_num = month_days_num(year, month)
day = 1
while day <= days_num:
difference = datetime.date(year, month, day) - self.first_free_day
if difference.days > 0 and (difference.days % 21 == 0 or difference.days % 21 == 1):
self.free_days.append(day)
day += 1
def __unicode__(self):
return self.name
def __str__(self):
return self.name.encode('utf-8')
class Schedule(object):
def __init__(self, year, month, employees):
self.year, self.month = year, month
self.month_days_num = month_days_num(year, month)
self.employees = employees
self.schedule = []
for employee in self.employees:
employee.count_free_days(self.year, self.month)
self.schedule.append(self._init_schedule(employee.free_days))
self._make_schedule()
def is_friday(self, day):
return calendar.weekday(self.year, self.month, day) == 4
def _init_schedule(self, free_days):
"""Initialize schedule, mark every day as working days, except for free_days."""
return [day not in free_days for day in xrange(1, self.month_days_num + 1)]
def _make_schedule(self):
"""Remove free days from the initial schedule, based on these rules:
- Everybody working on every friday if possible.
- at least two people are working on the same day.
"""
for ind, employee in enumerate(self.employees):
remained = self.month_days_num - len(employee.free_days) - employee.working_days_num
consecutive = 0
for dayind in xrange(self.month_days_num):
working = self.schedule[ind][dayind]
if dayind + 1 in employee.free_days:
consecutive += 1
elif consecutive >= 2:
consecutive = 0
elif working and \
not self.is_friday(dayind+1) and \
dayind + 1 not in employee.free_days and \
sum([self.schedule[empind][dayind] for empind in xrange(3)]) > 2:
self.schedule[ind][dayind] = False
remained -= 1
consecutive += 1
if remained == 0:
break
dayind = 0
while dayind < self.month_days_num and remained > 0:
if self.is_friday(dayind+1):
self.schedule[2][dayind] = False
remained -= 1
dayind += 1
class TableWidgetCenteredItem(QTableWidgetItem):
"""
Read only centered cells in QTableWidget.
"""
def __init__(self, *args, **kwargs):
super(TableWidgetCenteredItem, self).__init__(*args, **kwargs)
self.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
self.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
class AboutDialog(QDialog, about.Ui_About):
"""
Dialog with my contact.
"""
def __init__(self, *args, **kwargs):
super(AboutDialog, self).__init__(*args, **kwargs)
self.setupUi(self)
class HelpDialog(QDialog, helpwindow.Ui_Dialog):
"""
A simple help dialog.
"""
def __init__(self, *args, **kwargs):
super(HelpDialog, self).__init__(*args, **kwargs)
self.setupUi(self)
class MainWindow(QMainWindow, mainwindow.Ui_MainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setupUi(self)
self.dateEdit.setDate(datetime.datetime.now())
self.pushButton.clicked.connect(self.fill_data)
self.actionAbout.triggered.connect(self.about)
self.actionHelp.triggered.connect(self.help)
self.actionLoad.triggered.connect(self.notimplemented)
self.actionSave.triggered.connect(self.notimplemented)
self.actionExport.triggered.connect(self.notimplemented)
def fill_data(self):
date = self.dateEdit.date()
year, month = date.year(), date.month()
widgets_to_fill = ((self.first_month_table, self.first_month_label),
(self.second_month_table, self.second_month_label),
(self.third_month_table, self.third_month_label))
for table, label in widgets_to_fill:
self.fill_table(table, year, month)
self._set_month_label(label, year, month)
year, month = get_next_month(year, month)
def fill_table(self, table_widget, year, month):
schedule = Schedule(year, month, [Employee(u'Jack', datetime.date(2013, 8, 10)),
Employee(u'Jane', datetime.date(2013, 8, 17)),
Employee(u'Joe', datetime.date(2013, 8, 24))
]
)
table_widget.setColumnCount(schedule.month_days_num)
for row, sched in enumerate(schedule.schedule):
for col, day in enumerate(sched):
item = TableWidgetCenteredItem('X') if schedule.schedule[row][col] else TableWidgetCenteredItem()
if schedule.is_friday(col+1):
item.setBackground(FRIDAY_RED)
elif col+1 in schedule.employees[row].free_days:
item.setBackground(FREE_GREEN)
table_widget.setItem(row, col, item)
def _set_month_label(self, label_widget, year, month):
label_widget.setText(unicode(year) + '. ' + calendar.month_name[month])
def about(self):
"""Show about dialog."""
about = AboutDialog(self)
about.exec_()
def help(self):
"""Show help dialog."""
help = HelpDialog(self)
help.show()
def notimplemented(self):
QMessageBox.information(self, u'Not implemented',
u'This function is not implemented!\n'
u'If you need it, you can hire me for developing it.\n'
u'Check About menu!')
if __name__ == "__main__":
app = QApplication(sys.argv)
QFontDatabase.addApplicationFont(':/font/general_foundicons.ttf')
form = MainWindow()
form.show()
sys.exit(app.exec_())