def show_test(request, problem_id, test_name): problem = get_object_or_404(Problem, id=problem_id) tests_path = norm(os.path.join(problem.path, globalconfig.temp_tests_dir)) test_path = norm(os.path.join(tests_path, test_name)) response = HttpResponse(FileWrapper(open(test_path, 'rb')), content_type='text/plain') response['Content-Disposition'] = 'attachment; filename="{}"'.format(os.path.basename(test_path)) return response
def upload_solution(request, id): problem = Problem.objects.get(id=id) if request.method == 'POST': form = SolutionAddForm(request.POST, request.FILES) if 'submit_file' in form.data: if form.is_valid() and request.FILES: solution = Solution(problem=problem) directory = norm(os.path.join(str(problem.path), 'solutions')) solution.path = norm( os.path.relpath(file_save(request.FILES['solution_file'], directory), start=str(problem.path))) solution.input = form.cleaned_data['input_file_name'] solution.output = form.cleaned_data['output_file_name'] solution.save() for choice in Verdict.objects.filter( name__in=form.cleaned_data['possible_verdicts']): solution.possible_verdicts.add(choice) for choice in Verdict.objects.filter( name__in=form.cleaned_data['expected_verdicts']): solution.expected_verdicts.add(choice) solution.save() form = SolutionAddForm() export_from_database(problem) else: form = SolutionAddForm() else: form = SolutionAddForm() return {'form': form}
def edit_dict(request, id): model = Problem.objects.get(id=id) statement_abspath = norm(os.path.join( str(model.path), str(model.statement_path)) ) description_abspath = norm(os.path.join( str(model.path), str(model.description_path)) ) analysis_abspath = norm(os.path.join(str(model.path), str(model.analysis_path))) vals = edit_load_files(statement_abspath, description_abspath, analysis_abspath) if request.method == 'POST': form = ProblemEditMaterialsForm(request.POST) if form.is_valid(): if not vals[0][1]: file_write(form.cleaned_data["statement"], statement_abspath) if not vals[1][1]: file_write( form.cleaned_data["description"], description_abspath ) if not vals[2][1]: file_write(form.cleaned_data["analysis"], analysis_abspath) # Here we have to do "git commit". vals = edit_load_files(statement_abspath, description_abspath, analysis_abspath) form = ProblemEditMaterialsForm(initial={ 'statement': vals[0][0], 'description': vals[1][0], 'analysis': vals[2][0], }) for num, name in enumerate(('statement', 'description', 'analysis')): if vals[num][1]: form.fields[name].widget.attrs['readonly'] = True return {'form': form, 'problem_id': id}
def upload_solution(request, id): problem = Problem.objects.get(id=id) if request.method == 'POST': form = SolutionAddForm(request.POST, request.FILES) if 'submit_file' in form.data: if form.is_valid() and request.FILES: solution = Solution(problem=problem) directory = norm(os.path.join(str(problem.path), 'solutions')) solution.path = norm(os.path.relpath( file_save(request.FILES['solution_file'], directory), start=str(problem.path))) solution.input = form.cleaned_data['input_file_name'] solution.output = form.cleaned_data['output_file_name'] solution.save() for choice in Verdict.objects.filter(name__in=form.cleaned_data['possible_verdicts']): solution.possible_verdicts.add(choice) for choice in Verdict.objects.filter(name__in=form.cleaned_data['expected_verdicts']): solution.expected_verdicts.add(choice) solution.save() form = SolutionAddForm() export_from_database(problem) else: form = SolutionAddForm() else: form = SolutionAddForm() return {'form': form}
def upload_main_block(request, problem): if request.method == 'POST' and 'upload_main' in request.POST: form = ProblemUploadFilesForm(request.POST, request.FILES) modified = False if form.is_valid(): if form.cleaned_data['select_checker']: with ChangeDir(problem.path): path = standard_checkers_utils.add_standard_checker_to_solution( form.cleaned_data['select_checker']) problem.checker_path = norm(path) modified = True elif 'checker' in request.FILES.keys(): checker_file = request.FILES['checker'] problem.checker_path = norm(checker_file.name) file_save(checker_file, problem.path) modified = True if 'validator' in request.FILES.keys(): validator_file = request.FILES['validator'] problem.validator_path = norm(validator_file.name) file_save(validator_file, problem.path) modified = True problem.save() if modified: export_from_database(problem) form = ProblemUploadFilesForm() return {'form': form, 'problem': problem}
def edit_dict(request, id): model = Problem.objects.get(id=id) statement_abspath = norm( os.path.join(str(model.path), str(model.statement_path))) description_abspath = norm( os.path.join(str(model.path), str(model.description_path))) analysis_abspath = norm( os.path.join(str(model.path), str(model.analysis_path))) vals = edit_load_files(statement_abspath, description_abspath, analysis_abspath) if request.method == 'POST': form = ProblemEditMaterialsForm(request.POST) if form.is_valid(): if not vals[0][1]: file_write(form.cleaned_data["statement"], statement_abspath) if not vals[1][1]: file_write(form.cleaned_data["description"], description_abspath) if not vals[2][1]: file_write(form.cleaned_data["analysis"], analysis_abspath) # Here we have to do "git commit". vals = edit_load_files(statement_abspath, description_abspath, analysis_abspath) form = ProblemEditMaterialsForm( initial={ 'statement': vals[0][0], 'description': vals[1][0], 'analysis': vals[2][0], }) for num, name in enumerate(('statement', 'description', 'analysis')): if vals[num][1]: form.fields[name].widget.attrs['readonly'] = True return {'form': form, 'problem_id': id}
def upload_main_block(request, problem): if request.method == 'POST' and 'upload_main' in request.POST: form = ProblemUploadFilesForm(request.POST, request.FILES) modified = False if form.is_valid(): if form.cleaned_data['select_checker']: with ChangeDir(problem.path): path = standard_checkers_utils.add_standard_checker_to_solution(form.cleaned_data['select_checker']) problem.checker_path = norm(path) modified = True elif 'checker' in request.FILES.keys(): checker_file = request.FILES['checker'] problem.checker_path = norm(checker_file.name) file_save(checker_file, problem.path) modified = True if 'validator' in request.FILES.keys(): validator_file = request.FILES['validator'] problem.validator_path = norm(validator_file.name) file_save(validator_file, problem.path) modified = True problem.save() if modified: export_from_database(problem) form = ProblemUploadFilesForm() return {'form': form, 'problem': problem}
def export_from_database(model=None, path=None, name=globalconfig.default_package): assert (model is None) != (path is None) if path is not None: model = get_problem_by_path(norm(path)) with ChangeDir(model.path): try: conf = PackageConfig.get_config(".", name) except TypeError: # Seemingly, this is due to a lacking please_verion. conf = ConfigFile(name) conf["please_version"] = conf["please_version"] or str(globalconfig.please_version) conf["name"] = str(model.name) conf["shortname"] = str(model.short_name) conf["tags"] = "; ".join(map(str, model.tags.all())) conf["type"] = "" conf["input"] = str(model.input) conf["output"] = str(model.output) conf["time_limit"] = str(model.time_limit) conf["memory_limit"] = str(model.memory_limit) conf["checker"] = str(model.checker_path) conf["validator"] = str(model.validator_path) if model.main_solution is not None: conf["main_solution"] = str(model.main_solution.path) conf["statement"] = str(model.statement_path) conf["description"] = str(model.description_path) conf["hand_answer_extension"] = str(model.hand_answer_extension) conf["well_done_test"] = list(map(lambda well_done: well_done.name, model.well_done_test.all())) conf["well_done_answer"] = list(map(lambda well_done: well_done.name, model.well_done_answer.all())) conf["analysis"] = str(model.analysis_path) conf.write() sources = [] already_there = [norm(x["source"]) for x in conf["solution"]] for solution in model.solution_set.all(): solution.path = norm(solution.path) sources.append(str(solution.path)) if str(solution.path) in already_there: continue args = [] if solution.input: args += ["input", str(solution.input)] if solution.output: args += ["output", str(solution.output)] if solution.possible_verdicts.count() != 0: args += ["possible"] + list(map(str, solution.possible_verdicts.all())) if solution.expected_verdicts.count() != 0: args += ["expected"] + list(map(str, solution.expected_verdicts.all())) try: add_solution(str(solution.path), args) except PleaseException: solution.delete() for sol in already_there: if (sol not in sources) and (sol != norm(conf["main_solution"])): del_solution(sol)
def view_statement(request, id): problem = get_object_or_404(Problem.objects, id=id) pdf_path = norm(os.path.abspath( os.path.join( str(problem.path), globalconfig.statements_dir, os.path.splitext( os.path.basename(problem.statement_path))[0] + '.pdf'))) return HttpResponse(FileWrapper(open(pdf_path, 'rb')), content_type='application/pdf')
def import_tree(path): paths = [] for root, dirs, files in os.walk(path): if is_problem_path(root): paths.append(norm(root)) problem = Problem(path=root) problem.save() import_to_database(problem) problem.save() return paths
def view_statement(request, id): problem = get_object_or_404(Problem.objects, id=id) pdf_path = norm( os.path.abspath( os.path.join( str(problem.path), globalconfig.statements_dir, os.path.splitext(os.path.basename(problem.statement_path))[0] + '.pdf'))) return HttpResponse(FileWrapper(open(pdf_path, 'rb')), content_type='application/pdf')
def add_tree_block(request): if request.method == 'POST': form = AddProblemForm(request.POST) if form.is_valid(): path = norm(form.cleaned_data['path']) return { 'form': form, 'paths': import_tree(path), } else: form = AddProblemForm() return {'form': form}
def gen_statement(request, id): problem = get_object_or_404(Problem.objects, id=id) with ChangeDir(str(problem.path)): pdf_path = norm( os.path.abspath( os.path.join(globalconfig.statements_dir, os.path.basename(generate_problem())))) response = HttpResponse(FileWrapper(open(pdf_path, 'rb')), content_type='application/pdf') response['Content-Disposition'] = 'attachment; filename="{}"'.format( os.path.basename(pdf_path)) return response
def gen_statement(request, id): problem = get_object_or_404(Problem.objects, id=id) with ChangeDir(str(problem.path)): pdf_path = norm(os.path.abspath( os.path.join(globalconfig.statements_dir, os.path.basename(generate_problem()) ))) response = HttpResponse( FileWrapper(open(pdf_path, 'rb')), content_type='application/pdf' ) response['Content-Disposition'] = 'attachment; filename="{}"'.format( os.path.basename(pdf_path) ) return response
def edit_or_create_problem_block(request, problem=None): is_success = False if request.method == 'POST': form = ProblemEditForm(request.POST) if form.is_valid(): if problem is None: if not os.path.exists(form.cleaned_data["path"]): raise NoDirectoryException("There is no such directory!") if os.path.exists(os.path.join(form.cleaned_data["path"], form.cleaned_data["short_name"])): raise ProblemExistsException("This problem already exists") problem = import_to_database(problem, "../templates/Template/") problem.path = norm(os.path.join(form.cleaned_data["path"], form.cleaned_data["short_name"])) with ChangeDir(form.cleaned_data["path"]): generate_problem(form.cleaned_data["short_name"]) if not hasattr(ProblemEditForm, 'available_tags'): other_tags = form.cleaned_data['available_tags'] for tag in other_tags: if tag: problem.tags.add(ProblemTag.objects.get(name=tag)) if form.cleaned_data['new_tags']: for tags in form.cleaned_data['new_tags'].split(';'): for tag in map(str.strip, tags.split(',')): if tag: problem.tags.add(ProblemTag.objects.get_or_create(name=tag)[0]) problem.name = form.cleaned_data["name"] problem.short_name = form.cleaned_data["short_name"] problem.input = form.cleaned_data["input"] problem.output = form.cleaned_data["output"] problem.time_limit = float(form.cleaned_data["time_limit"]) problem.memory_limit = int(form.cleaned_data["memory_limit"]) problem.save() export_from_database(problem) is_success = True else: if problem is None: form = ProblemEditForm() else: form = ProblemEditForm(initial={ 'name': problem.name, 'short_name': problem.short_name, 'input': problem.input, 'output': problem.output, 'time_limit': problem.time_limit, 'memory_limit': problem.memory_limit, }) return { 'form': form, 'is_success': is_success, }
def show_tests_block(request, problem): SIZE_LIMIT = 40 LINES_LIMIT = 7 tests_path = norm(os.path.join(problem.path, globalconfig.temp_tests_dir)) current_test = 1 test_data = [] while True: input_name = '{}'.format(current_test) output_name = '{}.a'.format(current_test) input_file = norm(os.path.join(tests_path, input_name)) output_file = norm(os.path.join(tests_path, output_name)) if not (os.path.exists(input_file) and os.path.exists(output_file)): break input_content, is_input_too_big = read_from_file(input_file, LINES_LIMIT, SIZE_LIMIT) output_content, is_output_too_big = read_from_file(output_file, LINES_LIMIT, SIZE_LIMIT) test_data.append(( { 'content': input_content, 'is_too_big': is_input_too_big, 'name': input_name, }, { 'content': output_content, 'is_too_big': is_output_too_big, 'name': output_name, }, )) current_test += 1 return test_data
def retest_solutions(request, id): problem = Problem.objects.get(id=id) solutions = [{'obj': solution, 'path': solution.path, 'name': norm(os.path.relpath(solution.path, please.globalconfig.solutions_dir)), 'expected_verdicts': solution.expected_verdicts.all(), 'possible_verdicts': solution.possible_verdicts.all()} for solution in Solution.objects.filter(problem__id=id)] if request.method == 'POST': form = EmptyForm(request.POST) for solution in solutions: if (solution['name'] + '_retest' in form.data) or ("retest_all_solutions" in form.data): for result in TestResult.objects.filter(solution=solution['obj']): result.delete() with ChangeDir(problem.path): results = get_test_results_from_solution(solution['path'])[2] results = sorted(results.items(), key=lambda x: int(os.path.basename(x[0]))) for result in results: test_result = TestResult(solution=solution['obj'], verdict=result[1][0].verdict, return_code=result[1][0].return_code, real_time=result[1][0].real_time, cpu_time=result[1][0].cpu_time, used_memory=result[1][0].used_memory, test_number=int(os.path.basename(result[0])), checker_stdout=result[1][1], checker_stderr=result[1][2]) test_result.save() output = [] max_count = 0 for solution in solutions: output.append([{'verdict': str(results.verdict), 'time': str(results.cpu_time), 'solution': solution['name']} for results in TestResult.objects.filter(solution=solution['obj'])]) max_count = max(max_count, len(output[-1])) for row in output: if len(row) != max_count: row.extend([{}] * (max_count - len(row))) return {'output': list(zip(*output)), 'solutions': [solution['name'] for solution in solutions], 'expected_verdicts': [solution['expected_verdicts'] for solution in solutions], 'possible_verdicts': [solution['possible_verdicts'] for solution in solutions]}
def add_problem_block(request): is_success, is_error = False, False if request.method == 'POST': form = AddProblemForm(request.POST) if form.is_valid(): path = norm(form.cleaned_data['path']) if is_problem_path(path): problem = Problem(path=path) problem.save() import_to_database(problem) problem.save() is_success = True else: is_error = True else: form = AddProblemForm() return { 'form': form, 'is_success': is_success, 'is_error': is_error, }
def copy_problem_block(request): is_success, is_error = False, False if request.method == 'POST': form = CopyProblemForm(request.POST) if form.is_valid(): new_path = norm(form.cleaned_data['copy_to']) old_path = Problem.objects.get(id=form.cleaned_data['problem']).path copytree(old_path, new_path) problem = Problem(path=new_path) problem.save() problem = import_to_database(path=new_path) problem.save() is_success = True print(111, problem.id, problem.name) id = problem.id prob = Problem.objects.get(id=id) print(222, prob.id, prob.name) else: form = CopyProblemForm() return { 'form': form, 'is_success': is_success, 'is_error': is_error, }
def import_from_polygon_block(request): is_success = False if request.method == 'POST': form = ProblemImportFromPolygonForm(request.POST) if form.is_valid(): with ChangeDir(form.cleaned_data['target_path']): archive_name = download_zip.get_problem( form.cleaned_data['contest_id'], form.cleaned_data['problem_letter'].upper()) problem_name = create_problem(archive_name + ".zip") problem_path = norm(os.path.join(form.cleaned_data['target_path'], problem_name)) problem = Problem(path=problem_path, short_name=problem_name) problem.save() import_to_database(model=problem) problem.save() form = ProblemImportFromPolygonForm() is_success = True else: form = ProblemImportFromPolygonForm() return { 'form': form, 'is_success': is_success, }
def export_from_database(model=None, path=None, name=globalconfig.default_package): assert (model is None) != (path is None) if path is not None: model = get_problem_by_path(norm(path)) with ChangeDir(model.path): try: conf = PackageConfig.get_config('.', name) except TypeError: # Seemingly, this is due to a lacking please_verion. conf = ConfigFile(name) conf['please_version'] = conf['please_version'] or str( globalconfig.please_version) conf['name'] = str(model.name) conf['shortname'] = str(model.short_name) conf['tags'] = '; '.join(map(str, model.tags.all())) conf['type'] = '' conf['input'] = str(model.input) conf['output'] = str(model.output) conf['time_limit'] = str(model.time_limit) conf['memory_limit'] = str(model.memory_limit) conf['checker'] = str(model.checker_path) conf['validator'] = str(model.validator_path) if model.main_solution is not None: conf['main_solution'] = str(model.main_solution.path) conf['statement'] = str(model.statement_path) conf['description'] = str(model.description_path) conf['hand_answer_extension'] = str(model.hand_answer_extension) conf['well_done_test'] = list( map(lambda well_done: well_done.name, model.well_done_test.all())) conf['well_done_answer'] = list( map(lambda well_done: well_done.name, model.well_done_answer.all())) conf['analysis'] = str(model.analysis_path) conf.write() sources = [] already_there = [norm(x['source']) for x in conf['solution']] for solution in model.solution_set.all(): solution.path = norm(solution.path) sources.append(str(solution.path)) if str(solution.path) in already_there: continue args = [] if solution.input: args += ['input', str(solution.input)] if solution.output: args += ['output', str(solution.output)] if solution.possible_verdicts.count() != 0: args += (['possible'] + list(map(str, solution.possible_verdicts.all()))) if solution.expected_verdicts.count() != 0: args += (['expected'] + list(map(str, solution.expected_verdicts.all()))) try: add_solution(str(solution.path), args) except PleaseException: solution.delete() for sol in already_there: if (sol not in sources) and (sol != norm(conf['main_solution'])): del_solution(sol)
def import_to_database(model=None, path=None, name=globalconfig.default_package): assert (model is None) != (path is None) if path is not None: model = get_problem_by_path(norm(path)) problem_path = norm(path or str(model.path)) if not os.path.exists(problem_path): model.delete() return None conf = PackageConfig.get_config(problem_path, name, ignore_cache=True) model.name = conf.get("name", "") print(111, model.name) model.short_name = conf.get("shortname", "") model.input = conf.get("input", "") model.output = conf.get("output", "") model.time_limit = float(conf.get("time_limit", "2.0")) model.memory_limit = int(conf.get("memory_limit", "268435456")) model.checker_path = norm( os.path.relpath(conf.get("checker", ""), os.path.abspath(problem_path)) if conf.get("checker", "") != "" else "" ) model.validator_path = norm(conf.get("validator", "")) model.statement_path = norm(conf.get("statement", "")) model.description_path = norm(conf.get("description", "")) model.analysis_path = norm(conf.get("analysis", "")) model.hand_answer_extension = conf.get("hand_answer_extension", "") old_solutions = {norm(i.path) for i in model.solution_set.all()} for solution in conf.get("solution", []): path = norm(solution["source"]) sol = Solution.objects.get_or_create(path=path, problem=model)[0] old_solutions.discard(path) sol.input = solution.get("input", "") sol.output = solution.get("output", "") sol.expected_verdicts.clear() sol.possible_verdicts.clear() for verdict in solution["expected"]: sol.expected_verdicts.add(Verdict.objects.get_or_create(name=verdict)[0]) for verdict in solution.get("possible"): sol.possible_verdicts.add(Verdict.objects.get_or_create(name=verdict)[0]) if path == norm(conf["main_solution"]): model.main_solution = sol sol.save() for old in old_solutions: model.solution_set.get(path=old).delete() model.tags.clear() for entry in conf.get("tags", []): model.tags.add(ProblemTag.objects.get_or_create(name=entry)[0]) model.well_done_test.clear() for entry in conf.get("well_done_test", []): try: model.well_done_test.add(WellDone.objects.get(name=entry)) except WellDone.DoesNotExist: pass # Bad well done... model.well_done_answer.clear() for entry in conf.get("well_done_answer", []): try: model.well_done_answer.add(WellDone.objects.get(name=entry)) except WellDone.DoesNotExist: pass model.save() print(model.id, model.name) return model
def get_problem_by_path(path): model = Problem.objects.get_or_create(path=norm(path))[0] return model
def retest_solutions(request, id): problem = Problem.objects.get(id=id) solutions = [{ 'obj': solution, 'path': solution.path, 'name': norm(os.path.relpath(solution.path, please.globalconfig.solutions_dir)), 'expected_verdicts': solution.expected_verdicts.all(), 'possible_verdicts': solution.possible_verdicts.all() } for solution in Solution.objects.filter(problem__id=id)] if request.method == 'POST': form = EmptyForm(request.POST) for solution in solutions: if (solution['name'] + '_retest' in form.data) or ("retest_all_solutions" in form.data): for result in TestResult.objects.filter( solution=solution['obj']): result.delete() with ChangeDir(problem.path): results = get_test_results_from_solution( solution['path'])[2] results = sorted(results.items(), key=lambda x: int(os.path.basename(x[0]))) for result in results: test_result = TestResult( solution=solution['obj'], verdict=result[1][0].verdict, return_code=result[1][0].return_code, real_time=result[1][0].real_time, cpu_time=result[1][0].cpu_time, used_memory=result[1][0].used_memory, test_number=int(os.path.basename(result[0])), checker_stdout=result[1][1], checker_stderr=result[1][2]) test_result.save() output = [] max_count = 0 for solution in solutions: output.append([{ 'verdict': str(results.verdict), 'time': str(results.cpu_time), 'solution': solution['name'] } for results in TestResult.objects.filter(solution=solution['obj'])]) max_count = max(max_count, len(output[-1])) for row in output: if len(row) != max_count: row.extend([{}] * (max_count - len(row))) return { 'output': list(zip(*output)), 'solutions': [solution['name'] for solution in solutions], 'expected_verdicts': [solution['expected_verdicts'] for solution in solutions], 'possible_verdicts': [solution['possible_verdicts'] for solution in solutions] }
def import_to_database(model=None, path=None, name=globalconfig.default_package): assert ((model is None) != (path is None)) if path is not None: model = get_problem_by_path(norm(path)) problem_path = norm(path or str(model.path)) if not os.path.exists(problem_path): model.delete() return None conf = PackageConfig.get_config(problem_path, name, ignore_cache=True) model.name = conf.get("name", "") print(111, model.name) model.short_name = conf.get("shortname", "") model.input = conf.get("input", "") model.output = conf.get("output", "") model.time_limit = float(conf.get("time_limit", "2.0")) model.memory_limit = int(conf.get("memory_limit", "268435456")) model.checker_path = norm( os.path.relpath(conf.get("checker", ""), os.path.abspath(problem_path) ) if conf.get("checker", "") != "" else "") model.validator_path = norm(conf.get("validator", "")) model.statement_path = norm(conf.get("statement", "")) model.description_path = norm(conf.get("description", "")) model.analysis_path = norm(conf.get("analysis", "")) model.hand_answer_extension = conf.get("hand_answer_extension", "") old_solutions = {norm(i.path) for i in model.solution_set.all()} for solution in conf.get("solution", []): path = norm(solution['source']) sol = Solution.objects.get_or_create(path=path, problem=model)[0] old_solutions.discard(path) sol.input = solution.get('input', '') sol.output = solution.get('output', '') sol.expected_verdicts.clear() sol.possible_verdicts.clear() for verdict in solution['expected']: sol.expected_verdicts.add( Verdict.objects.get_or_create(name=verdict)[0]) for verdict in solution.get('possible'): sol.possible_verdicts.add( Verdict.objects.get_or_create(name=verdict)[0]) if path == norm(conf['main_solution']): model.main_solution = sol sol.save() for old in old_solutions: model.solution_set.get(path=old).delete() model.tags.clear() for entry in conf.get('tags', []): model.tags.add(ProblemTag.objects.get_or_create(name=entry)[0]) model.well_done_test.clear() for entry in conf.get('well_done_test', []): try: model.well_done_test.add(WellDone.objects.get(name=entry)) except WellDone.DoesNotExist: pass # Bad well done... model.well_done_answer.clear() for entry in conf.get('well_done_answer', []): try: model.well_done_answer.add(WellDone.objects.get(name=entry)) except WellDone.DoesNotExist: pass model.save() print(model.id, model.name) return model