~dricottone/fmg-timesheets

ref: 9efdae59717e3d48470a4834470072a86f5fb706 fmg-timesheets/parser/timeentry.py -rw-r--r-- 5.8 KiB
9efdae59Dominic Ricottone Implemented time entry extraction; no assert errors! 2 years ago
                                                                                
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
#!/usr/bin/env python3

from re import compile as re_compile

PROJECT_PATTERN = re_compile("(OHORG\.(MTG|ONB|PFR|TPD|TOH)\.[01][0-9]\.00[0-9]|UNALW\.EMW\.0[0-9]\.00[0-9]|FSERV\.PPM\.00\.000|[0-9]{5}\.[0-9]{3}\.[0-9]{2}\.[0-9]{3})")
TIMETYPE_PATTERN = re_compile("(FSERV|[0-9]{3,5})")
DATE_PATTERN = re_compile("[ADFJMNOS][aceopu][bcglnprtvy] [0-9][0-9]?, 20[12][89012]")

ENTRY_PATTERNS = (
    re_compile("[1-9][0-9]?"),
    re_compile("(HOL|OTU|ST|VAC)"),
    re_compile("(OHORG\.(MTG|ONB|PFR|TPD|TOH)\.[01][0-9]\.00[0-9]|UNALW\.EMW\.0[0-9]\.00[0-9]|FSERV\.PPM\.00\.000|[0-9]{5}\.[0-9]{3}\.[0-9]{2}\.[0-9]{3})"),
    re_compile("(FSERV|[0-9]{3,5})"),
    re_compile("."),
    re_compile("Notes"),
    re_compile("[ADFJMNOS][aceopu][bcglnprtvy] [0-9][0-9]?, 20[12][89012] -"),
    re_compile("[ADFJMNOS][aceopu][bcglnprtvy] [0-9][0-9]?, 20[12][89012] Other"),
    re_compile("."),
    re_compile("Approved"),
)

class TimeEntry(object):
    def __init__(self, semistructured_data, timesheet_name, entry_number):
        self._issues = []
        self._timesheet = timesheet_name
        self._entry = entry_number
        self._data = self.beat_data_with_a_stick(semistructured_data)
        self.parse_entry_number()
        self.parse_entry_project()
        self.parse_entry_notes()
        self.parse_entry_approval()

    def __str__(self):
        return f"entry #{self._entry} in the timesheet for {self._timesheet}"

    def __len__(self):
        return len(self._data)

    def report_issues(self):
        if self._issues:
            print(f"There are some issues in {self}...")
            for issue in self._issues:
                print(issue)
            for index, line in enumerate(self._data):
                print(index, line)

    def log_issue(self, *parts):
        self._issues.append(''.join(parts))


    def assert_entry_item_any(self, index, label, should_be_any):
        if self._data[index][0] not in should_be_any:
            self.log_issue(
                "parse_entry: ",
                f"{label} not one of {', '.join(should_be_any)} ",
                f"(is {self._data[index][0]})",
            )

    def assert_entry_item_match(self, index, label, pattern):
        if not pattern.match(self._data[index][0]):
            self.log_issue(
                "parse_entry: ",
                f"{label} does not look correct ",
                f"(is {self._data[index][0]})",
            )

    def assert_entry_item_at(self, index, label, should_be_at):
        if self._data[index][1] != should_be_at:
            self.log_issue(
                "parse_entry: ",
                f"{label} not at {should_be_at}px left ",
                f"(is at {self._data[index][1]}px)",
            )

    def parse_entry_number(self):
        del self._data[0]

    def parse_entry_project(self):
        self.assert_entry_item_any(0, "time code", ("HOL", "OTU", "ST", "VAC", ))
        self.assert_entry_item_at(0, "time code", 40)
        self._timecode = self._data[0][0]
        del self._data[0]

        if self._timecode in ("HOL", "OTU", "VAC", ):
            self._project = ""
            self._timetype = ""
        elif " " in self._data[0][0]:
            self.assert_entry_item_match(0, "project", PROJECT_PATTERN)
            self.assert_entry_item_at(0, "project", 120)
            project_timetype = self._data[0][0].split(" ", 1)
            self._project = project_timetype[0]
            self._timetype = project_timetype[1]
            del self._data[0]
        else:
            self.assert_entry_item_match(0, "project", PROJECT_PATTERN)
            self.assert_entry_item_at(0, "project", 120)
            self._project = self._data[0][0]
            self.assert_entry_item_match(1, "time type", TIMETYPE_PATTERN)
            self.assert_entry_item_at(1, "time type", 201)
            self._timetype = self._data[0][0]
            del self._data[0:2]

        self._name = self._data[0][0]
        del self._data[0]

    def parse_entry_note(self):
        if self._data[0][0] == "Line Note:":
            # There is no start or end field, just the text field
            self._notes.append({
                "start": "",
                "end": "",
                "text": self._data[1][0],
            })
            del self._data[0:2]
        else:
            match = DATE_PATTERN.match(self._data[0][0])
            if match:
                self._notes.append({})

                # Handle the start field
                self.assert_entry_item_at(0, "note start", 40)
                self._notes[-1]["start"] = match

                # Handle the start/end separator if it is separate from the
                # start field
                if self._data[1][0] == "-":
                    del self._data[1]

                # Handle the end field
                self.assert_entry_item_at(1, "note end", 99)
                match = DATE_PATTERN.match(self._data[1][0])
                if match:
                    self._notes[-1]["end"] = match

                # Handle the 'Approved' note if it floated up
                if self._data[2][0] == "Approved":
                    del self._data[2]

                # Handle the text field
                self.assert_entry_item_at(2, "note text", 201)
                self._notes[-1]["text"] = self._data[2][0]

                del self._data[0:3]

    def parse_entry_notes(self):
        print(self._data)
        self._notes = []
        if len(self) and self._data[0][0] == "Notes":
            del self._data[0]

            # One or two notes follow
            if len(self):
                self.parse_entry_note()
            if len(self):
                self.parse_entry_note()

    def parse_entry_approval(self):
        if len(self) and self._data[0][0] == "Approved":
            del self._data[0]

    def beat_data_with_a_stick(self, data):
        data = [i for i in data if i[1]<400] + [i for i in data if i[1]>=400]
        return data