def test_grade_plate_incorrect_background(): """ Test grader_scripts.2.grade script. """ svg = '<svg width="1440" height="200"> \ <rect x="1" y="1" height="200" width="1440" fill="rgba(222,222,222,0.5)"/> \ <rect x="51" y="1" height="180" width="180" stroke="gray" stroke-dasharray="10 5" stroke-width="2" fill="tomato" opacity="0.75"/> \ <circle cx="359" cy="91" r="90" stroke="gray" stroke-dasharray="10 5" stroke-width="2" fill="lightblue" opacity="0.75"/> \ <polygon points="250,43 160,199 340,199" stroke="darkgray" stroke-width="4" fill="lightgreen" opacity="0.75"/> \ </svg>' answer: dict = { "xqueue_body": { "student_response": ("ffmpeg -hide_banner -loglevel panic -ss 35 -i input_video.mp4 -t 10 cropped.mp4;\n" "echo '{0}' > plate.svg".format(svg) + ";\n" "convert plate.svg -resize 50% plate.png"), "grader_payload": "2", } } expected_response: dict = { "correct": False, "score": 0, "msg": ("Вы сделали подложку из прямоугольника? А заливка верная?\n"), } assert process_answer.process_answer(answer) == expected_response
def test_grade_incorrect(): """ Test grader_scripts.3.grade script. """ answer: dict = { "xqueue_body": { "student_response": """from onvif import ONVIFCamera cam = ONVIFCamera("172.18.191.143", "80", "admin", "Supervisor") media_service = cam.create_media_service() profiles = media_service.GetProfiles() media_profile = profiles[0] ptz = cam.create_ptz_service() status = ptz.GetStatus({"ProfileToken": media_profile.token}) status.Position.PanTilt.x = 0.6 status.Position.PanTilt.y = 0.6 status.Position.Zoom.x = 0.0 request_absolute_move = ptz.create_type("AbsoluteMove") request_absolute_move.ProfileToken = media_profile.token request_absolute_move.Position = status.Position ptz.AbsoluteMove(request_absolute_move) """, "grader_payload": "3", } } expected_response: dict = { "correct": False, "score": 0, "msg": "Неверная позиция камеры. Ожидалось: 0.7 : 0.7 : 0.0. Текущая: 0.6 : 0.6 : 0.0.\n", } assert process_answer.process_answer(answer) == expected_response
def test_grade_incorrect_metadata(): """ Test grader_scripts.2.grade script. """ answer: dict = { "xqueue_body": { "student_response": "ffmpeg -hide_banner -loglevel panic -ss 35 -i input_video.mp4 -t 10 cropped.mp4", "grader_payload": "2", } } expected_response: dict = { "correct": False, "score": 0, "msg": ("Метаданные вашего обрезанного фрагмента не совпадают с ожидаемыми метаданными." " Вы точно скопировали видеокодек?\n" "Ожидаемые: Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 720x576 [SAR 16:15 DAR 4:3]\n" "Полученные: Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 720x576 [SAR 16:15 DAR 4:3]\n" ), } assert process_answer.process_answer(answer) == expected_response
def test_grade_plate_incorrect_triangle(): """ Test grader_scripts.2.grade script. """ svg = '<svg width="1440" height="200"> \ <rect x="1" y="1" height="200" width="1440" fill="rgba(222,222,222,0.5)"/> \ <rect x="51" y="1" height="180" width="180" stroke="gray" stroke-dasharray="10 5" stroke-width="2" fill="tomato" opacity="0.75"/> \ <circle cx="359" cy="91" r="90" stroke="gray" stroke-dasharray="10 5" stroke-width="2" fill="lightblue" opacity="0.75"/> \ <polygon points="249,43 160,199 340,199" stroke="darkgray" stroke-width="4" fill="lightgreen" opacity="0.75"/> \ </svg>' answer: dict = { "xqueue_body": { "student_response": ("ffmpeg -hide_banner -loglevel panic -ss 35 -i input_video.mp4 -t 10 cropped.mp4;\n" "echo '{0}' > plate.svg".format(svg)), "grader_payload": "2", } } expected_response: dict = { "correct": False, "score": 0, "msg": ("Вы перенесли треугольник на 50 пикселей вправо?\n"), } assert process_answer.process_answer(answer) == expected_response
def test_grade_correct(): """ Test grader_scripts.1.grade script. """ answer: dict = { "xqueue_body": { "student_response": """ffmpeg -i input.mp4 -ss 00:00:02 -t 00:00:10 output.mp4 2360k ffmpeg -i input.mp4 -b 1180k output.mp4 129k ffmpeg -i input.mp4 -b:a 64500 output.mp4 ffmpeg -i input.mp4 -c:v copy -c:a mp3 output.mp4 ffmpeg -i input.mp4 -s 640x480 -t 00:00:10 output.gif ffmpeg -i input.mp4 -f image2 -t 00:00:01 output%d.png ffmpeg -i input%d.png -c:v h264 -b 1024k -r 25 output.mp4 ffmpeg -i input.mp4 -ss 00:37:50 -to 00:47:10 -s 640x360 -c:v mpeg2video -c:a mp3 -b:v 1684k output.avi""", "grader_payload": "ffmpeg-1", } } expected_response: dict = { "correct": True, "score": 10.0, "msg": """Задание 1: 1.0/1.0 Верно. Задание 2. Часть 1: 1.0/1.0 Верно. Задание 2. Часть 2: 1.0/1.0 Верно. Задание 3: 1.0/1.0 Верно. Задание 4: 1.0/1.0 Верно. Задание 5. Часть 1: 0.7/0.7 Верно. Задание 5. Часть 2: 1.3/1.3 Верно. Задание 6: 3.0/3.0 Верно. """, } assert process_answer.process_answer(answer) == expected_response
def test_grade_incorrect_invalid_output(): """ Test grader_scripts.1.grade script. """ answer: dict = { "xqueue_body": { "student_response": "-1000", "grader_payload": "1" } } with pytest.raises(InvalidGraderScriptException): assert process_answer.process_answer(answer)
def process_submission(session: requests.Session, host: str, user: str, password: str, queue: str) -> None: """ Get submission from XQueue, process it, and put results back. :param session: Session object. :param host: Host of XQueue broker. :param user: Username for basic auth. :param password: Password for basic auth. :param queue: Queue name. """ logger = get_logger("xqueue") xqueue_get_submission_url = host + "/xqueue/get_submission/" xqueue_put_result_url = host + "/xqueue/put_result/" response: requests.Response = session.get( xqueue_get_submission_url, auth=HTTPBasicAuth(user, password), params={"queue_name": queue}, ) logger.debug("GET request response: %s", response.json()) try: message: str = response.json()["content"] logger.debug("Message: %s", message) if message.startswith("Queue"): return message: dict = json.loads(message) logger.debug("Content: %s", message) # XQueue header is a unique dict, so it is removed from message to allow caching xqueue_header = message.pop("xqueue_header", None) reply: dict = { "xqueue_header": xqueue_header, "xqueue_body": json.dumps(process_answer(message)), } logger.debug("Reply message: %s", reply) response: requests.Response = session.post( xqueue_put_result_url, auth=HTTPBasicAuth(user, password), verify=False, data=reply, ) logger.debug("POST request response: %s", response.json()) except Exception as exception: raise exception
def test_grade_incorrect_not_number(): """ Test grader_scripts.1.grade script. """ answer: dict = { "xqueue_body": { "student_response": "hello", "grader_payload": "1" } } expected_response: dict = { "correct": False, "score": 0, "msg": "Ответ — не число.\n", } assert process_answer.process_answer(answer) == expected_response
def test_grade_incorrect_no_output_video(): """ Test grader_scripts.2.grade script. """ answer: dict = { "xqueue_body": { "student_response": "ffmpeg -hide_banner -loglevel panic", "grader_payload": "2", } } expected_response: dict = { "correct": False, "score": 0, "msg": "Не найден файл с обрезанным видео.\n", } assert process_answer.process_answer(answer) == expected_response
def test_grade_correct(): """ Test grader_scripts.1.grade script. """ answer: dict = { "xqueue_body": { "student_response": "5", "grader_payload": "1" } } expected_response: dict = { "correct": True, "score": 1, "msg": "Верный ответ.\n" } assert process_answer.process_answer(answer) == expected_response
def test_grade_incorrect_duration(): """ Test grader_scripts.2.grade script. """ answer: dict = { "xqueue_body": { "student_response": "ffmpeg -hide_banner -loglevel panic -ss 35 -i input_video.mp4 -to 45 cropped.mp4", "grader_payload": "2", } } expected_response: dict = { "correct": False, "score": 0, "msg": "Обрезанное видео имеет неверную длину.\n", } assert process_answer.process_answer(answer) == expected_response
def test_grade_correct(): """ Test grader_scripts.computer_graphics_3_5.grade script. """ answer: dict = { "xqueue_body": { "student_response": """ffmpeg -i in.mp4 -c:v h264 -b:v 5000k -minrate 5M -bufsize 1000k -maxrate 5000000 -s 1280x720 -r 25 -profile:v high -level:v 42 -bf 2 -g 13 -b:a 128k -c:a aac -strict -2 -ar 96k out.mp4""", "grader_payload": "computer_graphics_3_5", } } expected_response: dict = { "correct": True, "score": 1, "msg": "Верный ответ.\n", } assert process_answer.process_answer(answer) == expected_response
def test_grade_plate_no_svg(): """ Test grader_scripts.2.grade script. """ answer: dict = { "xqueue_body": { "student_response": "ffmpeg -hide_banner -loglevel panic -ss 35 -i input_video.mp4 -t 10 cropped.mp4", "grader_payload": "2", } } expected_response: dict = { "correct": False, "score": 0, "msg": ("Не найден svg-файл с кодом плашки.\n"), } assert process_answer.process_answer(answer) == expected_response
def test_grade_incorrect_time_to_value_wrong(): """ Test grader_scripts.2.grade script. """ answer: dict = { "xqueue_body": { "student_response": "ffmpeg -hide_banner -loglevel panic -ss 35 -i input_video.mp4 -t 5 cropped.mp4", "grader_payload": "2", } } expected_response: dict = { "correct": False, "score": 0, "msg": "Вы неверно задали длительность отрезаемого видеофрагмента.\n", } assert process_answer.process_answer(answer) == expected_response
def test_grade_plate_empty_svg(): """ Test grader_scripts.2.grade script. """ answer: dict = { "xqueue_body": { "student_response": ("ffmpeg -hide_banner -loglevel panic -ss 35 -i input_video.mp4 -t 10 cropped.mp4;\n" 'echo "" > plate.svg'), "grader_payload": "2", } } expected_response: dict = { "correct": False, "score": 0, "msg": ("Ваш svg-файл пуст.\n"), } assert process_answer.process_answer(answer) == expected_response
def test_grade_correct(): """ Test grader_scripts.computer_graphics_2_6.grade script. """ answer: dict = { "xqueue_body": { "student_response": """<svg width="400" height="200"> <rect x="1" y="1" height="180" width="180" stroke="gray" stroke-dasharray="10 5" stroke-width="2" fill="tomato"/> <circle cx="309" cy="91" r="90" stroke="gray" stroke-dasharray="10 5" stroke-width="2" fill="lightblue"/> <polygon points="200,43 110,199 290,199" stroke="darkgray" stroke-width="4" fill="lightgreen"/> </svg>""", "grader_payload": "computer_graphics_2_6", } } expected_response: dict = { "correct": True, "score": 1, "msg": "Верный ответ.\n", } assert process_answer.process_answer(answer) == expected_response
def test_grade_incorrect_time_to_value_none(): """ Test grader_scripts.2.grade script. """ answer: dict = { "xqueue_body": { "student_response": "ffmpeg -hide_banner -loglevel panic -ss 35 -i input_video.mp4 cropped.mp4", "grader_payload": "2", } } expected_response: dict = { "correct": False, "score": 0, "msg": "Мы не смогли найти длительность отрезаемого видеофайла (или конечный момент) в вашем командном файле.\n", } assert process_answer.process_answer(answer) == expected_response
def test_grade_incorrect_time_from_value_wrong(): """ Test grader_scripts.2.grade script. """ answer: dict = { "xqueue_body": { "student_response": "ffmpeg -hide_banner -loglevel panic -ss 25 -i input_video.mp4 -t 10 cropped.mp4", "grader_payload": "2", } } expected_response: dict = { "correct": False, "score": 0, "msg": "Вы задали начальное время обрезанного ролика вне того промежутка, где камера направлена в небо.\n", } assert process_answer.process_answer(answer) == expected_response
def test_grade_incorrect_time_from_value_none(): """ Test grader_scripts.2.grade script. """ answer: dict = { "xqueue_body": { "student_response": "ffmpeg -hide_banner -loglevel panic -i input_video.mp4 -t 10 cropped.mp4", "grader_payload": "2", } } expected_response: dict = { "correct": False, "score": 0, "msg": "Мы не смогли найти в вашем командном файле время, начиная с которого вы отрезали видеофайл.\n", } assert process_answer.process_answer(answer) == expected_response
def test_grade_incorrect_tags(): """ Test grader_scripts.computer_graphics_2_6.grade script. """ answer: dict = { "xqueue_body": { "student_response": """<svg width="400" height="200"> <polygon points="1,1 180,1 180,180 1,180" stroke="gray" stroke-dasharray="10 5" stroke-width="2" fill="tomato"/> <circle cx="309" cy="90" r="90" stroke="gray"; stroke-dasharray="10 5" stroke-width="2" fill="lightblue"/> <polygon points="200,43 110,199 290,199" stroke="darkgray" stroke-width="4" fill="lightgreen"/> </svg>""", "grader_payload": "computer_graphics_2_6", } } expected_response: dict = { "correct": False, "score": 0, "msg": "Проверьте ваш текст на наличие открывающих и закрывающих тегов <svg>," " на наличие синтаксических ошибок в тегах и на то, что все теги закрыты\n", } assert process_answer.process_answer(answer) == expected_response
def test_grade_incorrect_no_shape(): """ Test grader_scripts.computer_graphics_2_6.grade script. """ answer: dict = { "xqueue_body": { "student_response": """<svg width="400" height="200"> <polygon points="1,1 180,1 180,180 1,180" stroke="gray" stroke-dasharray="10 5" stroke-width="2" fill="tomato"/> <circle cx="309" cy="90" r="90" stroke="gray" stroke-dasharray="10 5" stroke-width="2" fill="lightblue"/> <polygon points="200,43 110,199 290,199" stroke="darkgray" stroke-width="4" fill="lightgreen"/> </svg>""", "grader_payload": "computer_graphics_2_6", } } expected_response: dict = { "correct": False, "score": 0, "msg": "Мы не нашли одну из фигур в вашей команде (иногда это сообщение появляется," " если в теге svg есть лишние атрибуты)\n", } assert process_answer.process_answer(answer) == expected_response
def callback_function( current_channel: channel.Channel, basic_deliver: spec.Basic.Deliver, properties: spec.BasicProperties, body: bytes, ) -> None: """ Callback function which receives and proceeds consumed messages from RabbitMQ broker. :param current_channel: Channel object. :param basic_deliver: Object which has exchange, routing key, delivery tag and a redelivered flag of the message. :param properties: Message properties. :param body: Message body. """ logger: Logger = get_logger("rabbitmq") try: message: dict = json.loads(body.decode("utf8")) logger.debug("Received message: %s", message) except json.decoder.JSONDecodeError as exception: send_reply( logger, current_channel, basic_deliver, properties, {}, False, 0, "Ошибка при декодировании сообщения.", ) logger.info("Failed to decode message: {}.".format(body.decode("utf8"))) return # XQueue header is a unique dict, so it is removed from message to allow caching xqueue_header = message.pop("xqueue_header", None) try: xqueue_body: dict = process_answer(message) send_reply( logger, current_channel, basic_deliver, properties, xqueue_header, xqueue_body=xqueue_body, ) except InvalidSubmissionException: send_reply( logger, current_channel, basic_deliver, properties, xqueue_header, False, 0, "Неверный формат сообщения или ID скрипта проверки.", ) except InvalidGraderScriptException: send_reply( logger, current_channel, basic_deliver, properties, xqueue_header, False, 0, "Неверный скрипт проверки.", ) except Exception as exception: send_reply( logger, current_channel, basic_deliver, properties, xqueue_header, False, 0, "Ошибка при проверке ответа.", ) raise exception logger.debug("Finished handling message.")