review_utils.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import json
  2. from requests import get, put
  3. from gauth.auth_utils import get_gmb_id, get_auth_header
  4. from gauth.location_utils import get_all_location_ids
  5. from django.db.models import Max
  6. from .models import Review, Reply
  7. from gauth.models import Location
  8. from django.utils import timezone
  9. STAR_REVIEW_NUM = {'STAR_RATING_UNSPECIFIED': 0, 'ONE': 1, 'TWO': 2, 'THREE': 3, 'FOUR': 4, 'FIVE': 5}
  10. USER, GMB_ID = get_gmb_id()
  11. def get_review_list_url(loc_id, next_page_token=''):
  12. # An helper function that make a url that need to consume GMB review api
  13. return 'https://mybusiness.googleapis.com/v4/accounts/' + GMB_ID + '/locations/' + loc_id + '/reviews?pageToken='+next_page_token
  14. def get_reply_url(location_id, review_id):
  15. _, account_id = get_gmb_id()
  16. return f'https://mybusiness.googleapis.com/v4/accounts/{account_id}/locations/{location_id}/reviews/{review_id}/reply'
  17. def reply_review(review, replied_text):
  18. '''
  19. reply a review with a put request.
  20. :param review: review object -> a review which you want to reply.
  21. :param replied_text: string -> The actual reply that you want to post.
  22. :return:
  23. '''
  24. url = get_reply_url(review.location.location_id, review.review_id)
  25. headers = get_auth_header()
  26. payload = json.dumps({'comment': replied_text})
  27. response = put(url, headers=headers, data=payload)
  28. return response
  29. def get_max_date(loc_id):
  30. '''
  31. find the maximum date of any particular location. the latest date
  32. :param loc_id: Integer -> Globally unique ID for google my business locations.
  33. :return: DateTime Obj -> The latest reviews date of that location.
  34. '''
  35. # Function that takes a location id and return the latest updated review.
  36. largest = Review.objects.filter(location_id=loc_id).aggregate(Max('create_time'))['create_time__max']
  37. max_date = largest if largest else '1970-01-11 17:41:17.532740'
  38. return max_date
  39. def filter_unrecorded_review_by_date(reviews, max_date):
  40. # A function that return only those reviews whose has larger value than
  41. # the max create_time value in review database.
  42. filtered_reviews = []
  43. for rev in reviews:
  44. if rev['createTime'] >= str(max_date):
  45. filtered_reviews.append(rev)
  46. return filtered_reviews
  47. def insert_review_into_database(unrecorded_reviews, loc):
  48. '''
  49. Insert reviews to database.
  50. :param unrecorded_reviews: all reviews for location.
  51. :param loc: location that unrecorded_reviews belongs to.
  52. :return: It insert all reviews if it is not exits in database and return nothing.
  53. '''
  54. for review in unrecorded_reviews:
  55. review_id = review.get('reviewId')
  56. # Check the review already in database then We don't need to store again.
  57. rev = Review.objects.filter(pk=review_id).first()
  58. if rev:
  59. continue
  60. comment = review.get('comment')
  61. create_time = review.get('createTime')
  62. update_time = review.get('updateTime')
  63. star_rating = STAR_REVIEW_NUM[review.get('starRating')]
  64. reviewer = review.get('reviewer')
  65. reviewer_name = reviewer.get('displayName')
  66. reviewer_photo = reviewer.get('profilePhotoUrl')
  67. review_reply = review.get('reviewReply')
  68. # Check if it is already replied.
  69. if review_reply:
  70. replied_text = review_reply.get('comment')
  71. create_time = review_reply.get('updateTime')
  72. reply = Reply.objects.create(replied_text=replied_text, create_time=create_time)
  73. else:
  74. reply = None
  75. review = Review(
  76. review_id=review_id,
  77. comment=comment,
  78. create_time=create_time,
  79. update_time=update_time,
  80. star_rating=star_rating,
  81. reviewer_name=reviewer_name,
  82. reviewer_photo=reviewer_photo,
  83. location=loc,
  84. reply=reply
  85. )
  86. review.save()
  87. def sync_all_review(loc_id):
  88. '''
  89. Sync a location if any bad thing occur i.e. any network break.
  90. :param: loc_id -> Location id of a particular location
  91. :return: None -> It just update all reviews of this location and return nothing.
  92. '''
  93. loc = Location.objects.get(pk=loc_id)
  94. next_page_token = ''
  95. headers = get_auth_header()
  96. while True:
  97. url = get_review_list_url(loc_id, next_page_token)
  98. res = get(url, headers=headers)
  99. if res.status_code == 401:
  100. headers = get_auth_header()
  101. continue
  102. data = res.json()
  103. reviews = data['reviews']
  104. if len(reviews) != 0:
  105. insert_review_into_database(reviews, loc)
  106. next_page_token = data.get('nextPageToken')
  107. if next_page_token is None:
  108. break
  109. average_rating = data.get('averageRating')
  110. total_reviews = data.get('totalReviewCount')
  111. total_reviews_db = Review.objects.filter(location_id=loc_id).count()
  112. update_location_data(loc, average_rating, total_reviews, total_reviews_db)
  113. def update_location_data(loc, average_rating, total_reviews, total_reviews_db):
  114. loc.average_rating = average_rating
  115. loc.total_review = total_reviews
  116. loc.total_review_DB = total_reviews_db
  117. loc.save()
  118. def fetch_all_review(loc_id):
  119. loc = Location.objects.get(pk=loc_id)
  120. max_date = get_max_date(loc_id)
  121. headers = get_auth_header()
  122. url = get_review_list_url(loc_id)
  123. res = get(url, headers=headers)
  124. if res.status_code == 401:
  125. return
  126. data = res.json()
  127. reviews = data['reviews']
  128. average_rating = data.get('averageRating')
  129. total_reviews = data.get('totalReviewCount')
  130. unrecorded_reviews = filter_unrecorded_review_by_date(reviews, max_date)
  131. if len(unrecorded_reviews) != 0:
  132. insert_review_into_database(unrecorded_reviews, loc)
  133. total_reviews_db = Review.objects.filter(location_id=loc_id).count()
  134. update_location_data(loc, average_rating, total_reviews, total_reviews_db)
  135. def populate_reviews():
  136. start = timezone.now()
  137. locations = get_all_location_ids()
  138. for loc_id in locations:
  139. fetch_all_review(loc_id)
  140. end = timezone.now()
  141. elapsed = end - start
  142. print(f'Elapsed time: {elapsed.seconds//60} minutes and {elapsed.seconds % 60} secs.')
  143. def get_bad_reviews(location_id, **kwargs):
  144. '''
  145. a utility function that return all reviews has less or equal three.
  146. :param location_id: str -> id of the location where reviews are belongs to
  147. :param kwargs: i.e (days=__, hours=__, minutes=__)
  148. :return: QuerySet -> all low rating reviews in last * days/hours/minutes
  149. Example --------------
  150. >>> get_bad_reviews(location_id='123456', days=5, hours=2, minute=1)
  151. >>> get_bad_reviews(location_id='123456', days=5)
  152. >>> get_bad_reviews(location_id='123456', hours=5)
  153. '''
  154. now = timezone.now()
  155. date = now - timezone.timedelta(**kwargs)
  156. reviews = Review.objects.filter(location_id=location_id, update_time__gte=date, star_rating__lte=3)
  157. return reviews