使用django代替bottle

This commit is contained in:
virusdefender 2016-03-08 17:55:23 +08:00
parent c14f60a4fb
commit 44749dd94c
19 changed files with 429 additions and 232 deletions

10
manage.py Executable file
View File

@ -0,0 +1,10 @@
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "openvj.settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)

View File

@ -1 +0,0 @@
# coding=utf-8

21
openvj/local_settings.py Normal file
View File

@ -0,0 +1,21 @@
# coding=utf-8
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
# Database
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}

21
openvj/server_settings.py Normal file
View File

@ -0,0 +1,21 @@
# coding=utf-8
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
# Database
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}

View File

@ -1,12 +1,118 @@
# coding=utf-8
"""
Django settings for openvj project.
DB_HOST = "127.0.0.1"
DB_USER = "root"
DB_PASSWORD = "root"
DB_DB = "openvj"
Generated by 'django-admin startproject' using Django 1.9.1.
For more information on this file, see
https://docs.djangoproject.com/en/1.9/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.9/ref/settings/
"""
import os
# 判断运行环境
ENV = os.environ.get("oj_env", "local")
if ENV == "local":
from .local_settings import *
elif ENV == "server":
from .server_settings import *
from .custom_settings import *
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
INSTALLED_ROBOTS = {"pat": {"robot": "robots.pat.PATRobot"},
"hduoj": {"robot": "robots.hduoj.HduojRobot"}}
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
LOG_LEVEL = "DEBUG"
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'server',
]
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'openvj.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'openvj.wsgi.application'
# Password validation
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/1.9/topics/i18n/
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.9/howto/static-files/
STATIC_URL = '/static/'

21
openvj/urls.py Normal file
View File

@ -0,0 +1,21 @@
"""openvj URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.9/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
]

16
openvj/wsgi.py Normal file
View File

@ -0,0 +1,16 @@
"""
WSGI config for openvj project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "openvj.settings")
application = get_wsgi_application()

View File

@ -1 +0,0 @@
# coding=utf-8

9
server/admin.py Normal file
View File

@ -0,0 +1,9 @@
# coding=utf-8
from django.contrib import admin
from .models import OJ, RobotUser, RobotStatusInfo, Problem, Submission
admin.site.register(OJ)
admin.site.register(RobotUser)
admin.site.register(RobotStatusInfo)
admin.site.register(Problem)
admin.site.register(Submission)

7
server/apps.py Normal file
View File

@ -0,0 +1,7 @@
from __future__ import unicode_literals
from django.apps import AppConfig
class ServerConfig(AppConfig):
name = 'server'

View File

@ -1,48 +0,0 @@
# coding=utf-8
import pymysql
from openvj.settings import DB_HOST, DB_USER, DB_PASSWORD, DB_DB
class ObjectDoesNotExist(Exception):
pass
class MultiObjectReturned(Exception):
pass
class DBHandler(object):
def __enter__(self):
self.connection = pymysql.connect(host=DB_HOST,
user=DB_USER,
password=DB_PASSWORD,
db=DB_DB,
charset="utf8",
cursorclass=pymysql.cursors.DictCursor)
return self
def filter(self, sql, args):
with self.connection.cursor() as cursor:
cursor.execute(sql, args)
return cursor.fetchall()
def get(self, sql, args):
r = self.filter(sql, args)
if not r:
raise ObjectDoesNotExist()
if len(r) > 1:
raise MultiObjectReturned()
return r
def first(self, sql, args):
r = self.filter(sql, args)
if not r:
raise ObjectDoesNotExist()
else:
return r[0]
def _close(self):
self.connection.close()
def __exit__(self, exc_type, exc_val, exc_tb):
self._close()

View File

