ops.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. # coding: utf-8
  2. from __future__ import print_function
  3. from __future__ import absolute_import
  4. from __future__ import division
  5. from __future__ import unicode_literals
  6. from django.utils.timezone import now, utc
  7. from backend import const
  8. from process.email import notify_new_dd
  9. from . import models as bmodels
  10. import datetime
  11. import logging
  12. import json
  13. log = logging.getLogger(__name__)
  14. class JSONSerializer(json.JSONEncoder):
  15. def default(self, o):
  16. if isinstance(o, datetime.datetime):
  17. if o.tzinfo: o = o.astimezone(utc)
  18. return o.strftime("%Y-%m-%d %H:%M:%S")
  19. elif isinstance(o, datetime.date):
  20. return o.strftime("%Y-%m-%d")
  21. elif isinstance(o, bmodels.Person):
  22. return o.lookup_key
  23. else:
  24. return super(JSONSerializer, self).default(o)
  25. def ensure_datetime(val):
  26. if isinstance(val, datetime.datetime): return val
  27. val = datetime.datetime.strptime(val, "%Y-%m-%d %H:%M:%S")
  28. val = val.replace(tzinfo=utc)
  29. return val
  30. def ensure_date(val):
  31. if isinstance(val, datetime.date): return val
  32. val = datetime.datetime.strptime(val, "%Y-%m-%d")
  33. return val.date()
  34. def ensure_person(val):
  35. if isinstance(val, bmodels.Person):
  36. return val
  37. else:
  38. return bmodels.Person.lookup(val)
  39. def ensure_old_process(val):
  40. if isinstance(val, bmodels.Process):
  41. return val
  42. else:
  43. return bmodels.Process.objects.get(pk=val)
  44. class Operation(object):
  45. classes = {}
  46. def __init__(self, audit_author, audit_notes):
  47. self.audit_author = ensure_person(audit_author)
  48. self.audit_notes = audit_notes
  49. def to_dict(self):
  50. """
  51. Returns a dict with all the constructor arguments to recreate this
  52. operation
  53. """
  54. return {
  55. "audit_author": self.audit_author,
  56. "audit_notes": self.audit_notes,
  57. }
  58. def to_json(self, **kw):
  59. res = self.to_dict()
  60. res["Operation"] = self.__class__.__name__
  61. return json.dumps(res, cls=JSONSerializer, **kw)
  62. @classmethod
  63. def register(cls, _class):
  64. cls.classes[_class.__name__] = _class
  65. return _class
  66. @classmethod
  67. def from_json(cls, encoded):
  68. import process.ops as pops
  69. val = json.loads(encoded)
  70. _class = val.pop("Operation")
  71. Op = cls.classes[_class]
  72. return Op(**val)
  73. @Operation.register
  74. class CreateUser(Operation):
  75. def __init__(self, audit_author, audit_notes, **kw):
  76. super(CreateUser, self).__init__(audit_author, audit_notes)
  77. self.fpr = kw.pop("fpr", None)
  78. for field in ("last_login", "date_joined", "status_changed", "created"):
  79. if field in kw: kw[field] = ensure_datetime(kw[field])
  80. if "expires" in kw:
  81. kw["expires"] = ensure_date(kw["expires"])
  82. self.kwargs = kw
  83. def __str__(self):
  84. return "Create user {}".format(self.kwargs.get("email", "<missing email>"))
  85. def execute(self):
  86. p = bmodels.Person.objects.create_user(
  87. audit_author=self.audit_author,
  88. audit_notes=self.audit_notes,
  89. **self.kwargs)
  90. if self.fpr:
  91. fpr = p.fprs.create(
  92. fpr=self.fpr,
  93. is_active=True,
  94. audit_author=self.audit_author,
  95. audit_notes=self.audit_notes,
  96. )
  97. def to_dict(self):
  98. res = super(CreateUser, self).to_dict()
  99. if self.fpr: res["fpr"] = self.fpr
  100. res.update(**self.kwargs)
  101. return res
  102. @Operation.register
  103. class ChangeStatus(Operation):
  104. def __init__(self, audit_author, audit_notes, person, status, status_changed=None):
  105. super(ChangeStatus, self).__init__(audit_author, audit_notes)
  106. self.person = ensure_person(person)
  107. self.status = status
  108. self.status_changed = status_changed if status_changed else now()
  109. def __str__(self):
  110. return "Change status of {} to {}".format(self.person.lookup_key, self.status)
  111. def execute(self):
  112. import process.models as pmodels
  113. process = pmodels.Process.objects.create(
  114. person=self.person,
  115. applying_for=self.status,
  116. frozen_by=self.audit_author,
  117. frozen_time=self.status_changed,
  118. approved_by=self.audit_author,
  119. approved_time=self.status_changed,
  120. closed=self.status_changed,
  121. skip_requirements=True,
  122. )
  123. process.add_log(
  124. changed_by=self.audit_author,
  125. logtext=self.audit_notes,
  126. is_public=False,
  127. action="proc_approve",
  128. logdate=self.status_changed
  129. )
  130. self.person.status = self.status
  131. self.person.status_changed = self.status_changed
  132. self.person.save(
  133. audit_author=self.audit_author,
  134. audit_notes=self.audit_notes)
  135. def to_dict(self):
  136. res = super(ChangeStatus, self).to_dict()
  137. res["person"] = self.person
  138. res["status"] = self.status
  139. res["status_changed"] = self.status_changed
  140. return res
  141. @Operation.register
  142. class ChangeFingerprint(Operation):
  143. def __init__(self, audit_author, audit_notes, person, fpr):
  144. super(ChangeFingerprint, self).__init__(audit_author, audit_notes)
  145. self.person = ensure_person(person)
  146. self.fpr = fpr
  147. def __str__(self):
  148. return "Change fingerprint of {} to {}".format(self.person.lookup_key, self.fpr)
  149. def execute(self):
  150. fpr = self.person.fprs.create(
  151. fpr=self.fpr, is_active=True,
  152. audit_author=self.audit_author,
  153. audit_notes=self.audit_notes)
  154. def to_dict(self):
  155. res = super(ChangeFingerprint, self).to_dict()
  156. res["person"] = self.person
  157. res["fpr"] = self.fpr
  158. return res
  159. @Operation.register
  160. class CloseOldProcess(Operation):
  161. def __init__(self, audit_author, audit_notes, process, logtext, logdate=None):
  162. super(CloseOldProcess, self).__init__(audit_author, audit_notes)
  163. self.process = ensure_old_process(process)
  164. self.logtext = logtext
  165. self.logdate = logdate if logdate else now()
  166. def execute(self):
  167. l = bmodels.Log.for_process(self.process, changed_by=self.audit_author, logdate=self.logdate, logtext=self.logtext)
  168. l.save()
  169. self.process.progress = const.PROGRESS_DONE
  170. self.process.is_active = False
  171. self.process.save()
  172. self.process.person.status = self.process.applying_for
  173. self.process.person.status_changed = self.logdate
  174. self.process.person.save(audit_author=self.audit_author, audit_notes=self.logtext)
  175. # Mail leader@debian.org as requested by mehdi via IRC on 2016-07-14
  176. if self.process.applying_for in (const.STATUS_DD_NU, const.STATUS_DD_U):
  177. notify_new_dd(self.process)
  178. def to_dict(self):
  179. res = super(CloseOldProcess, self).to_dict()
  180. res["process"] = self.process.pk
  181. res["logtext"] = self.logtext
  182. res["logdate"] = self.logdate
  183. return res