/reelcam/models/follow.py

https://gitlab.com/cimh/rc-api · Python · 355 lines · 209 code · 43 blank · 103 comment · 18 complexity · 37181d834e2272095079b0a45df93228 MD5 · raw file

  1. # -*- coding: utf-8 -*-
  2. from tornado import gen
  3. from datetime import timedelta
  4. from api_engine import s
  5. from api_engine.enums import FOLLOW_STATUS
  6. from .base import BaseModel, BaseCustomID
  7. from ..mappers.follow import FollowEntryMappingType
  8. class Follow(BaseCustomID, BaseModel):
  9. """
  10. Follow Model.
  11. """
  12. mapper = FollowEntryMappingType
  13. custom_id_attrs = ['_from', '_to']
  14. def _mapped_fields(self):
  15. """
  16. :return:
  17. """
  18. return dict(id=str(),
  19. _from=str(),
  20. _to=str(),
  21. message=str(),
  22. status=FOLLOW_STATUS.not_follow,
  23. modify_ts=str(),
  24. first_request_ts=None,
  25. last_request_ts=None,
  26. _to_is_hidden=None,
  27. _from_is_hidden=None)
  28. def get_data(self):
  29. """
  30. :return:
  31. """
  32. return {
  33. 'from': self._from,
  34. 'message': self.message,
  35. 'to': self._to,
  36. 'status': self.status,
  37. 'ts': self.ts_from_YmdHMS(self.modify_ts)
  38. }
  39. @classmethod
  40. def _validation_schema(cls):
  41. """
  42. :return:
  43. """
  44. return dict(_from=dict(required=True, type='string'),
  45. _to=dict(required=True, type='string'))
  46. def get_req_data(self):
  47. """
  48. :return:
  49. """
  50. return dict(id=self.id,
  51. user_id=self._from,
  52. message=self.message)
  53. @classmethod
  54. @gen.coroutine
  55. def _create(cls, user, followed_user):
  56. """
  57. :param user:
  58. :param followed_user:
  59. :return:
  60. """
  61. assert user.id != followed_user.id
  62. data = dict(_from=user.id,
  63. _to=followed_user.id,
  64. status=FOLLOW_STATUS.not_follow,
  65. modify_ts=cls.now_YmdHMS())
  66. follow = cls(data)
  67. r = yield follow.save()
  68. raise gen.Return(r)
  69. def is_follow(self):
  70. """
  71. :return:
  72. """
  73. return self.status == FOLLOW_STATUS.follow
  74. def is_disfollow(self):
  75. """
  76. :return:
  77. """
  78. return self.status == FOLLOW_STATUS.disfollow
  79. def is_request(self):
  80. """
  81. :return:
  82. """
  83. return self.status == FOLLOW_STATUS.request
  84. def is_not_follow(self):
  85. """
  86. :return:
  87. """
  88. return self.status == FOLLOW_STATUS.not_follow
  89. @gen.coroutine
  90. def set_follow(self):
  91. """
  92. :return:
  93. """
  94. User = s().M.model('User')
  95. if self.status is FOLLOW_STATUS.follow:
  96. return
  97. self.status = FOLLOW_STATUS.follow
  98. self.first_request_ts = None
  99. self.last_request_ts = None
  100. r = yield self.save()
  101. user_from, user_to = yield [User.get_one(_id=self._from), User.get_one(_id=self._to)]
  102. user_from.followed = set(user_from.followed)
  103. user_from.followed.add(user_to.id)
  104. user_from.followed = list(user_from.followed)
  105. user_to.followers = set(user_to.followers)
  106. user_to.followers.add(user_from.id)
  107. user_to.followers = list(user_to.followers)
  108. user_from, user_to = yield [user_from.save(), user_to.save()]
  109. raise gen.Return(r)
  110. @gen.coroutine
  111. def set_disfollow(self):
  112. """
  113. :return:
  114. """
  115. User = s().M.model('User')
  116. self.status = FOLLOW_STATUS.disfollow
  117. self.first_request_ts = None
  118. self.last_request_ts = None
  119. r = yield self.save()
  120. user_from, user_to = yield [User.get_one(_id=self._from), User.get_one(_id=self._to)]
  121. user_from.followed = set(user_from.followed)
  122. user_from.followed.discard(user_to.id)
  123. user_from.followed = list(user_from.followed)
  124. user_to.followers = set(user_to.followers)
  125. user_to.followers.discard(user_from.id)
  126. user_to.followers = list(user_to.followers)
  127. yield [user_from.save(), user_to.save()]
  128. raise gen.Return(r)
  129. @gen.coroutine
  130. def set_not_follow(self):
  131. """
  132. :return:
  133. """
  134. User = s().M.model('User')
  135. self.status = FOLLOW_STATUS.not_follow
  136. self.first_request_ts = None
  137. self.last_request_ts = None
  138. r = yield self.save()
  139. user_from, user_to = yield [User.get_one(_id=self._from), User.get_one(_id=self._to)]
  140. user_from.followed = set(user_from.followed)
  141. user_from.followed.discard(user_to.id)
  142. user_from.followed = list(user_from.followed)
  143. user_to.followers = set(user_to.followers)
  144. user_to.followers.discard(user_from.id)
  145. user_to.followers = list(user_to.followers)
  146. yield [user_from.save(), user_to.save()]
  147. raise gen.Return(r)
  148. @gen.coroutine
  149. def set_request(self):
  150. """
  151. :return:
  152. """
  153. self.status = FOLLOW_STATUS.request
  154. if self.first_request_ts is None:
  155. self.first_request_ts = self.now_YmdHMS()
  156. self.last_request_ts = self.now_YmdHMS()
  157. r = yield self.save()
  158. e_data = self.get_data()
  159. e_data.update(dict(follow_request_id=self.id), scope=self.default_scope)
  160. s().h("follow:follow", e_data, self._from, self._to)
  161. raise gen.Return(r)
  162. @gen.coroutine
  163. def disfollow(self):
  164. """
  165. :return:
  166. """
  167. if not self.is_follow():
  168. raise gen.Return(self)
  169. yield self.set_disfollow()
  170. e_data = self.get_data()
  171. e_data.update(dict(follow_request_id=self.id))
  172. s().e('follow:disfollow', sender=self.ctx.request_user, notify_sender=True,
  173. users=[(yield self.get_user_from())], data=e_data)
  174. raise gen.Return(self)
  175. @gen.coroutine
  176. def undisfollow(self):
  177. """
  178. :return:
  179. """
  180. if not self.is_disfollow():
  181. raise gen.Return(self)
  182. yield self.set_follow()
  183. e_data = self.get_data()
  184. e_data.update(dict(follow_request_id=self.id))
  185. s().e('follow:undisfollow', sender=self.ctx.request_user, notify_sender=True,
  186. users=[(yield self.get_user_from())], data=e_data)
  187. raise gen.Return(self)
  188. @gen.coroutine
  189. def unfollow(self):
  190. """
  191. :return:
  192. """
  193. if not self.is_follow():
  194. raise gen.Return(self)
  195. yield self.set_not_follow()
  196. e_data = self.get_data()
  197. e_data.update(dict(follow_request_id=self.id))
  198. s().e('follow:unfollow', sender=self.ctx.request_user, notify_sender=True,
  199. users=[(yield self.get_user_to())], data=e_data)
  200. raise gen.Return(self)
  201. @gen.coroutine
  202. def accept(self):
  203. """
  204. :return:
  205. """
  206. if not self.is_request():
  207. raise gen.Return(self)
  208. yield self.set_follow()
  209. e_data = self.get_data()
  210. e_data.update(dict(follow_request_id=self.id))
  211. s().e('follow:accept', sender=self.ctx.request_user, notify_sender=True,
  212. users=[(yield self.get_user_from())], data=e_data)
  213. raise gen.Return(self)
  214. @gen.coroutine
  215. def discard(self):
  216. """
  217. :return:
  218. """
  219. if not self.is_request():
  220. raise gen.Return(self)
  221. self.status = FOLLOW_STATUS.not_follow
  222. yield self.save()
  223. e_data = self.get_data()
  224. e_data.update(dict(follow_request_id=self.id))
  225. s().e('follow:discard', sender=self.ctx.request_user, notify_sender=True,
  226. users=[(yield self.get_user_from())], data=e_data)
  227. raise gen.Return(self)
  228. @gen.coroutine
  229. def follow(self, follow, user_from, user_to):
  230. """
  231. :param follow:
  232. :param user_from:
  233. :param user_to:
  234. :return:
  235. """
  236. assert user_from.id != user_to.id
  237. if follow.is_follow():
  238. raise gen.Return(follow)
  239. if follow.is_disfollow() or user_to.follow_approve and follow.is_not_follow():
  240. yield follow.set_request()
  241. elif follow.is_not_follow():
  242. yield follow.set_follow()
  243. elif self.is_request():
  244. yield self.set_request()
  245. e_data = self.get_data()
  246. e_data.update(dict(follow_request_id=self.id))
  247. s().e('follow:follow', sender=self.ctx.request_user, notify_sender=True,
  248. users=[(yield self.get_user_to())], data=e_data)
  249. raise gen.Return(follow)
  250. def can_retry_request(self):
  251. """
  252. :return:
  253. """
  254. if not self.is_request():
  255. return False
  256. try:
  257. first_request_ts = self.dt_from_YmdHMS(self.first_request_ts)
  258. last_request_ts = self.dt_from_YmdHMS(self.last_request_ts)
  259. except (TypeError, ValueError):
  260. return True
  261. # @TODO: check this
  262. return (self.now_dt() - first_request_ts) < timedelta(weeks=1) and \
  263. (self.now_dt() - last_request_ts) > timedelta(days=1) or \
  264. (self.now_dt() - last_request_ts) > timedelta(weeks=1)
  265. @gen.coroutine
  266. def get_user_from(self):
  267. """
  268. :return:
  269. """
  270. User = s().M.model('User', self.ctx)
  271. user = yield User.get_one(_id=self._from)
  272. raise gen.Return(user)
  273. @gen.coroutine
  274. def get_user_to(self):
  275. """
  276. :return:
  277. """
  278. User = s().M.model('User', self.ctx)
  279. user = yield User.get_one(_id=self._to)
  280. raise gen.Return(user)
  281. @classmethod
  282. @gen.coroutine
  283. def delete_user(cls, user_id):
  284. """
  285. :param user_id:
  286. :return:
  287. """
  288. r = yield cls.mapper._delete_user(user_id)
  289. raise gen.Return(r)