update_1.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. #!/usr/bin/env python3
  2. # Contest Management System - http://cms-dev.github.io/
  3. # Copyright © 2013 Luca Wehrstedt <luca.wehrstedt@gmail.com>
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU Affero General Public License as
  7. # published by the Free Software Foundation, either version 3 of the
  8. # License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU Affero General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU Affero General Public License
  16. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. """A class to update a dump created by CMS.
  18. Used by DumpImporter and DumpUpdater.
  19. This converts the dump to the new format introduced in commit
  20. db4adada08d66b4797d0569d95e8f0c028a4e5e0.
  21. """
  22. from functools import partial
  23. class Updater:
  24. def __init__(self, data):
  25. self.data = data
  26. self.objs = dict()
  27. self.next_id = 0
  28. def run(self):
  29. self.parse_contest(self.data)
  30. self.objs["_objects"] = ["0"]
  31. return self.objs
  32. def get_id(self):
  33. ret = str(self.next_id)
  34. self.next_id += 1
  35. return ret
  36. def parse_list(self, list_, fun, num=False, **kwargs):
  37. ret = list()
  38. for i, item in enumerate(list_):
  39. item_id = fun(item)
  40. if num:
  41. self.objs[item_id]['num'] = i
  42. for k, v in kwargs.items():
  43. self.objs[item_id][k] = v
  44. ret.append(item_id)
  45. return ret
  46. def parse_dict(self, list_, fun, key, **kwargs):
  47. ret = dict()
  48. for item in list_:
  49. item_id = fun(item)
  50. for k, v in kwargs.items():
  51. self.objs[item_id][k] = v
  52. ret[item[key]] = item_id
  53. return ret
  54. def parse_generic(self, data, cls):
  55. id_ = self.get_id()
  56. data['_class'] = cls
  57. self.objs[id_] = data
  58. return id_
  59. def parse_contest(self, data):
  60. id_ = self.get_id()
  61. data['tasks'] = self.parse_list(
  62. data['tasks'], self.parse_task, contest=id_)
  63. tasks_by_name = dict((self.objs[i]['name'], i) for i in data['tasks'])
  64. data['users'] = self.parse_list(data['users'],
  65. partial(self.parse_user,
  66. tasks_by_name=tasks_by_name),
  67. contest=id_)
  68. data['announcements'] = self.parse_list(
  69. data['announcements'],
  70. partial(self.parse_generic, cls='Announcement'), contest=id_)
  71. data['_class'] = 'Contest'
  72. self.objs[id_] = data
  73. return id_
  74. def parse_task(self, data):
  75. id_ = self.get_id()
  76. data['statements'] = self.parse_dict(
  77. data['statements'], partial(self.parse_generic, cls='Statement'),
  78. 'language', task=id_)
  79. data['attachments'] = self.parse_dict(
  80. data['attachments'], partial(self.parse_generic, cls='Attachment'),
  81. 'filename', task=id_)
  82. data['submission_format'] = self.parse_list(
  83. data['submission_format'],
  84. partial(self.parse_generic, cls='SubmissionFormatElement'),
  85. task=id_)
  86. data['managers'] = self.parse_dict(
  87. data['managers'], partial(self.parse_generic, cls='Manager'),
  88. 'filename', task=id_)
  89. data['testcases'] = self.parse_list(
  90. data['testcases'], partial(self.parse_generic, cls='Testcase'),
  91. True, task=id_)
  92. data['submissions'] = []
  93. data['user_tests'] = []
  94. # Handle some pre-1.0 dumps
  95. if "score_parameters" in data:
  96. data["score_type_parameters"] = data["score_parameters"]
  97. del data["score_parameters"]
  98. data['_class'] = 'Task'
  99. self.objs[id_] = data
  100. return id_
  101. def parse_user(self, data, tasks_by_name):
  102. id_ = self.get_id()
  103. data['messages'] = self.parse_list(
  104. data['messages'],
  105. partial(self.parse_generic, cls='Message'), user=id_)
  106. data['questions'] = self.parse_list(
  107. data['questions'],
  108. partial(self.parse_generic, cls='Question'), user=id_)
  109. data['submissions'] = self.parse_list(
  110. data['submissions'],
  111. partial(self.parse_submission, tasks_by_name=tasks_by_name),
  112. user=id_)
  113. # Because of a bug in older versions of CMS, some dumps may
  114. # lack the user_tests key; unfortunately user tests for such
  115. # contests have been lost
  116. if 'user_tests' in data:
  117. data['user_tests'] = self.parse_list(
  118. data['user_tests'],
  119. partial(self.parse_user_test, tasks_by_name=tasks_by_name),
  120. user=id_)
  121. else:
  122. data['user_tests'] = []
  123. data['_class'] = 'User'
  124. self.objs[id_] = data
  125. return id_
  126. def parse_submission(self, data, tasks_by_name):
  127. id_ = self.get_id()
  128. data['files'] = self.parse_dict(
  129. data['files'],
  130. partial(self.parse_generic, cls='File'), 'filename',
  131. submission=id_)
  132. data['executables'] = self.parse_dict(
  133. data['executables'],
  134. partial(self.parse_generic, cls='Executable'), 'filename',
  135. submission=id_)
  136. data['evaluations'] = self.parse_list(
  137. data['evaluations'],
  138. partial(self.parse_generic, cls='Evaluation'), submission=id_)
  139. if data['token'] is not None:
  140. data['token'] = self.parse_generic(data['token'], 'Token')
  141. task_id = tasks_by_name[data['task']]
  142. data['task'] = task_id
  143. self.objs[task_id]['submissions'].append(id_)
  144. data['_class'] = 'Submission'
  145. self.objs[id_] = data
  146. return id_
  147. def parse_user_test(self, data, tasks_by_name):
  148. id_ = self.get_id()
  149. data['files'] = self.parse_dict(
  150. data['files'], partial(self.parse_generic, cls='UserTestFile'),
  151. 'filename', user_test=id_)
  152. data['executables'] = self.parse_dict(
  153. data['executables'],
  154. partial(self.parse_generic, cls='UserTestExecutable'),
  155. 'filename', user_test=id_)
  156. data['managers'] = self.parse_dict(
  157. data['managers'],
  158. partial(self.parse_generic, cls='UserTestManager'),
  159. 'filename', user_test=id_)
  160. task_id = tasks_by_name[data['task']]
  161. data['task'] = task_id
  162. self.objs[task_id]['user_tests'].append(id_)
  163. data['_class'] = 'UserTest'
  164. self.objs[id_] = data
  165. return id_