temporal_relationships.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. """!@package grass.temporal
  2. @brief GRASS Python scripting module (temporal GIS functions)
  3. Temporal GIS related functions to be used in temporal GIS Python library package.
  4. Usage:
  5. @code
  6. import grass.temporal as tgis
  7. tgis.print_temporal_relations(maps)
  8. ...
  9. @endcode
  10. (C) 2008-2011 by the GRASS Development Team
  11. This program is free software under the GNU General Public
  12. License (>=v2). Read the file COPYING that comes with GRASS
  13. for details.
  14. @author Soeren Gebbert
  15. """
  16. from abstract_map_dataset import *
  17. from datetime_math import *
  18. import grass.lib.vector as vector
  19. import grass.lib.gis as gis
  20. from ctypes import *
  21. ###############################################################################
  22. class TemporalTopologyBuilder(object):
  23. """!This class is designed to build the temporal topology
  24. of temporally related abstract dataset objects.
  25. The abstract dataset objects must be provided as a single list, or in two lists.
  26. Example:
  27. @code
  28. # We have a space time raster dataset and build a map list
  29. # from all registered maps ordered by start time
  30. maps = strds.get_registered_maps_as_objects()
  31. # Now lets build the temporal topology of the maps in the list
  32. identical = False
  33. tb = TemporalTopologyBuilder()
  34. tb.build(maps)
  35. dbif, connected = init_dbif(None)
  36. for _map in tb:
  37. _map.select(dbif)
  38. _map.print_info()
  39. # Using the next and previous methods, we can iterate over the
  40. # topological related maps in this way
  41. _first = tb.get_first()
  42. while _first:
  43. _first.print_topology_info()
  44. _first = _first.next()
  45. # Dictionary like accessed
  46. _map = tb["name@mapset"]
  47. @endcode
  48. """
  49. def __init__(self):
  50. self._reset()
  51. # 0001-01-01 00:00:00
  52. self._timeref = datetime(1,1,1)
  53. def _reset(self):
  54. self._store = {}
  55. self._first = None
  56. self._iteratable = False
  57. def _set_first(self, first):
  58. self._first = first
  59. self._insert(first)
  60. def _detect_first(self):
  61. if len(self) > 0:
  62. prev_ = self._store.values()[0]
  63. while prev_ is not None:
  64. self._first = prev_
  65. prev_ = prev_.prev()
  66. def _insert(self, t):
  67. self._store[t.get_id()] = t
  68. def get_first(self):
  69. """!Return the first map with the earliest start time
  70. @return The map with the earliest start time
  71. """
  72. return self._first
  73. def _build_internal_iteratable(self, maps):
  74. """!Build an iteratable temporal topology structure for all maps in
  75. the list and store the maps internally
  76. Basically the "next" and "prev" relations will be set in the
  77. temporal topology structure of each map
  78. The maps will be added to the object, so they can be
  79. accessed using the iterator of this class
  80. @param maps: A sorted (by start_time)list of abstract_dataset
  81. objects with initiated temporal extent
  82. """
  83. self._build_iteratable(maps)
  84. for _map in maps:
  85. self._insert(_map)
  86. # Detect the first map
  87. self._detect_first()
  88. def _build_iteratable(self, maps):
  89. """!Build an iteratable temporal topology structure for
  90. all maps in the list
  91. Basically the "next" and "prev" relations will be set in
  92. the temporal topology structure of each map.
  93. @param maps: A sorted (by start_time)list of abstract_dataset
  94. objects with initiated temporal extent
  95. """
  96. # for i in xrange(len(maps)):
  97. # offset = i + 1
  98. # for j in xrange(offset, len(maps)):
  99. # # Get the temporal relationship
  100. # relation = maps[j].temporal_relation(maps[i])
  101. #
  102. # # Build the next reference
  103. # if relation != "equal" and relation != "started":
  104. # maps[i].set_next(maps[j])
  105. # break
  106. # First we need to order the map list chronologically
  107. sorted_maps = sorted(
  108. maps, key=AbstractDatasetComparisonKeyStartTime)
  109. for i in xrange(len(sorted_maps) - 1):
  110. sorted_maps[i].set_next(sorted_maps[i + 1])
  111. for map_ in sorted_maps:
  112. next_ = map_.next()
  113. if next_:
  114. next_.set_prev(map_)
  115. map_.set_topology_build_true()
  116. def _map_to_rect(self, tree, map_):
  117. """Use the temporal extent of a map to create and return a RTree rectange"""
  118. rect = vector.RTreeAllocRect(tree)
  119. start, end = map_.get_valid_time()
  120. if not end:
  121. end = start
  122. if map_.is_time_absolute():
  123. start = time_delta_to_relative_time(start - self._timeref)
  124. end = time_delta_to_relative_time(end - self._timeref)
  125. vector.RTreeSetRect1D(rect, tree, float(start), float(end))
  126. return rect
  127. def _build_1d_rtree(self, maps):
  128. """Build and return the one dimensional R*-Tree"""
  129. tree = vector.RTreeCreateTree(-1, 0, 4)
  130. for i in xrange(len(maps)):
  131. rect = self._map_to_rect(tree, maps[i])
  132. vector.RTreeInsertRect(rect, i + 1, tree)
  133. return tree
  134. def build(self, mapsA, mapsB=None):
  135. """!Build the temporal topology structure between
  136. one or two unordered lists of abstract dataset objects
  137. This method builds the temporal topology from mapsA to
  138. mapsB and vice verse. The temporal topology structure of each map,
  139. defined in class temporal_map_relations,
  140. will be reseted and rebuild for mapsA and mapsB.
  141. After building the temporal topology the modified
  142. map objects of mapsA can be accessed
  143. in the same way as a dictionary using there id.
  144. The implemented iterator assures
  145. the chronological iteration over the mapsA.
  146. @param mapsA: A list of abstract_dataset
  147. objects with initiated temporal extent
  148. @param mapsB: An optional list of abstract_dataset
  149. objects with initiated temporal extent
  150. """
  151. identical = False
  152. if mapsA == mapsB:
  153. identical = True
  154. if mapsB == None:
  155. mapsB = mapsA
  156. idetnical = True
  157. for map_ in mapsA:
  158. map_.reset_topology()
  159. if not identical:
  160. for map_ in mapsB:
  161. map_.reset_topology()
  162. tree = self. _build_1d_rtree(mapsA)
  163. for j in xrange(len(mapsB)):
  164. list_ = gis.ilist()
  165. rect = self._map_to_rect(tree, mapsB[j])
  166. num = vector.RTreeSearch2(tree, rect, byref(list_))
  167. vector.RTreeFreeRect(rect)
  168. for k in xrange(list_.n_values):
  169. i = list_.value[k] - 1
  170. # Get the temporal relationship
  171. relation = mapsB[j].temporal_relation(mapsA[i])
  172. if relation == "equal":
  173. if mapsB[j] != mapsA[i]:
  174. if not mapsB[j].get_equal() or \
  175. (mapsB[j].get_equal() and \
  176. mapsA[i] not in mapsB[j].get_equal()):
  177. mapsB[j].append_equal(mapsA[i])
  178. if not mapsA[i].get_equal() or \
  179. (mapsA[i].get_equal() and \
  180. mapsB[j] not in mapsA[i].get_equal()):
  181. mapsA[i].append_equal(mapsB[j])
  182. elif relation == "follows":
  183. if not mapsB[j].get_follows() or \
  184. (mapsB[j].get_follows() and \
  185. mapsA[i] not in mapsB[j].get_follows()):
  186. mapsB[j].append_follows(mapsA[i])
  187. if not mapsA[i].get_precedes() or \
  188. (mapsA[i].get_precedes() and
  189. mapsB[j] not in mapsA[i].get_precedes()):
  190. mapsA[i].append_precedes(mapsB[j])
  191. elif relation == "precedes":
  192. if not mapsB[j].get_precedes() or \
  193. (mapsB[j].get_precedes() and \
  194. mapsA[i] not in mapsB[j].get_precedes()):
  195. mapsB[j].append_precedes(mapsA[i])
  196. if not mapsA[i].get_follows() or \
  197. (mapsA[i].get_follows() and \
  198. mapsB[j] not in mapsA[i].get_follows()):
  199. mapsA[i].append_follows(mapsB[j])
  200. elif relation == "during" or relation == "starts" or \
  201. relation == "finishes":
  202. if not mapsB[j].get_during() or \
  203. (mapsB[j].get_during() and \
  204. mapsA[i] not in mapsB[j].get_during()):
  205. mapsB[j].append_during(mapsA[i])
  206. if not mapsA[i].get_contains() or \
  207. (mapsA[i].get_contains() and \
  208. mapsB[j] not in mapsA[i].get_contains()):
  209. mapsA[i].append_contains(mapsB[j])
  210. if relation == "starts":
  211. if not mapsB[j].get_starts() or \
  212. (mapsB[j].get_starts() and \
  213. mapsA[i] not in mapsB[j].get_starts()):
  214. mapsB[j].append_starts(mapsA[i])
  215. if not mapsA[i].get_started() or \
  216. (mapsA[i].get_started() and \
  217. mapsB[j] not in mapsA[i].get_started()):
  218. mapsA[i].append_started(mapsB[j])
  219. if relation == "finishes":
  220. if not mapsB[j].get_finishes() or \
  221. (mapsB[j].get_finishes() and \
  222. mapsA[i] not in mapsB[j].get_finishes()):
  223. mapsB[j].append_finishes(mapsA[i])
  224. if not mapsA[i].get_finished() or \
  225. (mapsA[i].get_finished() and \
  226. mapsB[j] not in mapsA[i].get_finished()):
  227. mapsA[i].append_finished(mapsB[j])
  228. elif relation == "contains" or relation == "started" or \
  229. relation == "finished":
  230. if not mapsB[j].get_contains() or \
  231. (mapsB[j].get_contains() and \
  232. mapsA[i] not in mapsB[j].get_contains()):
  233. mapsB[j].append_contains(mapsA[i])
  234. if not mapsA[i].get_during() or \
  235. (mapsA[i].get_during() and \
  236. mapsB[j] not in mapsA[i].get_during()):
  237. mapsA[i].append_during(mapsB[j])
  238. if relation == "started":
  239. if not mapsB[j].get_started() or \
  240. (mapsB[j].get_started() and \
  241. mapsA[i] not in mapsB[j].get_started()):
  242. mapsB[j].append_started(mapsA[i])
  243. if not mapsA[i].get_starts() or \
  244. (mapsA[i].get_starts() and \
  245. mapsB[j] not in mapsA[i].get_starts()):
  246. mapsA[i].append_starts(mapsB[j])
  247. if relation == "finished":
  248. if not mapsB[j].get_finished() or \
  249. (mapsB[j].get_finished() and \
  250. mapsA[i] not in mapsB[j].get_finished()):
  251. mapsB[j].append_finished(mapsA[i])
  252. if not mapsA[i].get_finishes() or \
  253. (mapsA[i].get_finishes() and \
  254. mapsB[j] not in mapsA[i].get_finishes()):
  255. mapsA[i].append_finishes(mapsB[j])
  256. elif relation == "overlaps":
  257. if not mapsB[j].get_overlaps() or \
  258. (mapsB[j].get_overlaps() and \
  259. mapsA[i] not in mapsB[j].get_overlaps()):
  260. mapsB[j].append_overlaps(mapsA[i])
  261. if not mapsA[i].get_overlapped() or \
  262. (mapsA[i].get_overlapped() and \
  263. mapsB[j] not in mapsA[i].get_overlapped()):
  264. mapsA[i].append_overlapped(mapsB[j])
  265. elif relation == "overlapped":
  266. if not mapsB[j].get_overlapped() or \
  267. (mapsB[j].get_overlapped() and \
  268. mapsA[i] not in mapsB[j].get_overlapped()):
  269. mapsB[j].append_overlapped(mapsA[i])
  270. if not mapsA[i].get_overlaps() or \
  271. (mapsA[i].get_overlaps() and \
  272. mapsB[j] not in mapsA[i].get_overlaps()):
  273. mapsA[i].append_overlaps(mapsB[j])
  274. self._build_internal_iteratable(mapsA)
  275. if not identical and mapsB != None:
  276. self._build_iteratable(mapsB)
  277. vector.RTreeDestroyTree(tree)
  278. def __iter__(self):
  279. start_ = self._first
  280. while start_ is not None:
  281. yield start_
  282. start_ = start_.next()
  283. def __getitem__(self, index):
  284. return self._store[index.get_id()]
  285. def __len__(self):
  286. return len(self._store)
  287. def __contains__(self, _map):
  288. return _map in self._store.values()
  289. ###############################################################################
  290. def print_temporal_topology_relationships(maps1, maps2=None, dbif=None):
  291. """!Print the temporal relationships of the
  292. map lists maps1 and maps2 to stdout.
  293. @param maps1: A list of abstract_dataset
  294. objects with initiated temporal extent
  295. @param maps2: An optional list of abstract_dataset
  296. objects with initiated temporal extent
  297. @param dbif: The database interface to be used
  298. """
  299. tb = TemporalTopologyBuilder()
  300. tb.build(maps1, maps2)
  301. dbif, connected = init_dbif(dbif)
  302. for _map in tb:
  303. _map.select(dbif)
  304. _map.print_info()
  305. if connected:
  306. dbif.close()
  307. return
  308. ###############################################################################
  309. def count_temporal_topology_relationships(maps1, maps2=None, dbif=None):
  310. """!Count the temporal relations of a single list of maps or between two lists of maps
  311. @param maps1: A list of abstract_dataset
  312. objects with initiated temporal extent
  313. @param maps2: A list of abstract_dataset
  314. objects with initiated temporal extent
  315. @param dbif: The database interface to be used
  316. @return A dictionary with counted temporal relationships
  317. """
  318. tb = TemporalTopologyBuilder()
  319. tb.build(maps1, maps2)
  320. dbif, connected = init_dbif(dbif)
  321. relations = None
  322. for _map in tb:
  323. if relations != None:
  324. r = _map.get_number_of_relations()
  325. for k in r.keys():
  326. relations[k] += r[k]
  327. else:
  328. relations = _map.get_number_of_relations()
  329. if connected:
  330. dbif.close()
  331. return relations
  332. ###############################################################################
  333. def create_temporal_relation_sql_where_statement(
  334. start, end, use_start=True, use_during=False,
  335. use_overlap=False, use_contain=False, use_equal=False,
  336. use_follows=False, use_precedes=False):
  337. """!Create a SQL WHERE statement for temporal relation selection of maps in space time datasets
  338. @param start: The start time
  339. @param end: The end time
  340. @param use_start: Select maps of which the start time is located in the selection granule
  341. @verbatim
  342. map : s
  343. granule: s-----------------e
  344. map : s--------------------e
  345. granule: s-----------------e
  346. map : s--------e
  347. granule: s-----------------e
  348. @endverbatim
  349. @param use_during: during: Select maps which are temporal during the selection granule
  350. @verbatim
  351. map : s-----------e
  352. granule: s-----------------e
  353. @endverbatim
  354. @param use_overlap: Select maps which temporal overlap the selection granule
  355. @verbatim
  356. map : s-----------e
  357. granule: s-----------------e
  358. map : s-----------e
  359. granule: s----------e
  360. @endverbatim
  361. @param use_contain: Select maps which temporally contain the selection granule
  362. @verbatim
  363. map : s-----------------e
  364. granule: s-----------e
  365. @endverbatim
  366. @param use_equal: Select maps which temporally equal to the selection granule
  367. @verbatim
  368. map : s-----------e
  369. granule: s-----------e
  370. @endverbatim
  371. @param use_follows: Select maps which temporally follow the selection granule
  372. @verbatim
  373. map : s-----------e
  374. granule: s-----------e
  375. @endverbatim
  376. @param use_precedes: Select maps which temporally precedes the selection granule
  377. @verbatim
  378. map : s-----------e
  379. granule: s-----------e
  380. @endverbatim
  381. Usage:
  382. @code
  383. >>> # Relative time
  384. >>> start = 1
  385. >>> end = 2
  386. >>> create_temporal_relation_sql_where_statement(start, end,
  387. ... use_start=False)
  388. >>> create_temporal_relation_sql_where_statement(start, end)
  389. '((start_time >= 1 and start_time < 2) )'
  390. >>> create_temporal_relation_sql_where_statement(start, end,
  391. ... use_start=True)
  392. '((start_time >= 1 and start_time < 2) )'
  393. >>> create_temporal_relation_sql_where_statement(start, end,
  394. ... use_start=False, use_during=True)
  395. '(((start_time > 1 and end_time < 2) OR (start_time >= 1 and end_time < 2) OR (start_time > 1 and end_time <= 2)))'
  396. >>> create_temporal_relation_sql_where_statement(start, end,
  397. ... use_start=False, use_overlap=True)
  398. '(((start_time < 1 and end_time > 1 and end_time < 2) OR (start_time < 2 and start_time > 1 and end_time > 2)))'
  399. >>> create_temporal_relation_sql_where_statement(start, end,
  400. ... use_start=False, use_contain=True)
  401. '(((start_time < 1 and end_time > 2) OR (start_time <= 1 and end_time > 2) OR (start_time < 1 and end_time >= 2)))'
  402. >>> create_temporal_relation_sql_where_statement(start, end,
  403. ... use_start=False, use_equal=True)
  404. '((start_time = 1 and end_time = 2))'
  405. >>> create_temporal_relation_sql_where_statement(start, end,
  406. ... use_start=False, use_follows=True)
  407. '((start_time = 2))'
  408. >>> create_temporal_relation_sql_where_statement(start, end,
  409. ... use_start=False, use_precedes=True)
  410. '((end_time = 1))'
  411. >>> create_temporal_relation_sql_where_statement(start, end,
  412. ... use_start=True, use_during=True, use_overlap=True, use_contain=True,
  413. ... use_equal=True, use_follows=True, use_precedes=True)
  414. '((start_time >= 1 and start_time < 2) OR ((start_time > 1 and end_time < 2) OR (start_time >= 1 and end_time < 2) OR (start_time > 1 and end_time <= 2)) OR ((start_time < 1 and end_time > 1 and end_time < 2) OR (start_time < 2 and start_time > 1 and end_time > 2)) OR ((start_time < 1 and end_time > 2) OR (start_time <= 1 and end_time > 2) OR (start_time < 1 and end_time >= 2)) OR (start_time = 1 and end_time = 2) OR (start_time = 2) OR (end_time = 1))'
  415. >>> # Absolute time
  416. >>> start = datetime(2001, 1, 1, 12, 30)
  417. >>> end = datetime(2001, 3, 31, 14, 30)
  418. >>> create_temporal_relation_sql_where_statement(start, end,
  419. ... use_start=False)
  420. >>> create_temporal_relation_sql_where_statement(start, end)
  421. "((start_time >= '2001-01-01 12:30:00' and start_time < '2001-03-31 14:30:00') )"
  422. >>> create_temporal_relation_sql_where_statement(start, end,
  423. ... use_start=True)
  424. "((start_time >= '2001-01-01 12:30:00' and start_time < '2001-03-31 14:30:00') )"
  425. >>> create_temporal_relation_sql_where_statement(start, end,
  426. ... use_start=False, use_during=True)
  427. "(((start_time > '2001-01-01 12:30:00' and end_time < '2001-03-31 14:30:00') OR (start_time >= '2001-01-01 12:30:00' and end_time < '2001-03-31 14:30:00') OR (start_time > '2001-01-01 12:30:00' and end_time <= '2001-03-31 14:30:00')))"
  428. >>> create_temporal_relation_sql_where_statement(start, end,
  429. ... use_start=False, use_overlap=True)
  430. "(((start_time < '2001-01-01 12:30:00' and end_time > '2001-01-01 12:30:00' and end_time < '2001-03-31 14:30:00') OR (start_time < '2001-03-31 14:30:00' and start_time > '2001-01-01 12:30:00' and end_time > '2001-03-31 14:30:00')))"
  431. >>> create_temporal_relation_sql_where_statement(start, end,
  432. ... use_start=False, use_contain=True)
  433. "(((start_time < '2001-01-01 12:30:00' and end_time > '2001-03-31 14:30:00') OR (start_time <= '2001-01-01 12:30:00' and end_time > '2001-03-31 14:30:00') OR (start_time < '2001-01-01 12:30:00' and end_time >= '2001-03-31 14:30:00')))"
  434. >>> create_temporal_relation_sql_where_statement(start, end,
  435. ... use_start=False, use_equal=True)
  436. "((start_time = '2001-01-01 12:30:00' and end_time = '2001-03-31 14:30:00'))"
  437. >>> create_temporal_relation_sql_where_statement(start, end,
  438. ... use_start=False, use_follows=True)
  439. "((start_time = '2001-03-31 14:30:00'))"
  440. >>> create_temporal_relation_sql_where_statement(start, end,
  441. ... use_start=False, use_precedes=True)
  442. "((end_time = '2001-01-01 12:30:00'))"
  443. >>> create_temporal_relation_sql_where_statement(start, end,
  444. ... use_start=True, use_during=True, use_overlap=True, use_contain=True,
  445. ... use_equal=True, use_follows=True, use_precedes=True)
  446. "((start_time >= '2001-01-01 12:30:00' and start_time < '2001-03-31 14:30:00') OR ((start_time > '2001-01-01 12:30:00' and end_time < '2001-03-31 14:30:00') OR (start_time >= '2001-01-01 12:30:00' and end_time < '2001-03-31 14:30:00') OR (start_time > '2001-01-01 12:30:00' and end_time <= '2001-03-31 14:30:00')) OR ((start_time < '2001-01-01 12:30:00' and end_time > '2001-01-01 12:30:00' and end_time < '2001-03-31 14:30:00') OR (start_time < '2001-03-31 14:30:00' and start_time > '2001-01-01 12:30:00' and end_time > '2001-03-31 14:30:00')) OR ((start_time < '2001-01-01 12:30:00' and end_time > '2001-03-31 14:30:00') OR (start_time <= '2001-01-01 12:30:00' and end_time > '2001-03-31 14:30:00') OR (start_time < '2001-01-01 12:30:00' and end_time >= '2001-03-31 14:30:00')) OR (start_time = '2001-01-01 12:30:00' and end_time = '2001-03-31 14:30:00') OR (start_time = '2001-03-31 14:30:00') OR (end_time = '2001-01-01 12:30:00'))"
  447. @endcode
  448. """
  449. where = "("
  450. if use_start:
  451. if isinstance(start, datetime):
  452. where += "(start_time >= '%s' and start_time < '%s') " % (start, end)
  453. else:
  454. where += "(start_time >= %i and start_time < %i) " % (start, end)
  455. if use_during:
  456. if use_start:
  457. where += " OR "
  458. if isinstance(start, datetime):
  459. where += "((start_time > '%s' and end_time < '%s') OR " % (start, end)
  460. where += "(start_time >= '%s' and end_time < '%s') OR " % (start, end)
  461. where += "(start_time > '%s' and end_time <= '%s'))" % (start, end)
  462. else:
  463. where += "((start_time > %i and end_time < %i) OR " % (start, end)
  464. where += "(start_time >= %i and end_time < %i) OR " % (start, end)
  465. where += "(start_time > %i and end_time <= %i))" % (start, end)
  466. if use_overlap:
  467. if use_start or use_during:
  468. where += " OR "
  469. if isinstance(start, datetime):
  470. where += "((start_time < '%s' and end_time > '%s' and end_time < '%s') OR " % (start, start, end)
  471. where += "(start_time < '%s' and start_time > '%s' and end_time > '%s'))" % (end, start, end)
  472. else:
  473. where += "((start_time < %i and end_time > %i and end_time < %i) OR " % (start, start, end)
  474. where += "(start_time < %i and start_time > %i and end_time > %i))" % (end, start, end)
  475. if use_contain:
  476. if use_start or use_during or use_overlap:
  477. where += " OR "
  478. if isinstance(start, datetime):
  479. where += "((start_time < '%s' and end_time > '%s') OR " % (start, end)
  480. where += "(start_time <= '%s' and end_time > '%s') OR " % (start, end)
  481. where += "(start_time < '%s' and end_time >= '%s'))" % (start, end)
  482. else:
  483. where += "((start_time < %i and end_time > %i) OR " % (start, end)
  484. where += "(start_time <= %i and end_time > %i) OR " % (start, end)
  485. where += "(start_time < %i and end_time >= %i))" % (start, end)
  486. if use_equal:
  487. if use_start or use_during or use_overlap or use_contain:
  488. where += " OR "
  489. if isinstance(start, datetime):
  490. where += "(start_time = '%s' and end_time = '%s')" % (start, end)
  491. else:
  492. where += "(start_time = %i and end_time = %i)" % (start, end)
  493. if use_follows:
  494. if use_start or use_during or use_overlap or use_contain or use_equal:
  495. where += " OR "
  496. if isinstance(start, datetime):
  497. where += "(start_time = '%s')" % (end)
  498. else:
  499. where += "(start_time = %i)" % (end)
  500. if use_precedes:
  501. if use_start or use_during or use_overlap or use_contain or use_equal \
  502. or use_follows:
  503. where += " OR "
  504. if isinstance(start, datetime):
  505. where += "(end_time = '%s')" % (start)
  506. else:
  507. where += "(end_time = %i)" % (start)
  508. where += ")"
  509. # Catch empty where statement
  510. if where == "()":
  511. where = None
  512. return where