| 18 | | from zope.schema import Text, TextLine, Date, Timedelta, Tuple, Int |
| 19 | | |
| 20 | | class ITimeLogSubKey(Interface): |
| 21 | | """A sub-key to a timelog entry |
| 22 | | |
| 23 | | Every ITimeLogEntry is stored keyed on a tuple of items adaptable to |
| 24 | | ITimeLogEntry. Identifiers (the id field) need only be unique for the |
| 25 | | position in the tuple they are used in; e.g. key[0].id == 'foo' and |
| 26 | | key[1].id == 'foo' but key[0] and key[1] can be totally different sub-keys. |
| 27 | | |
| 28 | | """ |
| | 18 | from zope.interface.common.mapping import IEnumerableMapping |
| | 19 | from zope.schema import Text, TextLine, Date, Timedelta, Set, Int |
| | 20 | |
| | 21 | class ITimeLogKey(Interface): |
| | 22 | """A key for a timelog entry |
| | 23 | |
| | 24 | Every ITimeLogEntry is stored keyed on a set of ITimeLogKey. Keys are |
| | 25 | unique for a given prefix and id; the prefix denotes the source from |
| | 26 | which they where obtained. |
| | 27 | |
| | 28 | """ |
| | 29 | prefix = TextLine( |
| | 30 | title=u'Prefix', |
| | 31 | description=u'The source identifier of the key.', |
| | 32 | readonly=True, |
| | 33 | required=True) |
| | 34 | |
| 42 | | description=u'The title of the sub-key; usually displayed in the UI.', |
| 43 | | required=False) |
| 44 | | |
| 45 | | |
| 46 | | class TimeLogSubKeyLookupError(LookupError): |
| 47 | | """A sub-key could not be found for a given id""" |
| 48 | | |
| 49 | | |
| 50 | | class ITimeLogSubKeySource(Interface): |
| 51 | | """A source of sub-keys for a key position |
| 52 | | |
| 53 | | For any given key position only one source can be used, but a source can |
| 54 | | be re-used for multiple key positions. |
| 55 | | |
| 56 | | """ |
| 57 | | def getSubKey(id): |
| 58 | | """Return the `ITimeLogSubKey` with the given id |
| | 47 | description=u'The title of the key; usually displayed in the UI.', |
| | 48 | required=False) |
| | 49 | |
| | 50 | |
| | 51 | class TimeLogKeyLookupError(LookupError): |
| | 52 | """A key could not be found for a given id""" |
| | 53 | |
| | 54 | |
| | 55 | class ITimeLogKeySource(Interface): |
| | 56 | """A source of keys |
| | 57 | |
| | 58 | For any given key prefix only one source can be used. |
| | 59 | |
| | 60 | """ |
| | 61 | prefix = TextLine( |
| | 62 | title=u'Prefix', |
| | 63 | description=u'The source identifier of the key.', |
| | 64 | required=True) |
| | 65 | |
| | 66 | def getKey(id): |
| | 67 | """Return the `ITimeLogKey` with the given id |
| 64 | | def listSubKeys(): |
| 65 | | """Return an iterable of all sub-keys""" |
| 66 | | |
| 67 | | |
| 68 | | class ITimeLogKeySource(Interface): |
| 69 | | """A source of ITimeLogSubKeySources""" |
| 70 | | keyLength = Int( |
| 71 | | title=u'Key Length', |
| 72 | | description=u'''Number of sub-key sources provided.''', |
| 73 | | required=True, |
| 74 | | readonly=True) |
| 75 | | |
| 76 | | def getSubKeySource(position): |
| 77 | | """Return an `ITimeLogSubKeySource` for a given key position |
| 78 | | |
| 79 | | Raises an ``IndexError`` when no source exists at that position |
| 80 | | |
| 81 | | """ |
| | 73 | def listKeys(): |
| | 74 | """Return an iterable of all keys in this source""" |
| | 75 | |
| | 76 | |
| | 77 | class ITimeLogKeySourcesCollection(IEnumerableMapping): |
| | 78 | """A collection of ITimeLogKeySources |
| | 79 | |
| | 80 | ITimeLogKeySources are keyed on their prefixes. |
| | 81 | |
| | 82 | """ |
| 139 | | same key and date are replaced. Any entry with the duration set to None |
| 140 | | can be discarded from the store completely. |
| 141 | | |
| 142 | | Only entries whose keys have keyLength sub-keys are accepted. |
| 143 | | |
| 144 | | """ |
| 145 | | |
| 146 | | def queryKeys(partial_key, start_date=None, end_date=None): |
| | 131 | same keys and date are replaced. Any entry with the duration set to |
| | 132 | None can be discarded from the store completely. |
| | 133 | |
| | 134 | """ |
| | 135 | |
| | 136 | def queryKeys(keys, start_date=None, end_date=None): |
| 159 | | Returns a sequence of complete keys that have been used to log time |
| 160 | | against, expanded from the given ITimeLogSubKeys. So (A, None, None) |
| 161 | | could return ((A, B, C), (A, B, D)), while (None, None, D) would only |
| 162 | | return ((A, B, D),) |
| 163 | | |
| 164 | | """ |
| 165 | | |
| 166 | | def queryDateRange(partial_key): |
| | 148 | Returns a sequence of key sets that have been used to log time |
| | 149 | against, expanded from the given ITimeLogKeys. So the set (A) |
| | 150 | could return ((A, B, C), (A, B, D)), while (D) would onl return |
| | 151 | ((A, B, D),) |
| | 152 | |
| | 153 | """ |
| | 154 | |
| | 155 | def queryDateRange(keys): |
| 174 | | dates there are log entries found. |
| 175 | | |
| 176 | | """ |
| 177 | | |
| 178 | | def iterateDayTotals(partial_key, start_date=None): |
| 179 | | """Iterate over totalled log information matching the partial key` |
| 180 | | |
| 181 | | partial_key |
| 182 | | A ITimeLogStore.keyLength length tuple of Nones and |
| 183 | | ITimeLogSubKeys.` |
| | 162 | dates there are log entries found that used all of the given keys. So |
| | 163 | querying for (A, B) returns the first and last date that any time was |
| | 164 | logged against both those keys. Logs against either (A) or (B) are not |
| | 165 | eligable, but a log entry against (A, B, C) is. |
| | 166 | |
| | 167 | """ |
| | 168 | |
| | 169 | def iterateDayTotals(keys, start_date=None): |
| | 170 | """Iterate over totalled log information matching all the keys` |
| | 171 | |
| | 172 | keys |
| | 173 | A set of ITimeLogKeys.` |