File size: 5,638 Bytes
1748405
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
888971b
1748405
 
888971b
 
1748405
 
 
 
 
 
 
 
 
888971b
1748405
 
888971b
 
1748405
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
888971b
1748405
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from datetime import datetime
from typing import Dict, List

import pandas as pd
from dateutil.parser import parse
from type_alias import EARLY_MODE, LATE_MODE


class WorkingTime(object):

    def __init__(self, date: datetime, checkin: str, checkout: str,
                 config) -> None:
        self.MORNING_IN = parse(config.WORKING_TIME.MORNING_IN)
        self.MORNING_OUT = parse(config.WORKING_TIME.MORNING_OUT)
        self.AFTERNOON_IN = parse(config.WORKING_TIME.AFTERNOON_IN)
        self.AFTERNOON_OUT = parse(config.WORKING_TIME.AFTERNOON_OUT)
        self.LATE_TIME: Dict[str, int] = dict(
            morning_in=config.LATE.MORNING_IN,
            morning_out=config.LATE.MORNING_OUT,
            afternoon_in=config.LATE.AFTERNOON_IN,
            afternoon_out=config.LATE.AFTERNOON_OUT)
        self.ROUND_GAP = config.WORKING_TIME.ROUND_GAP

        self.date = datetime.strftime(date, "%d/%m/%Y")

        self.error: bool = False
        if checkin == 0:
            # self.checkin = -1
            self.error = True
        else:
            self.checkin = parse(checkin)
            if self.checkin < self.MORNING_IN:
                self.checkin = parse("08:00")

        if checkout == 0:
            # self.checkout = -1
            self.error = True
        else:
            self.checkout = parse(checkout)
            if self.checkout > self.AFTERNOON_OUT:
                self.checkout = parse("17:00")

    def __repr__(self) -> str:
        return "{" + ";\n".join(f"{k}={getattr(self, k)}"
                                for k in self.__dict__) + "}"

    def get_late_checkin(self, time: datetime, mode: LATE_MODE) -> int:
        is_penalty_count: int = 0
        if mode == 'morning_in':
            target_time: datetime = self.MORNING_IN
            acceptable: int = self.LATE_TIME.get('MORNING_IN', 30)
        elif mode == 'afternoon_in':
            target_time: datetime = self.AFTERNOON_IN
            acceptable: int = self.LATE_TIME.get('AFTERNOON_IN', 30)
        if time < target_time:
            return 0
        late_time = (time - target_time).seconds // 60
        if late_time > acceptable:
            is_penalty_count = 1
        return is_penalty_count * late_time

    def get_early_checkout(self, time: datetime, mode: EARLY_MODE) -> int:
        is_penalty_count: int = 0
        if mode == 'morning_out':
            target_time: datetime = self.MORNING_OUT
            acceptable: int = self.LATE_TIME.get('MORNING_OUT', 0)
        elif mode == 'afternoon_out':
            target_time: datetime = self.AFTERNOON_OUT
            acceptable: int = self.LATE_TIME.get('AFTERNOON_OUT', 0)
        if time > target_time:
            return 0
        early_time = (target_time - time).seconds // 60
        if early_time > acceptable:
            is_penalty_count = 1
        return is_penalty_count * early_time

    def morning_shift(self):
        if self.error:
            return 0
        if self.checkin > self.MORNING_OUT:
            return 0
        if self.checkout < self.MORNING_IN:
            return 0
        late_checkin_penalty = self.get_late_checkin(time=self.checkin,
                                                     mode='morning_in')
        early_checkout_penalty = self.get_early_checkout(time=self.checkout,
                                                         mode='morning_out')
        return (self.MORNING_OUT - self.MORNING_IN
                ).seconds // 60 - late_checkin_penalty - early_checkout_penalty

    def afternoon_shift(self):
        if self.error:
            return 0
        if self.checkout < self.AFTERNOON_IN:
            return 0
        if self.checkin > self.AFTERNOON_OUT:
            return 0
        late_checkin_penalty = self.get_late_checkin(time=self.checkin,
                                                     mode='afternoon_in')
        early_checkout_penalty = self.get_early_checkout(time=self.checkout,
                                                         mode='afternoon_out')
        return (self.AFTERNOON_OUT - self.AFTERNOON_IN
                ).seconds // 60 - late_checkin_penalty - early_checkout_penalty

    def working_time(self):
        return self.morning_shift() + self.afternoon_shift()

    def normalize(self, num: int) -> int:
        gap = 15
        return round(num / gap) * gap

    def to_tuple(self):
        if self.error:
            return self.date, 0
        return self.date, self.normalize(self.working_time())


class Employee(object):

    def __init__(self,
                 id: str,
                 name: str,
                 workdate: Dict[str, int] = {}) -> None:
        self.id = id
        self.name = name
        self.workdate = workdate

    def __repr__(self) -> str:
        return "{" + ";\n".join(f"{k}={getattr(self, k)}"
                                for k in self.__dict__) + "}"


class WorkingTable(object):

    def __init__(self) -> None:
        pass

    def make_table(self, employees: List[Employee]):
        id_list: List[str] = []
        name_list: List[str] = []
        work_date: List[Dict] = []
        for employee in employees:
            id_list.append(employee.id)
            name_list.append(employee.name)
            work_date.append(employee.workdate)
        work_df: pd.DataFrame = pd.DataFrame(work_date, dtype=int)
        info_df: pd.DataFrame = pd.DataFrame({
            'Mã nhân viên': id_list,
            'Tên nhân viên': name_list
        })
        working_table: pd.DataFrame = pd.concat(objs=[info_df, work_df],
                                                axis=1)
        return working_table