@ -1,73 +0,0 @@
CREATE DATABASE IF NOT EXISTS openvj DEFAULT CHARACTER SET utf8;
use openvj;
CREATE TABLE IF NOT EXISTS oj (
id VARCHAR(40) PRIMARY KEY,
name VARCHAR(50) NOT NULL UNIQUE,
status SMALLINT DEFAULT 1 NOT NULL
);
CREATE TABLE IF NOT EXISTS robot_user(
id VARCHAR(40) PRIMARY KEY,
oj_id VARCHAR(40) NOT NULL,
username VARCHAR(50) NOT NULL,
password VARCHAR(50) NOT NULL,
status SMALLINT NOT NULL,
last_login_time DATETIME NOT NULL,
FOREIGN KEY (oj_id) REFERENCES oj(id),
UNIQUE KEY oj_username (oj_id, username)
);
CREATE TABLE IF NOT EXISTS robot_status(
name VARCHAR(40) NOT NULL,
status SMALLINT NOT NULL,
info TEXT NOT NULL,
oj_id VARCHAR(40) NOT NULL,
robot_user_id VARCHAR(40) NOT NULL,
FOREIGN KEY (oj_id) REFERENCES oj(id),
FOREIGN KEY (robot_user_id) REFERENCES robot_user(id)
);
CREATE TABLE IF NOT EXISTS apikey (
apikey VARCHAR(40) PRIMARY KEY,
name VARCHAR(50) NOT NULL ,
create_time DATETIME DEFAULT NOW() NOT NULL,
is_valid SMALLINT DEFAULT 1 NOT NULL
);
CREATE TABLE IF NOT EXISTS problem (
id VARCHAR(40) PRIMARY KEY,
oj_id VARCHAR(40) NOT NULL,
spj SMALLINT NOT NULL,
url VARCHAR(200) NOT NULL,
submit_url VARCHAR(200) NOT NULL,
title VARCHAR(100) NOT NULL,
description TEXT NOT NULL,
time_limit INTEGER NOT NULL,
memory_limit INTEGER NOT NULL,
input_description TEXT NOT NULL,
output_desription TEXT NOT NULL,
samples TEXT NOT NULL,
is_valid SMALLINT DEFAULT 1 NOT NULL,
/* 可能是分为正在爬取 爬取完成 爬取失败等状态 */
status SMALLINT NOT NULL,
FOREIGN KEY (oj_id) REFERENCES oj(id)
);
CREATE TABLE IF NOT EXISTS submission (
id VARCHAR(40) PRIMARY KEY,
problem_id VARCHAR(40) NOT NULL,
result SMALLINT NOT NULL,
cpu_time INTEGER,
memory INTEGER,
info TEXT,
user_id VARCHAR(40) NOT NULL,
FOREIGN KEY (problem_id) REFERENCES problem(id),
FOREIGN KEY (user_id) REFERENCES robot_user(id)
);

View File

@ -0,0 +1,115 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2016-03-08 08:20
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='APIKey',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('api_key', models.CharField(max_length=40)),
('name', models.CharField(max_length=40)),
('is_valid', models.BooleanField(default=True)),
('create_time', models.DateTimeField(auto_now_add=True)),
],
options={
'db_table': 'api_key',
},
),
migrations.CreateModel(
name='OJ',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=30)),
('is_valid', models.BooleanField(default=True)),
],
options={
'db_table': 'oj',
},
),
migrations.CreateModel(
name='Problem',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('url', models.URLField()),
('submit_url', models.URLField()),
('title', models.CharField(max_length=100)),
('description', models.TextField()),
('time_limit', models.IntegerField()),
('memory_limit', models.IntegerField()),
('input_description', models.TextField()),
('output_description', models.TextField()),
('samples', models.TextField()),
('spj', models.BooleanField()),
('hint', models.TextField()),
('is_valid', models.BooleanField(default=True)),
('status', models.IntegerField()),
('task_id', models.CharField(max_length=40)),
('oj', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='server.OJ')),
],
options={
'db_table': 'problem',
},
),
migrations.CreateModel(
name='RobotStatusInfo',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status_info', models.TextField()),
('last_update_time', models.DateTimeField(auto_now=True)),
],
options={
'db_table': 'robot_status_info',
},
),
migrations.CreateModel(
name='RobotUser',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('username', models.CharField(max_length=30)),
('password', models.CharField(max_length=30)),
('is_valid', models.BooleanField(default=True)),
('last_login_time', models.DateTimeField()),
('status', models.IntegerField(choices=[('Occupied', 1), ('free', 0)])),
('oj', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='server.OJ')),
],
options={
'db_table': 'robot_user',
},
),
migrations.CreateModel(
name='Submission',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('result', models.IntegerField()),
('cpu_time', models.IntegerField()),
('memory', models.IntegerField()),
('info', models.TextField()),
('problem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='server.Problem')),
('robot_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='server.RobotUser')),
],
options={
'db_table': 'submission',
},
),
migrations.AddField(
model_name='robotstatusinfo',
name='robot_user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='server.RobotUser'),
),
migrations.AlterUniqueTogether(
name='robotuser',
unique_together=set([('oj', 'username')]),
),
]

View File

90
server/models.py Normal file
View File

