db.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. from pymongo import MongoClient, errors, DESCENDING, ASCENDING
  2. from bson.objectid import ObjectId
  3. from datetime import timedelta
  4. import datetime
  5. import re
  6. import news
  7. import pytz
  8. import time
  9. class db():
  10. DB = "alexis"
  11. def __init__(self):
  12. self.client = MongoClient('localhost', 27017)
  13. self.c_sources = self.client[self.DB]["sources"]
  14. self.c_articles = self.client[self.DB]["articles"]
  15. self.c_kw = self.client[self.DB]["keywords"]
  16. def change_timezone(self, el):
  17. #gmt = pytz.timezone("America/Buenos_Aires") # FIXME
  18. local = pytz.timezone("US/Central")
  19. est = pytz.timezone('US/Eastern')
  20. fmt = '%Y-%m-%d %H:%M:%S %Z%z'
  21. el["scrap_date"] = local.localize(el["scrap_date"])
  22. el["scrap_date"] = el["scrap_date"].astimezone(est)
  23. el["scrap_date"] = el["scrap_date"].strftime(fmt)
  24. return el
  25. def jsonable(self, el):
  26. if el is None or "_id" not in el:
  27. return el
  28. el["_id"] = str(el["_id"])
  29. return el
  30. def filter_articles(self, filter, limit=80):
  31. start = time.time()
  32. db_filter = {}
  33. db_filter["matches"] = True
  34. db_filter["deleted"] = False
  35. db_filter["scrap_date"] = {}
  36. if "maxdate" in filter and len(filter["maxdate"]) > 7:
  37. db_filter["scrap_date"]["$lt"] = datetime.datetime.strptime(
  38. filter["maxdate"], "%Y-%m-%d")
  39. if "mindate" in filter and len(filter["mindate"]) > 7:
  40. db_filter["scrap_date"]["$gt"] = datetime.datetime.strptime(
  41. filter["mindate"], "%Y-%m-%d")
  42. if db_filter["scrap_date"] == {}:
  43. del db_filter["scrap_date"]
  44. if "title" in filter and filter["title"] != "":
  45. filter["title"] = filter["title"].replace(
  46. "(", "\(").replace(")", "\)")
  47. db_filter["title"] = {"$regex": ".*%s.*" %
  48. filter["title"], "$options": 'i'}
  49. if "site" in filter and filter["site"] != "":
  50. filter["site"] = filter["site"].replace(
  51. "(", "\(").replace(")", "\)")
  52. db_filter["source_name"] = {
  53. "$regex": ".*%s.*" % filter["site"], "$options": 'i'}
  54. # db_filter["$or"]=[
  55. # {"source_name": {"$regex":".*%s.*"%filter["site"], "$options": 'i'}},
  56. # {"url": {"$regex":".*%s.*"%filter["site"], "$options": 'i' }}
  57. # ]
  58. if "minweight" not in filter or not filter["minweight"].isdigit():
  59. filter["minweight"] = 1
  60. if "category" not in filter or filter["category"] == "-1":
  61. filter["category"] = -1
  62. if int(filter["category"]) >= 0:
  63. db_filter["category"] = int(filter["category"])
  64. # pass
  65. db_filter["weight"] = {"$gt": int(filter["minweight"])}
  66. if "keyword" in filter and len(filter["keyword"]) > 0:
  67. db_filter["kw"] = {"$all": filter["keyword"]}
  68. # db_filter["kw.0"]={"$exists":False}
  69. pageNumber = 0
  70. if "page" in filter:
  71. pageNumber = filter["page"]
  72. # print(db_filter)
  73. print(db_filter)
  74. fields = {"html": 0, "text": 0, "authors":0,"lowerurl":0}
  75. fields = {"kw":1,"scrap_date":1, "source_name":1,"summary":1,"title":1,"url":1,"weight":1}
  76. res = self.c_articles.find(db_filter, fields)
  77. if res is not None:
  78. toSkip = (((pageNumber - 1) * limit) if pageNumber > 0 else 0)
  79. #q = res.skip(toSkip).sort("scrap_date", DESCENDING).limit(limit)
  80. q = res.skip(toSkip).sort("scrap_date", DESCENDING).limit(limit*3) #+2 pages
  81. start=time.time()
  82. else:
  83. q = []
  84. count = 0
  85. ret_arts = [self.jsonable(self.change_timezone(p)) for p in q]
  86. count = len(ret_arts)+toSkip
  87. ret_arts = ret_arts[:limit]
  88. #count = len(ret_arts)
  89. print("pre-ret", time.time()-start)
  90. return {'articles': ret_arts,
  91. 'count': count}
  92. def article(self, id):
  93. ret = self.c_articles.find_one({"_id": ObjectId(id)})
  94. return self.jsonable(ret)
  95. def delete_article(self, artid):
  96. return self.c_articles.update({"_id": ObjectId(artid)}, {"$set": {"deleted": True}})
  97. def undelete_article(self, artid):
  98. return self.c_articles.update({"_id": ObjectId(artid)}, {"$set": {"deleted": False}})
  99. def sources(self, id=None):
  100. if id is None:
  101. return [self.jsonable(p) for p in self.c_sources.find()]
  102. ret = self.c_sources.find_one({"_id": ObjectId(id)})
  103. return self.jsonable(ret)
  104. def rss_sources(self):
  105. return [p for p in self.c_sources.find({"rss": 1})]
  106. def insert_article(self, o):
  107. try:
  108. self.c_articles.insert_one(o)
  109. return True
  110. except errors.DuplicateKeyError:
  111. print("Dup")
  112. return False
  113. def count_error(self):
  114. return self.c_articles.count({'error': True})
  115. def purge_error(self):
  116. self.c_articles.remove({'error': True})
  117. def article_exists(self, url, src):
  118. #slow as balls
  119. #ret = self.c_articles.find({'source': src, 'lowerurl': url.lower()},{"_id":1}).limit(1) #faster than findone
  120. #articles may have different src and same url.
  121. ret = self.c_articles.find({'lowerurl': url.lower()},{"_id":1}).limit(1) #faster than findone
  122. c = len([ 1 for r in ret ] )
  123. #findOne returns the elem. find returns the cursor (pointer)
  124. return c>0
  125. def lower_arr(self, arr):
  126. return [a.lower() for a in arr]
  127. def to_regex(self, arr): # "ACH" | "PAYMENT" | "EBAY"
  128. return [re.compile('\\b' + w.lower() + '\\b') for w in arr]
  129. def get_keywords_list(self):
  130. kw = self.c_kw
  131. return [k["keyword"] for k in kw.find({})]
  132. def get_keywords(self, category):
  133. kw = self.c_kw
  134. ret = [{"weight": k["weight"],
  135. "kw": k["keyword"],
  136. "pattern": self.to_regex(k["keyword"].split("|"))} for k in kw.find({})]
  137. # FIXME, categories
  138. return ret
  139. def add_keyword(self, kw, weight):
  140. kwdb = self.c_kw
  141. try:
  142. data = {'keyword': kw, 'weight': weight}
  143. kwdb.insert_one(data)
  144. return True
  145. except errors.DuplicateKeyError:
  146. print("Dup KW")
  147. return False
  148. def update_keyword(self, kw):
  149. if 'oldkw' not in kw or 'weight' not in kw or 'newkw' not in kw:
  150. return False
  151. if not isinstance(kw['weight'], int):
  152. return False
  153. kwdb = self.c_kw
  154. try:
  155. kwdb.update({'keyword': kw['oldkw']},
  156. {'$set':
  157. {'weight': kw['weight'], 'keyword': kw['newkw']}})
  158. return True
  159. except Exception:
  160. print("Dup KW")
  161. return False
  162. def delete_keyword(self, kw):
  163. kwdb = self.c_kw
  164. try:
  165. kwdb.remove({'keyword': kw})
  166. return True
  167. except Exception:
  168. return False
  169. def add_source(self, url, rss, selector, name, category):
  170. self.c_sources.insert({"link": url,
  171. "rss": rss,
  172. "selector": selector,
  173. "name": name,
  174. "category": category})
  175. def reparse_articles(self, articles, kw):
  176. for a in articles:
  177. # print(a)
  178. art = self.c_articles.find_one({"url": a["url"]})
  179. res = news.match_keywords(kw, art["text"])
  180. #(matches,w_sum,matching)
  181. # if(res["matches"]):
  182. print(res)
  183. self.c_articles.update({"_id": ObjectId(a["_id"])}, {"$set": res})
  184. #matches, kw, weight
  185. def get_sources_with_error(self):
  186. r = self.c_articles.aggregate([{'$match': {'error': True}},
  187. {'$group': {'_id': '$source_name', 'count': {'$sum': 1}}}])
  188. return r
  189. def get_sources_with_count(self):
  190. # TODO filter with scrap_date, this year mb?
  191. r = self.c_articles.aggregate([{'$match': {'matches': True}},
  192. {'$group':
  193. {'_id': '$source_name',
  194. 'count': {'$sum': 1},
  195. 'weight': {'$avg': '$weight'}
  196. }
  197. }])
  198. # t = self.c_articles.aggregate([{'$group': {'_id': '$source_name', 'count': {'$sum': 1}}}])
  199. return r
  200. def get_keywords_with_count(self):
  201. this_year = datetime.datetime.now() - timedelta(days=180)
  202. r = self.c_articles.aggregate([{'$match': {
  203. '$and': [{'matches': True}, {'scrap_date': {'$gt': this_year}}]
  204. }},
  205. {'$unwind': '$kw'},
  206. {'$group':
  207. {'_id': '$kw', 'sum': {'$sum': 1}}
  208. }
  209. ])
  210. d1 = {}
  211. for res in r:
  212. d1[res['_id']] = res['sum']
  213. return d1