Total Complexity | 42 |
Total Lines | 141 |
Duplicated Lines | 85.11 % |
Changes | 0 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like dynamicserialize.dstypes.com.raytheon.uf.common.time.TimeRange often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | # |
||
2 | # SOFTWARE HISTORY |
||
3 | # |
||
4 | # Date Ticket# Engineer Description |
||
5 | # ------------ ---------- ----------- -------------------------- |
||
6 | # ??/??/?? xxxxxxxx Initial Creation. |
||
7 | # 01/22/14 2667 bclement fixed millisecond support |
||
8 | # 02/28/14 2667 bclement constructor can take extra micros for start and end |
||
9 | # 06/24/15 4480 dgilling fix __eq__. |
||
10 | # |
||
11 | # |
||
12 | |||
13 | import calendar |
||
14 | import datetime |
||
15 | import time |
||
16 | |||
17 | MAX_TIME = 2147483647 |
||
18 | MICROS_IN_SECOND = 1000000 |
||
19 | |||
20 | |||
21 | View Code Duplication | class TimeRange(object): |
|
|
|||
22 | def __init__(self, start=None, end=None, startExtraMicros=None, endExtraMicros=None): |
||
23 | self.start = self.__convertToDateTimeWithExtra(start, startExtraMicros) |
||
24 | self.end = self.__convertToDateTimeWithExtra(end, endExtraMicros) |
||
25 | |||
26 | def __str__(self): |
||
27 | return self.__repr__() |
||
28 | |||
29 | def __repr__(self): |
||
30 | return "(" + self.start.strftime("%b %d %y %H:%M:%S %Z") + ", " + \ |
||
31 | self.end.strftime("%b %d %y %H:%M:%S %Z") + ")" |
||
32 | |||
33 | def __eq__(self, other): |
||
34 | if not isinstance(self, type(other)): |
||
35 | return False |
||
36 | |||
37 | if self.isValid() and other.isValid(): |
||
38 | return self.getStart() == other.getStart() and self.getEnd() == other.getEnd() |
||
39 | elif not self.isValid() and not other.isValid(): |
||
40 | return True |
||
41 | else: |
||
42 | return False |
||
43 | |||
44 | def __ne__(self, other): |
||
45 | return not self.__eq__(other) |
||
46 | |||
47 | def __convertToDateTimeWithExtra(self, timeArg, extraMicros): |
||
48 | rval = self.__convertToDateTime(timeArg) |
||
49 | if rval is not None and extraMicros is not None: |
||
50 | rval = rval + datetime.timedelta(microseconds=extraMicros) |
||
51 | return rval |
||
52 | |||
53 | def __convertToDateTime(self, timeArg): |
||
54 | if timeArg is None: |
||
55 | return None |
||
56 | if isinstance(timeArg, datetime.datetime): |
||
57 | return timeArg |
||
58 | elif isinstance(timeArg, time.struct_time): |
||
59 | return datetime.datetime(*timeArg[:6]) |
||
60 | elif isinstance(timeArg, float): |
||
61 | # seconds as float, should be avoided due to floating point errors |
||
62 | totalSecs = int(timeArg) |
||
63 | micros = int((timeArg - totalSecs) * MICROS_IN_SECOND) |
||
64 | return self.__convertSecsAndMicros(totalSecs, micros) |
||
65 | elif isinstance(timeArg, int): |
||
66 | # seconds as integer |
||
67 | totalSecs = timeArg |
||
68 | return self.__convertSecsAndMicros(totalSecs, 0) |
||
69 | else: |
||
70 | return None |
||
71 | |||
72 | def __convertSecsAndMicros(self, seconds, micros): |
||
73 | if seconds < MAX_TIME: |
||
74 | rval = datetime.datetime.utcfromtimestamp(seconds) |
||
75 | else: |
||
76 | extraTime = datetime.timedelta(seconds=(seconds - MAX_TIME)) |
||
77 | rval = datetime.datetime.utcfromtimestamp(MAX_TIME) + extraTime |
||
78 | return rval.replace(microsecond=micros) |
||
79 | |||
80 | def getStart(self): |
||
81 | return self.start.utctimetuple() |
||
82 | |||
83 | def getStartInMillis(self): |
||
84 | return self._getInMillis(self.start) |
||
85 | |||
86 | def setStart(self, start, extraMicros=None): |
||
87 | self.start = self.__convertToDateTimeWithExtra(start, extraMicros) |
||
88 | |||
89 | def getEnd(self): |
||
90 | return self.end.utctimetuple() |
||
91 | |||
92 | def getEndInMillis(self): |
||
93 | return self._getInMillis(self.end) |
||
94 | |||
95 | def _getInMillis(self, time): |
||
96 | rval = int(calendar.timegm(time.utctimetuple()) * 1000) |
||
97 | rval += time.microsecond // 1000 |
||
98 | return rval |
||
99 | |||
100 | def setEnd(self, end, extraMicros=None): |
||
101 | self.end = self.__convertToDateTimeWithExtra(end, extraMicros) |
||
102 | |||
103 | def duration(self): |
||
104 | delta = self.end - self.start |
||
105 | return int(delta.total_seconds()) |
||
106 | |||
107 | def contains(self, timeArg): |
||
108 | if isinstance(timeArg, TimeRange): |
||
109 | if self.duration() == 0: |
||
110 | return self.__eq__(timeArg) |
||
111 | elif timeArg.duration() == 0: |
||
112 | return self.contains(timeArg.start) |
||
113 | return timeArg.start >= self.start and timeArg.end <= self.end |
||
114 | else: |
||
115 | convTime = self.__convertToDateTime(timeArg) |
||
116 | if not isinstance(convTime, datetime.datetime): |
||
117 | raise TypeError("Invalid type for argument time specified to TimeRange.contains().") |
||
118 | if self.duration() != 0: |
||
119 | return self.start <= convTime < self.end |
||
120 | return convTime == self.start |
||
121 | |||
122 | def isValid(self): |
||
123 | return bool(self.start != self.end) |
||
124 | |||
125 | def overlaps(self, timeRange): |
||
126 | return timeRange.contains(self.start) or self.contains(timeRange.start) |
||
127 | |||
128 | def combineWith(self, timeRange): |
||
129 | if self.isValid() and timeRange.isValid(): |
||
130 | newStart = min(self.start, timeRange.start) |
||
131 | newEnd = max(self.end, timeRange.end) |
||
132 | return TimeRange(newStart, newEnd) |
||
133 | elif self.isValid(): |
||
134 | return self |
||
135 | |||
136 | return timeRange |
||
137 | |||
138 | @staticmethod |
||
139 | def allTimes(): |
||
140 | return TimeRange(0, MAX_TIME) |
||
141 |