@ -0,0 +1,90 @@
# coding=utf-8
from __future__ import unicode_literals
from django.db import models
class OJ(models.Model):
name = models.CharField(max_length=30)
is_valid = models.BooleanField(default=True)
class Meta:
db_table = "oj"
class APIKey(models.Model):
api_key = models.CharField(max_length=40)
name = models.CharField(max_length=40)
is_valid = models.BooleanField(default=True)
create_time= models.DateTimeField(auto_now_add=True)
class Meta:
db_table = "api_key"
class RobotUserStatus(object):
occupied = 1
free = 0
class RobotUser(models.Model):
oj = models.ForeignKey(OJ)
username = models.CharField(max_length=30)
password = models.CharField(max_length=30)
is_valid = models.BooleanField(default=True)
last_login_time = models.DateTimeField()
status = models.IntegerField(choices=((RobotUserStatus.occupied, "Occupied"), (RobotUserStatus.free, "Free")))
class Meta:
db_table = "robot_user"
unique_together = (("oj", "username"), )
class RobotStatusInfo(models.Model):
status_info = models.TextField()
robot_user = models.ForeignKey(RobotUser)
last_update_time = models.DateTimeField(auto_now=True)
class Meta:
db_table = "robot_status_info"
class ProblemStatus(models.Model):
done = 0
crawling = 1
failed = 2
class Problem(models.Model):
oj = models.ForeignKey(OJ)
url = models.URLField()
submit_url = models.URLField()
title = models.CharField(max_length=100)
description = models.TextField()
time_limit = models.IntegerField()
memory_limit = models.IntegerField()
input_description = models.TextField()
output_description = models.TextField()
samples = models.TextField()
spj = models.BooleanField()
hint = models.TextField()
is_valid = models.BooleanField(default=True)
status = models.IntegerField(choices=((ProblemStatus.done, "Done"),
(ProblemStatus.crawling, "Crawling"),
(ProblemStatus.failed, "Failed")))
task_id = models.CharField(max_length=40)
class Meta:
db_table = "problem"
class Submission(models.Model):
problem = models.ForeignKey(Problem)
result = models.IntegerField()
cpu_time = models.IntegerField()
memory = models.IntegerField()
info = models.TextField()
robot_user = models.ForeignKey(RobotUser)
class Meta:
db_table = "submission"

View File

@ -1,101 +0,0 @@
# coding=utf-8
import json
import logging
import traceback
from bottle import route, get, post, run, response, request, Bottle, install
from openvj.settings import INSTALLED_ROBOTS, LOG_LEVEL
from .db import DBHandler, ObjectDoesNotExist
logging.basicConfig(filename='server.log', level=LOG_LEVEL,
format="%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] "
"[%(module)s:%(funcName)s] [%(levelname)s]- %(message)s")
app = Bottle()
def content_type_plugin(callback):
def wrapper(*args, **kwargs):
body = callback(*args, **kwargs)
response.content_type = "application/json; charset=utf-8"
return body
return wrapper
def apikey_auth_plugin(callback):
def wrapper(*args, **kwargs):
'''
api_key = request.headers.get("VJ_API_KEY")
if not api_key:
return error("Invalid VJ_API_KEY")
with DBHandler() as db:
try:
db.get("SELECT apikey FROM apikey WHERE apikey = %s and is_valid = 1", (api_key, ))
except ObjectDoesNotExist:
return error("VJ_API_KEY does not exist")
'''
request.api_key = "123456"
return callback(*args, **kwargs)
return wrapper
def server_log_plugin(callback):
def wrapper(*args, **kwargs):
try:
return callback(*args, **kwargs)
except Exception:
logging.error(traceback.format_exc())
return error("Server error")
return wrapper
def error(reason):
return json.dumps({"code": 1, "data": reason})
def success(data):
return json.dumps({"code": 0, "data": data})
def parameter_error(message="参数错误"):
return error(message)
@route("/")
def index():
return success("It works")
def import_class(cl):
d = cl.rfind(".")
classname = cl[d+1:len(cl)]
m = __import__(cl[0:d], globals(), locals(), [classname])
return getattr(m, classname)
@get("/problem/")
def get_problem():
oj = request.GET.get("oj")
url = request.GET.get("url")
if not (oj and url):
return parameter_error()
if oj not in INSTALLED_ROBOTS:
return error("oj不存在")
Robot = import_class(INSTALLED_ROBOTS[oj]["robot"])
with DBHandler() as db:
robot_info = db.first("select robot_status.info "
"from robot_status, oj "
"where oj.name = %s and robot_status.oj_id = oj.id", (oj, ))
robot = Robot(**json.loads(robot_info["info"]))
if not robot.check_url(url):
return error("url格式错误")
problem = robot.get_problem(url)
return success(problem)
install(content_type_plugin)
install(apikey_auth_plugin)
install(server_log_plugin)
run(host='127.0.0.1', port=8081, server='gunicorn', workers=4, debug=True)

View File

@ -1 +0,0 @@
# coding=utf-8

3
server/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

3
server/views.py Normal file
View File

@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.