Advertisement
Guest User

Untitled

a guest
Jul 18th, 2019
58
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.15 KB | None | 0 0
  1. import textwrap
  2. import collections
  3.  
  4.  
  5. class BaseQuery:
  6.  
  7. indent = ' '
  8. separators = collections.defaultdict(
  9. lambda: ',',
  10. WHERE=' AND',
  11. )
  12.  
  13. def __init__(self):
  14. self._query = []
  15.  
  16. def _list_for_keyword(self, keyword):
  17. try:
  18. return self._query[self._query.index(keyword) + 1]
  19. except ValueError:
  20. rv = []
  21. self._query.extend((keyword, rv))
  22. return rv
  23.  
  24. def _extend_keyword(self, keyword, things):
  25. self._list_for_keyword(keyword).extend(self._clean_up(t) for t in things)
  26.  
  27. def _clean_up(self, thing):
  28. return textwrap.dedent(thing.rstrip()).strip()
  29.  
  30. def __getattr__(self, name):
  31. keyword = name.replace('_', ' ').strip().upper()
  32.  
  33. def add(*things):
  34. self._extend_keyword(keyword, things)
  35. return self
  36.  
  37. # TODO: Set __doc__ on add.
  38.  
  39. return add
  40.  
  41. def __str__(self):
  42. def make():
  43. pairs = zip(self._query[0::2], self._query[1::2])
  44.  
  45. for keyword, things in pairs:
  46. yield keyword
  47. for i, thing in enumerate(things, 1):
  48. if i < len(things):
  49. thing += self.separators[keyword]
  50. yield textwrap.indent(thing, self.indent)
  51.  
  52. yield ';'
  53.  
  54. return '\n'.join(make())
  55.  
  56.  
  57. class ScrollingWindowMixin:
  58.  
  59. def __init__(self):
  60. super().__init__()
  61. self._order_by_things = None
  62. self._order_by_desc = None
  63.  
  64. def scrolling_window_order_by(self, *things, desc=False):
  65. assert self._order_by_things is None
  66.  
  67. self._order_by_things = things
  68. self._order_by_desc = desc
  69.  
  70. direction = 'DESC' if desc else 'ASC'
  71.  
  72. self.order_by(*(
  73. '{} {}'.format(self._clean_up(thing), direction)
  74. for thing in things
  75. ))
  76. return self
  77.  
  78. def _make_last_label(self, i):
  79. return ":last_{}".format(i)
  80.  
  81. def scrolling_window_limit(self, thing):
  82. assert self._order_by_things is not None
  83.  
  84. self.limit(thing)
  85. self._where(
  86. "(\n{}\n) {} (\n{}\n)".format(
  87. textwrap.indent(
  88. ',\n'.join(self._order_by_things),
  89. self.indent,
  90. ),
  91. '<' if self._order_by_desc else '>',
  92. textwrap.indent(
  93. ',\n'.join(
  94. self._make_last_label(i)
  95. for i in range(len(self._order_by_things))
  96. ),
  97. self.indent,
  98. ),
  99. ),
  100. )
  101. return self
  102.  
  103. def extract_last(self, result):
  104. if not self._order_by_things:
  105. return {}
  106. names = self.select_names
  107. assert len(result) == len(names)
  108. return collections.OrderedDict(
  109. (self._make_last_label(i), result[names.index(thing)])
  110. for i, thing in enumerate(self._order_by_things)
  111. )
  112.  
  113.  
  114. class SelectAliasMixin:
  115.  
  116. def __init__(self):
  117. super().__init__()
  118. self._select_names = []
  119.  
  120. def _super_select(self, *things):
  121. try:
  122. fn = super().select
  123. except AttributeError:
  124. # AttributeError: 'super' object has no attribute 'select'
  125. fn = super().__getattr__('select')
  126. return fn(*things)
  127.  
  128. def select(self, *things):
  129. rv = self._super_select(*things)
  130. self._select_names.extend(self._clean_up(t) for t in things)
  131. return rv
  132.  
  133. def select_alias(self, alias, thing):
  134. alias = self._clean_up(alias)
  135. thing = self._clean_up(thing)
  136. rv = self._super_select("{} AS {}".format(thing, alias))
  137. self._select_names.append(alias)
  138. return rv
  139.  
  140. @property
  141. def select_names(self):
  142. return list(self._select_names)
  143.  
  144.  
  145. class Query(SelectAliasMixin, ScrollingWindowMixin, BaseQuery): pass
  146.  
  147.  
  148. def _make_better_query(
  149. which, feed_url, has_enclosures, chunk_size, last, entry_id, search
  150. ):
  151. query = (
  152. Query()
  153. .select('feeds.url', 'entries.id')
  154. .from_('entries', 'feeds')
  155. .where('feeds.url = entries.feed')
  156. )
  157.  
  158. if which == 'all':
  159. pass
  160. elif which == 'unread':
  161. query.where("(entries.read IS NULL OR entries.read != 1)")
  162. elif which == 'read':
  163. query.where("entries.read = 1")
  164. else:
  165. assert False, "shouldn't get here" # pragma: no cover
  166.  
  167. if feed_url:
  168. query.where("feeds.url = :feed_url")
  169. if entry_id:
  170. query.where("entries.id = :entry_id")
  171.  
  172. if has_enclosures is not None:
  173. query.select_alias("has_enclosures", """
  174. (json_array_length(entries.enclosures) IS NULL
  175. OR json_array_length(entries.enclosures) = 0
  176. )
  177. """)
  178. query.where("AND {}has_enclosures".format('' if has_enclosures else "NOT "))
  179.  
  180. if not search:
  181. query.select_alias("kinda_first_updated", """
  182. coalesce(
  183. CASE
  184. WHEN
  185. coalesce(entries.published, entries.updated) >= :recent_threshold
  186. THEN entries.first_updated
  187. END,
  188. entries.published, entries.updated
  189. )
  190. """)
  191. query.select_alias("kinda_published", "coalesce(entries.published, entries.updated)")
  192. query.scrolling_window_order_by(*"""
  193. kinda_first_updated
  194. kinda_published
  195. feeds.url
  196. entries.id
  197. """.split(), desc=True)
  198.  
  199. else:
  200. # How hard would it be to add this to _make_get_entries_query?
  201. query.select_alias("search_details", "fancy query returning search details as json")
  202. query.select("entries_search.rank")
  203. query.scrolling_window_order_by("entries_search.rank")
  204.  
  205. if chunk_size:
  206. query.scrolling_window_limit(':chunk_size')
  207.  
  208. return query
  209.  
  210.  
  211. args = ('all', 'feed', 0, 4, list(range(5)), 'entry', "search")
  212.  
  213. query = _make_better_query(*args)
  214. print(query)
  215. print()
  216. print(query.select_names)
  217. print(query.extract_last(('feed', 'id', "has_enclosures", "details", "rank")))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement