update_6.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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 adapts the dump to some changes in the model introduced in the
  20. commit that created this same file and in commit
  21. af2338b9a22df8a19671c7fee78d9dc4b35c49ea.
  22. """
  23. import json
  24. def parse_compilation_text(s):
  25. if s is None:
  26. return None, None, None, None, None
  27. if s == "No compilation needed.":
  28. return "[\"No compilation needed\"]", None, None, None, None
  29. s = s.split('\n')
  30. if s[0].startswith('Killed with signal '):
  31. del s[1]
  32. status, stats = s[0].split(' [')
  33. time, memory = stats.rstrip(']').split(' - ')
  34. stdout, _, stderr = "\n".join(s[1:])[len("Compiler standard output:\n"):]\
  35. .partition("\nCompiler standard error:\n")
  36. status = status.split(' ')
  37. text = [{'OK': "Compilation succeeded",
  38. 'Failed': "Compilation failed",
  39. 'Time': "Compilation timed out",
  40. 'Killed': "Compilation killed with signal %s (could be triggered "
  41. "by violating memory limits)"}[status[0]]]
  42. if status[0] == "Killed":
  43. text += [int(status[-1])]
  44. if time == "(time unknown)":
  45. time = None
  46. else:
  47. time = float(time.partition(' ')[0])
  48. if memory == "(memory usage unknown)":
  49. memory = None
  50. else:
  51. memory = int(float(memory.partition(' ')[0]) * 1024 * 1024)
  52. if stdout == "(empty)\n":
  53. stdout = ""
  54. if stderr == "(empty)\n":
  55. stderr = ""
  56. return json.dumps(text), time, memory, stdout, stderr
  57. def parse_evaluation_text(s):
  58. if s is None:
  59. return None
  60. # Catch both "Evaluation" and "Execution".
  61. if "tion didn't produce file " in s:
  62. res = ["Evaluation didn't produce file %s", ' '.join(s.split(' ')[4:])]
  63. elif s.startswith("Execution killed with signal "):
  64. res = ["Execution killed with signal %s (could be triggered by "
  65. "violating memory limits)", int(s.rpartition(' ')[2][:-1])]
  66. elif s.startswith("Execution killed because of forbidden syscall "):
  67. res = ["Execution killed because of forbidden syscall %s",
  68. s.rpartition(' ')[2][:-1]]
  69. elif s in ["Execution failed because the return code was nonzero.",
  70. "Execution killed because of forbidden file access.",
  71. "Execution timed out."]:
  72. res = [s.rstrip('.')]
  73. else:
  74. res = [s]
  75. return json.dumps(res)
  76. def parse_tc_details(s):
  77. for i in s:
  78. if "text" in i:
  79. i["text"] = parse_evaluation_text(i["text"])
  80. return s
  81. def parse_st_details(s):
  82. for i in s:
  83. parse_tc_details(i["testcases"])
  84. return s
  85. class Updater:
  86. def __init__(self, data):
  87. assert data["_version"] == 5
  88. self.objs = data
  89. def run(self):
  90. for k, v in self.objs.items():
  91. if k.startswith("_"):
  92. continue
  93. # Compilation
  94. if v["_class"] in ("SubmissionResult", "UserTestResult"):
  95. # Parse compilation_text
  96. v["compilation_text"], v["compilation_time"], \
  97. v["compilation_memory"], v["compilation_stdout"], \
  98. v["compilation_stderr"] = \
  99. parse_compilation_text(v["compilation_text"])
  100. v["compilation_wall_clock_time"] = None
  101. # Evaluation
  102. if v["_class"] == "Evaluation":
  103. v["text"] = parse_evaluation_text(v["text"])
  104. v["execution_memory"] = v["memory_used"]
  105. del v["memory_used"]
  106. if v["_class"] == "UserTestResult":
  107. v["evaluation_text"] = \
  108. parse_evaluation_text(v["evaluation_text"])
  109. v["evaluation_memory"] = v["memory_used"]
  110. del v["memory_used"]
  111. # Scoring
  112. if v["_class"] == "SubmissionResult":
  113. s = v.get("score")
  114. sd = v.get("score_details")
  115. ps = v.get("public_score")
  116. psd = v.get("public_score_details")
  117. rsd = v.get("ranking_score_details")
  118. if self.objs[v["dataset"]]["score_type"] == "Sum":
  119. if sd is not None:
  120. sd = json.dumps(parse_tc_details(json.loads(sd)))
  121. if psd is not None:
  122. psd = json.dumps(parse_tc_details(json.loads(psd)))
  123. else: # Group*
  124. if sd is not None:
  125. sd = json.dumps(parse_st_details(json.loads(sd)))
  126. if psd is not None:
  127. psd = json.dumps(parse_st_details(json.loads(psd)))
  128. if rsd is not None:
  129. rsd = json.dumps(
  130. list(i.strip() for i in rsd[1:-1].split(',')))
  131. v["score"] = s
  132. v["score_details"] = sd
  133. v["public_score"] = ps
  134. v["public_score_details"] = psd
  135. v["ranking_score_details"] = rsd
  136. return self.objs