db.py 6.9 KB

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