Commit 94899c82 authored by zhengjinlei's avatar zhengjinlei

Initial commit

parents
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
charset = utf-8
end_of_line = lf
[*.bat]
indent_style = tab
end_of_line = crlf
[LICENSE]
insert_final_newline = false
[Makefile]
indent_style = tab
[*.{diff,patch}]
trim_trailing_whitespace = false
*.pid
.idea/
.DS_Store
*.sqlite3
src/config/config.py
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
develop-eggs/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# dotenv
.env
# virtualenv
.venv
venv/
ENV/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
/uploads/
/staticfiles/
help: ## Show this help.
@fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//'
init: ## 初始化python环境,并加载测试数据
python3 -m venv env
env/bin/python3 -m pip install -r requirements.txt
env/bin/python3 src/manage.py migrate
env/bin/python3 src/manage.py loaddata --format yaml fixtures.yaml
@echo "初始化完成。现在你可以运行:make run 启动后端应用了。"
stop: ## 停止 make prd/ make run 启动的服务
-lsof -i:8099 | awk 'NR==2{print $$2}' | xargs kill
run: stop ## 运行后端服务(front)
env/bin/python3 src/manage.py runserver 8099
prd: stop ## 生产环境运行(backend)
nohup env/bin/python3 src/manage.py runserver 8099 2>&1 &
upgrade: ## 升级后端服务代码
env/bin/python3 -m pip install -r requirements.txt
env/bin/python3 src/manage.py migrate
env/bin/python3 src/manage.py loaddata --format yaml fixtures.yaml
env/bin/python3 src/manage.py collectstatic --noinput
dep: ## 部署服务到supervisor与nginx
-sudo cp deploy/nginx/inspect_report_prd.conf /etc/nginx/conf.d/
-sudo cp deploy/supervisor/inspect_report_prd.ini /etc/supervisord.d/
crontab: ## 安装 cron 定时任务
cd src && ../env/bin/python3 manage.py installtasks
cloc: ## 代码量统计。请提前安装cloc(brew install cloc)
cloc --exclude-dir="env,docs,logs,include,CMakeFiles,dist,static,theme,build,staticfiles" --exclude-ext="json,xml,yaml,yml,md" .
%: ## cli命令
env/bin/python3 "cli.py" $(MAKECMDGOALS)
# inspect_report
## 安装条件
* xxx
## 安装说明
* 构建运行环境
```
make init
```
* 代码升级
```
make upgrade
```
* 启动服务
```
make run
```
## FAQ
1. pycharm debug 失败
注意跟下代码。一般来讲如果代码中引入:eventlet.monkey_patch(),就会导致不能debug。
如 nameko 的 testing/pytest.py 中就有相应代码。注释掉后,pycharm 的 debug 就正常了。
2. 创建数据库
* `postgres`
```sql
CREATE USER inspect_report_prd WITH PASSWORD '<password>';
CREATE DATABASE inspect_report_prd OWNER inspect_report_prd;
```
注意如果用 远程数据库,请把账号密码放在 `config.py` 中。不要提交到代码库中
`config.py`
```python
db_name = '<name>'
db_host = '<host>'
db_port = '<port>'
db_user = '<username>'
db_password = '<password>'
```
`settings.py`
```python
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': config.db_name,
'USER': config.db_user,
'PASSWORD': config.db_password,
'HOST': config.db_host,
'PORT': config.db_port,
}
}
```
\ No newline at end of file
#!/usr/bin/env python
# coding: utf-8
import sys
import os
import logging
import click
import subprocess
import django
sys.path.append(os.path.join(os.path.dirname(__file__), 'src'))
os.environ['DJANGO_SETTINGS_MODULE'] = 'config.settings'
django.setup()
log = logging.getLogger(__name__)
@click.group()
def main():
pass
@main.command()
def mm():
"""make migration and migrate it."""
click.secho('make migrations...', fg='yellow')
subprocess.call('python src/manage.py makemigrations', shell=True)
click.secho('migrate...', fg='yellow')
subprocess.call('python src/manage.py migrate', shell=True)
if __name__ == '__main__':
main()
server {
listen 80;
server_name inspect.report.taijihuabao.com;
access_log /data/prd/inspect_report/logs/inspect_report.access.log main;
error_log /data/prd/inspect_report/logs/inspect_report.error.log warn;
root /data/prd/inspect_report/www/;
gzip on;
gzip_min_length 4k;
gzip_comp_level 6;
gzip_types text/plain application/x-javascript text/css application/xml application/javascript application/json;
gzip_vary on;
gzip_http_version 1.1;
location @django {
proxy_pass http://127.0.0.1:8099;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
add_header Cache-Control no-store;
}
location = / {
try_files /index.html @django;
}
location / {
try_files $uri $uri/ @django;
}
location /static {
alias /data/prd/inspect_report/staticfiles;
}
}
[program:inspect_report_prd]
command=/data/prd/inspect_report/env/bin/gunicorn -c gunicorn.conf.py -p gunicorn-inspect_report.pid config.wsgi
directory=/data/prd/inspect_report/src
user=pyer
autostart=true
autorestart=true
startretries=5
stdout_logfile=/data/prd/inspect_report/logs/supervisor_stdout.log
stderr_logfile=/data/prd/inspect_report/logs/supervisor_stderr.log
environment=DEBUG="False"
\ No newline at end of file
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = inspect_report
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
# -*- coding: utf-8 -*-
#
# Configuration file for the Sphinx documentation builder.
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'inspect_report}'
copyright = '2018, 太极华保'
author = '太极华保'
# The short X.Y version
version = '1.0'
# The full version, including alpha/beta/rc tags
release = '1.0'
# -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.todo',
'sphinx.ext.imgmath',
'sphinx.ext.viewcode',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = 'zh_CN'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path .
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself. Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
# html_sidebars = {}
# -- Options for HTMLHelp output ---------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'inspect_reportdoc'
# -- Options for LaTeX output ------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'inspect_report.tex', 'inspect_report} Documentation',
'inspect_report', 'manual'),
]
# -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'inspect_report', 'inspect_report Documentation',
[author], 1)
]
# -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'inspect_report', 'inspect_report Documentation',
author, '太极华保', 'inspect_report',
'Miscellaneous'),
]
# -- Options for Epub output -------------------------------------------------
# Bibliographic Dublin Core info.
epub_title = project
epub_author = author
epub_publisher = author
epub_copyright = copyright
# The unique identifier of the text. This can be a ISBN number
# or the project homepage.
#
# epub_identifier = ''
# A unique identification for the text.
#
# epub_uid = ''
# A list of files that should not be packed into the epub file.
epub_exclude_files = ['search.html']
# -- Extension configuration -------------------------------------------------
# -- Options for todo extension ----------------------------------------------
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True
.. tjccdoc documentation master file, created by
sphinx-quickstart on Mon Jul 2 10:55:52 2018.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
inspect_report
===================================
.. toctree::
:maxdepth: 2
:caption: Contents:
后台管理使用手册
索引
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
后台管理使用手册
=================
使用对象
-----------
.. glossary::
超级管理员
超级管理员拥有所有权限。
超级管理员负责创建公司、分配权限、设定座席登录密码等初始化工作
组长
组长负责从运营商系统同步项目,然后把呼叫清单分配给具体的座席人员。组长同时可以在这里查看总体的外呼情况。
数据结构
----------
.. glossary::
公司
系统的权限是按公司隔离的. 每个公司员工只能看到自己公司的数据.
使用步骤
---------
- model: auth.user
pk: 1
fields:
password: pbkdf2_sha256$100000$eISiXiLKYQ1Y$0C0Bc93qPlg+c5h0e+qfoAR0Dmqh9Zf2C81aUdTVSss=
last_login: 2018-06-21 06:24:50.499788+00:00
is_superuser: true
username: tjhb
first_name: ''
last_name: ''
email: ''
is_staff: true
is_active: true
date_joined: 2018-06-20 05:23:00+00:00
groups: []
user_permissions: []
[pytest]
DJANGO_SETTINGS_MODULE = config.settings
# -- recommended but optional:
python_files = tests.py test_*.py *_tests.py
testpaths = tests
# coding: utf-8
from functools import wraps
from django.http import HttpRequest
"""
def token_required(func):
@wraps(func)
def wrapper(request: HttpRequest, *args, **kwargs):
return func(request, *args, **kwargs)
return wrapper
"""
# coding: utf-8
# 经常调整的配置文件参数
debug = True
\ No newline at end of file
# coding: utf-8
from django.conf import settings
def footer(request):
return {'footer': settings.FOOTER}
# coding: utf-8
from django.http import Http404
from rest_framework import exceptions, status
from rest_framework.exceptions import PermissionDenied, ValidationError
from rest_framework.response import Response
from rest_framework.views import set_rollback
import logging
log = logging.getLogger(__name__)
def exception_handler(exc, context):
"""
Returns the response that should be used for any given exception.
By default we handle the REST framework `APIException`, and also
Django's built-in `Http404` and `PermissionDenied` exceptions.
Any unhandled exceptions may return `None`, which will cause a 500 error
to be raised.
"""
if isinstance(exc, ValidationError):
msg = '参数非法'
data = {'msg': msg, 'code': -998, 'errors': exc.detail}
set_rollback()
return Response(data, status=status.HTTP_402_PAYMENT_REQUIRED)
elif isinstance(exc, exceptions.APIException):
headers = {}
if getattr(exc, 'auth_header', None):
headers['WWW-Authenticate'] = exc.auth_header
if getattr(exc, 'wait', None):
headers['Retry-After'] = '%d' % exc.wait
if isinstance(exc.detail, (list, dict)):
data = exc.detail
else:
data = exc.get_full_details()
set_rollback()
return Response(data, status=exc.status_code, headers=headers)
elif isinstance(exc, Http404):
msg = 'Not found.'
data = {'msg': str(msg), 'code': -997}
set_rollback()
return Response(data, status=status.HTTP_404_NOT_FOUND)
elif isinstance(exc, PermissionDenied):
msg = 'Permission denied.'
data = {'msg': str(msg), 'code': -996}
set_rollback()
return Response(data, status=status.HTTP_403_FORBIDDEN)
else:
log.exception(exc)
data = {
'msg': '不能处理的异常',
'code': -999
}
set_rollback()
return Response(data, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
# coding: utf-8
import os
LOG_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'logs')
if not os.path.exists(LOG_DIR):
os.mkdir(LOG_DIR)
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '%(asctime)s | %(levelname)s | %(name)s:%(lineno)s | %(funcName)s '
'| %(process)d | %(thread)d | %(threadName)s | %(message)s'
},
'simple': {
'format': '%(asctime)s | %(levelname)s | %(name)s:%(lineno)s:%(funcName)s | %(message)s'
},
'dblog': {
'format': '%(asctime)s | %(levelname)s | %(message)s'
}
},
# 'filters': {
# 'require_debug_true': {
# '()': 'django.utils.log.RequireDebugTrue',
# },
# },
'handlers': {
'console': {
'level': 'DEBUG',
# 'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'db_file': {
'level': 'DEBUG',
'class': 'logging.handlers.WatchedFileHandler',
'filename': os.path.join(LOG_DIR, 'db.log'),
'formatter': 'dblog',
},
'app_file': {
'level': 'DEBUG',
'class': 'logging.handlers.WatchedFileHandler',
'filename': os.path.join(LOG_DIR, 'app.log'),
'formatter': 'verbose',
},
'sec_file': {
'level': 'DEBUG',
'class': 'logging.handlers.WatchedFileHandler',
'filename': os.path.join(LOG_DIR, 'sec.log'),
'formatter': 'verbose',
},
},
'root': {
'handlers': ['console', 'app_file'],
'level': 'INFO',
},
'loggers': {
'django.db': {
'handlers': ['db_file', ],
'level': 'DEBUG',
'propagate': False
},
'security': {
'handlers': ['sec_file'],
'level': 'INFO',
'propagate': False
},
'inspect_report': {
'handlers': ['app_file'],
'level': 'INFO',
'propagate': False
}
}
}
"""
Django settings for tjccconfig project.
Generated by 'django-admin startproject' using Django 2.0.3.
For more information on this file, see
https://docs.djangoproject.com/en/2.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.0/ref/settings/
"""
import datetime
import os
import pymysql
import raven
from . import config
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '_uh0jj8&ge&xp_0^*n&ms_@)72pzlmx99-=4!7#esgdiq@%&#k'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = config.debug
ALLOWED_HOSTS = ['*']
# Application definition
INSTALLED_APPS = [
'inspect_report.apps.Inspect_reportConfig',
'kronos',
'raven.contrib.django.raven_compat',
'django_extensions',
'django_filters',
'rest_framework',
'rest_framework.authtoken',
'corsheaders',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'whitenoise.runserver_nostatic',
'django.contrib.staticfiles',
# 'debug_toolbar',
]
MIDDLEWARE = [
# 'debug_toolbar.middleware.DebugToolbarMiddleware',
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'config.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'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',
'config.context_processors.footer',
],
},
},
]
WSGI_APPLICATION = 'config.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
pymysql.install_as_MySQLdb()
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': config.DB_NAME,
'USER': config.DB_USERNAME,
'PASSWORD': config.DB_PASSWORD,
'HOST': config.DB_HOST,
'PORT': config.DB_PORT,
}
}
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
# }
# }
# Password validation
# https://docs.djangoproject.com/en/2.0/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/2.0/topics/i18n/
LANGUAGE_CODE = 'zh-Hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_TZ = True
USE_L10N = True
USE_THOUSAND_SEPARATOR = True
THOUSAND_SEPARATOR = ','
NUMBER_GROUPING = 3
from django.conf.locale.zh_Hans import formats
formats.NUMBER_GROUPING = 3
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]
STATIC_URL = '/static/'
REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated'
],
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
'EXCEPTION_HANDLER': 'config.exception_handler.exception_handler',
}
JWT_AUTH = {
# 'JWT_RESPONSE_PAYLOAD_HANDLER': 'users.auth.jwt_response_payload_handler',
'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=60 * 60 * 24)
}
CORS_ORIGIN_WHITELIST = (
'http://localhost',
'http://127.0.0.1',
)
# debug tool bar settings
INTERNAL_IPS = [
# ...
'127.0.0.1',
# ...
]
import django.test.runner
# 单元测试不创建测试数据库
class MyTestRunner(django.test.runner.DiscoverRunner):
def setup_databases(self, **kwargs):
pass
def teardown_databases(self, old_config, **kwargs):
pass
TEST_RUNNER = 'config.settings.MyTestRunner'
FOOTER = "Copyright &copy; 2002-2019 北京太极华保科技股份有限公司 版权所有 (京ICP备09058794号)"
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/cache_inspect_report',
}
}
# noinspection PyBroadException
try:
release = raven.fetch_git_sha(BASE_DIR)
except:
release = "0.1"
# raven config
# RAVEN_CONFIG = {
# 'dsn': '<replace with your sentry dsn here.>',
# # If you are using git, you can also automatically configure the
# # release based on the git info.
# 'release': release,
# }
# Use telegraf to monitor your app health.
TELEGRAF_HOST = 'localhost'
TELEGRAF_PORT = 8094
TELEGRAF_TAGS = {}
"""tjccconfig URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from rest_framework.documentation import include_docs_urls
from rest_framework_jwt.views import obtain_jwt_token, verify_jwt_token, refresh_jwt_token
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include('inspect_report.urls_api_v1')),
path('api/auth/', include('rest_framework.urls')),
path('api/docs/', include_docs_urls(title='inspect_reportAPI', public=False)),
path('api/auth-jwt/', obtain_jwt_token), # POST email=email&password=password
path('api/auth-jwt-verify/', verify_jwt_token),
path('api/auth-jwt-refresh/', refresh_jwt_token),
]
if settings.DEBUG:
import debug_toolbar
urlpatterns = [
path('__debug__/', include(debug_toolbar.urls)),
# For django versions before 2.0:
# url(r'^__debug__/', include(debug_toolbar.urls)),
] + urlpatterns
"""
WSGI config for tjccconfig 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/2.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
application = get_wsgi_application()
# coding: utf-8
bind = "127.0.0.1:8099"
workers = 4
loglevel = "info"
proc_name = "inspect_report"
worker_class = "gevent"
timeout = 300
from django.contrib import admin
from django.core.exceptions import ObjectDoesNotExist
from django.shortcuts import render
from . import models as m
admin.site.site_header = 'inspect_report'
admin.site.site_title = 'inspect_report'
admin.site.index_title = '首页'
admin.site.site_url = None
# Register your models here.
# sample admin
"""
@admin.register(m.Customer)
class CustomerAdmin(admin.ModelAdmin):
list_display = ('id', 'project', 'name', 'mobile_enc', 'task_name', 'created', 'modified')
actions = ('do_uni_update_info', 'do_make_call', 'assign_task')
list_select_related = ('task',)
autocomplete_fields = ('project', 'task')
search_fields = ('name', 'mobile_enc', 'project__name', 'task__name')
def get_queryset(self, request):
if request.user.is_superuser:
return super().get_queryset(request)
else:
try:
company = request.user.userext.company
return m.Customer.objects.filter(project__company=company)
except ObjectDoesNotExist:
return m.Customer.objects.none()
def assign_task(self, req, qs):
user = req.user
company = user.userext.company
task_list = list(m.Task.objects.filter(assign_to__userext__company=company).all())
customer_list = list(qs)
if 'apply' in req.POST:
task_id = req.POST.get('task_id')
if not task_id:
self.message_user(req, '没选择项目')
return
else:
task = m.Task.objects.get(pk=task_id)
qs.update(task_id=task_id)
customerbiz.refresh_task(task)
self.message_user(req, f"分配到任务成功:{task_id}")
return
return render(req, 'admin/customer/assign_task.html', context={'tasks': task_list, 'customers': customer_list})
assign_task.short_description = '分配任务'
"""
# coding: utf-8
# from .user import UserApi
from .toolapi import ToolsApi
from .tasksapi import TasksApi
# coding: utf-8
from django.shortcuts import render
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.request import Request
from rest_framework.response import Response
from inspect_report.models import Tasks, CheckSession
import json
from datetime import datetime, timedelta
from config.config import TABLE_PRE
from inspect_report import permissions
import logging
logger = logging.getLogger(__name__)
class TasksApi(viewsets.ViewSet):
authentication_classes = ()
permission_classes = ()
# permission_classes = (permissions.ValidateToken, )
@action(['get'], detail=False)
def obtain(self, req: Request):
"""
查询任务列表
:param req:
:return:
"""
tasks = Tasks.objects.all().values('id', 'name')
# return render(req, 'inspect/index.html', {'tasks': tasks})
return Response({'code': 0, 'msg': 'success', 'data': tasks})
@action(['get'], detail=False)
def seat(self, req: Request):
"""
获取坐席列表
:param req:
:return:
"""
task_id = req.GET.get('task', '')
if not task_id:
return Response({'code': -1, 'msg': 'not select task'})
try:
task = Tasks.objects.get(pk=task_id)
except Exception as e:
logger.info(e)
return Response({'code': -2, 'msg': 'no task'})
session_collection = task.sessionCollectionId
table_name = TABLE_PRE + session_collection
tn = CheckSession.set_table(table_name)
agents = tn.objects.filter(taskId=task_id).values('agentName')
return Response({'code': 0, 'msg': 'success', 'data': agents})
@action(['post'], detail=False)
def rule(self, req: Request):
"""
获取违规项分析
:param req:
:return:
"""
task_id = req.data.get('task', '')
agent_name = req.data.get('agentName', '')
start_date = req.data.get('start_date', (datetime.now()+timedelta(days=-30)).strftime('%Y-%m-%d'))
end_date = req.data.get('end_date', (datetime.now()+timedelta(days=1)).strftime('%Y-%m-%d'))
task_condition = {'createdAt__gte': start_date, 'createdAt__lt': end_date}
if task_id:
task_condition['pk'] = task_id
tasks = Tasks.objects.filter(**task_condition)
return_data = {}
for t in tasks:
session_collection = t.sessionCollectionId
table_name = TABLE_PRE + session_collection
tn = CheckSession.set_table(table_name)
session_condition = {'taskId': t.id}
if agent_name:
session_condition['agentName'] = agent_name
checks = tn.objects.filter(**session_condition)
for check in checks:
result = check.checkResult
if result:
data = json.loads(result)
for d in data:
if 'rule' in d.keys() and 'name' in d['rule'].keys():
name = d['rule']['name']
if name in return_data.keys():
return_data[name] += 1
else:
return_data[name] = 1
return Response({'code': 0, 'msg': 'success', 'data': return_data})
@action(['post'], detail=False)
def seat_session(self, req: Request):
"""
坐席违规分析-按照会话
:param req:
:return:
"""
task_id = req.data.get('task', '')
num = req.data.get('num', '15')
start_date = req.data.get('start_date', (datetime.now()+timedelta(days=-30)).strftime('%Y-%m-%d'))
end_date = req.data.get('end_date', (datetime.now()+timedelta(days=1)).strftime('%Y-%m-%d'))
task_condition = {'createdAt__gte': start_date, 'createdAt__lt': end_date}
if task_id:
task_condition['pk'] = task_id
tasks = Tasks.objects.filter(**task_condition)
return_data = {}
for t in tasks:
session_collection = t.sessionCollectionId
table_name = TABLE_PRE + session_collection
tn = CheckSession.set_table(table_name)
session_condition = {'taskId': t.id}
checks = tn.objects.filter(**session_condition)
for check in checks:
agent_name = check.agentName
if agent_name:
if agent_name in return_data.keys():
return_data[agent_name] += 1
else:
return_data[agent_name] = 1
data_max = sorted(return_data.items(), key=lambda item: item[1], reverse=True)
data_max = data_max[:int(num)]
data_max_return = {}
for dm in data_max:
data_max_return[dm[0]] = dm[1]
return Response({'code': 0, 'msg': 'success', 'data': data_max_return})
@action(['post'], detail=False)
def seat_score(self, req: Request):
"""
坐席违规分析-按照得分
:param req:
:return:
"""
task_id = req.data.get('task', '')
num = req.data.get('num', '15')
start_date = req.data.get('start_date', (datetime.now()+timedelta(days=-30)).strftime('%Y-%m-%d'))
end_date = req.data.get('end_date', (datetime.now()+timedelta(days=1)).strftime('%Y-%m-%d'))
task_condition = {'createdAt__gte': start_date, 'createdAt__lt': end_date}
if task_id:
task_condition['pk'] = task_id
tasks = Tasks.objects.filter(**task_condition)
return_data = {}
for t in tasks:
session_collection = t.sessionCollectionId
table_name = TABLE_PRE + session_collection
tn = CheckSession.set_table(table_name)
session_condition = {'taskId': t.id}
checks = tn.objects.filter(**session_condition)
for check in checks:
agent_name = check.agentName
if agent_name:
if agent_name in return_data.keys():
return_data[agent_name] += check.score
else:
return_data[agent_name] = check.score
data_max = sorted(return_data.items(), key=lambda item: item[1], reverse=True)
data_max = data_max[:int(num)]
data_max_return = {}
for dm in data_max:
data_max_return[dm[0]] = dm[1]
return Response({'code': 0, 'msg': 'success', 'data': data_max_return})
# coding: utf-8
# coding: utf-8
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.request import Request
from rest_framework.response import Response
from telegraf.defaults.django import telegraf
from ..utils.logtools import TimeIt
class ToolsApi(viewsets.ViewSet):
authentication_classes = ()
permission_classes = ()
@action(['get'], detail=False)
def hello(self, req: Request):
"""Sample code
"""
with TimeIt() as timeit:
code = 0
telegraf.metric('hello', {'duration': timeit.duration}, {'code': code})
return Response({'code': code, 'msg': 'success'})
# coding: utf-8
import coreapi
from django.db import connection
from rest_framework import serializers, viewsets, decorators
from rest_framework.response import Response
from rest_framework.schemas import AutoSchema
from urllib import parse
from django.contrib.auth.models import User
from .. import models as m
"""sample
# coding: utf-8
from rest_framework import serializers, viewsets, decorators, request, response, status
from mgcut import mgcut
class MangoDialSerializer(serializers.Serializer):
mobile = serializers.CharField(max_length=16)
name = serializers.CharField(max_length=16)
class MangoApi(viewsets.ViewSet):
serializers = MangoDialSerializer
permission_classes = ()
@decorators.action(['POST'], detail=False)
def dial(self, req: request.Request):
serializer = MangoDialSerializer(data=req.data)
if serializer.is_valid():
try:
mgcut.go(serializer.data['mobile'], serializer.data['name'])
return response.Response({'code': 0, 'msg': '拨叫成功'})
except Exception as e:
return response.Response({'code': -1, 'msg': str(e)})
else:
return response.Response({'code': -2, 'msg': '参数非法', 'data': serializer.errors},
status=status.HTTP_400_BAD_REQUEST)
class UserExtSerializer(serializers.ModelSerializer):
class Meta:
model = m.UserExt
fields = ('company',)
class UserSerializer(serializers.ModelSerializer):
userext = UserExtSerializer(
many=False,
read_only=True,
)
class Meta:
model = m.User
fields = ('id', 'username', 'userext')
class UserApi(viewsets.ReadOnlyModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
schema = CustomSchema()
@decorators.action(['GET'], detail=False)
def current(self, request):
user: User = request.user
userext = request.user.userext if hasattr(request.user, 'userext') else None
groups = [i.name for i in user.groups.all()]
return Response({'code': 0, 'message': '调用成功', 'data': {
'id': user.id,
'username': user.username,
'tel': '',
'role': groups
}})
@decorators.action(['POST'], detail=False)
def logout(self, request):
user: User = request.user
return Response({
'code': 0, 'message': '登出成功'
})
"""
from django.apps import AppConfig
class Inspect_reportConfig(AppConfig):
name = 'inspect_report'
verbose_name = 'inspect_report'
# coding: utf-8
"""定时任务
参考:https://github.com/jgorset/django-kronos
"""
# coding: utf-8
from rest_framework.exceptions import APIException
class Inspect_reportException(APIException):
pass
from django.db import models
# Create your models here.
class Tasks(models.Model):
name = models.CharField('任务名称', null=True, max_length=50, db_index=True)
type = models.IntegerField('任务分类', default=1)
sessionCollectionId = models.CharField('质检任务对应的对话集合', null=True, max_length=500)
keywords = models.CharField('检查包含此关键词的会话', max_length=500)
sessionCount = models.IntegerField('任务包含的会话数量', default=0)
totalData = models.CharField('本任务质检结果的统计数据', null=True, max_length=500)
createUser = models.CharField('创建人', null=True, max_length=100)
hasCheck = models.IntegerField('是否质检', default=0)
rules = models.TextField('选择的质检条件')
dataSource = models.CharField('数据源', max_length=2000)
extra = models.CharField('额外信息', max_length=2000)
createdAt = models.DateTimeField('创建时间', null=True)
updatedAt = models.DateTimeField('更新时间', null=True)
def __str__(self):
return self.name
class Meta:
db_table = 'check_tasks'
class CheckSession(models.Model):
taskId = models.IntegerField('质检任务标识')
sessionId = models.CharField('会话唯一标识')
sessionTags = models.CharField('会话标签', max_length=500)
startTime = models.CharField('会话开始时间', null=True)
closeTime = models.CharField('会话结束时间', null=True)
remainTime = models.IntegerField('会话持续时间', default=0)
agentNo = models.CharField('坐席编号')
agentGroup = models.CharField('坐席分组')
agentName = models.CharField('坐席名称')
customName = models.CharField('客户名称')
customLocal = models.CharField('客户地区')
customReview = models.CharField('客户评价')
closeByAgent = models.IntegerField('是否是客户关闭', default=0)
matchKeywords = models.IntegerField('是否需要检查关键词', default=0)
hasCheck = models.IntegerField('是否已完成检查', default=0)
score = models.FloatField('对当前会话的评分', default=0)
missNodeCount = models.IntegerField('遗漏节点数', default=0)
violationRuleCount = models.IntegerField('当前质检会话的违规数', default=0)
violationRules = models.CharField('违规规则列表', max_length=5000)
checkResult = models.TextField('质检结果')
processCheckResult = models.TextField('流程质检结果')
note = models.CharField('标签分类,备注建议', max_length=500)
conversationCount = models.IntegerField('此会话包含的消息数量', default=0)
conversationList = models.TextField('此会话包含的对话消息')
extra = models.CharField('此会话包含的额外消息', max_length=2000)
scoreItemRecord = models.TextField('记录每个评分项的得分情况')
createdAt = models.DateTimeField('创建时间', null=True)
updatedAt = models.DateTimeField('更新时间', null=True)
def __str__(self):
return self.sessionId
class Meta:
abstract = True
@classmethod
def set_table(Class, table_name):
class Meta:
db_table = table_name
attrs = {
'__module__': Class.__module__,
'Meta': Meta
}
return type(table_name, (Class,), attrs)
# -*- coding: utf-8 -*-
import logging
from rest_framework.permissions import BasePermission
from rest_framework.authentication import exceptions
log = logging.getLogger(__name__)
class ValidateToken(BasePermission):
""" token 正确才可以调用 """
def has_permission(self, request, view):
try:
http_auth = request.GET.get('token', '')
if http_auth:
# token = http_auth.split(' ')[1]
# if http_auth.split(' ')[0] != 'Token':
# log.error('token 认证异常 | HTTP_WXAUTH 中缺少 Token')
# raise exceptions.AuthenticationFailed(detail={'code': -5, 'msg': 'token 认证异常'})
# key = request.session.get(token, None)
# if not key:
# log.error('token 认证异常 | Token不存在或者已过期')
# raise exceptions.AuthenticationFailed(detail={'code': -5, 'msg': '无效的token'})
return True
except KeyError as ke:
log.error('token 认证异常 | header中缺少HTTP_WXAUTH | 异常信息 : KeyError : %s', str(ke))
raise exceptions.AuthenticationFailed(detail={'code': -5, 'msg': 'token 认证异常'})
except IndexError as ie:
log.error('token 认证异常 | HTTP_WXAUTH 中值错误 : %s | 异常信息 : IndexError : %s',
request.META['HTTP_WXAUTH'], str(ie))
raise exceptions.AuthenticationFailed(detail={'code': -5, 'msg': 'token 认证异常'})
except Exception as e:
log.error('token 认证异常 : %s', str(e))
raise exceptions.AuthenticationFailed(detail={'code': -5, 'msg': 'token 认证异常'})
raise exceptions.AuthenticationFailed(detail={'code': -5, 'msg': 'token 认证异常'})
<!DOCTYPE html>
{% load static %}
<html lang="en">
<head>
<meta charset='utf-8' />
<title>语音坐席</title>
<!-- 引入样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<!-- 引入组件库 -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<link rel='stylesheet' type='text/css' href="{% static "inspect/css/all.css" %}">
</head>
<body style='height:100%'>
<div class='child-title'>语音坐席</div>
<div class='child-main' id='childApp'>
<div style='text-align:right;' class='childTitle'>
<div class='childTitleName'>质检违规统计</div>
<el-date-picker
v-model="dateRange"
@change='changeRange'
clearable='false'
type="daterange"
value-format='yyyy-MM-dd'
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']">
</el-date-picker>
<el-select v-model="taskvalue" placeholder="请选择" @change="changeTask">
<el-option
v-for="item in taskList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
<el-select v-model="takevalue" placeholder="请选择" @change='changeTake'>
<el-option
v-for="item in takeList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<div id='main' style='width:80%;height:600px'></div>
</div>
<script type="text/javascript">
window.onload = function(){
var option = {
dataZoom : [
{
type: 'slider',
show: true,
start: 50,
// orient: 'horizontal',
orient: 'vertical',
zoomLock: true,
end: 100,
handleSize: 8
},
{
type: 'inside',
start: 50,
orient: 'vertical',
zoomLock: true,
// zoomOnMouseWheel:false,
end: 100
},
{
type: 'slider',
show: true,
xAxisIndex: 0,
filterMode: 'empty',
width: 12,
// height: '70%',
handleSize: 8,
orient: 'vertical',
zoomLock: true,
showDataShadow: false,
left: '93%'
}
],
dataset: {
source: [
[ 'amount', 'product'],
[2, 'Matcha Latte'],
[4, 'Milk Tea'],
[2, 'Cheese Cocoa'],
[5, 'Cheese Brownie'],
[5, 'Matcha Cocoa'],
[6, 'Tea'],
[2, 'Orange Juice'],
[52, 'Lemon Juice'],
[2, 'Walnut Brownie'],
[2, 'Walnut Brownie1'],
[2, 'Walnut Brownie2'],
[2, 'Walnut Brownie3'],
[2, 'Walnut Brownie4'],
[2, 'Walnut Brownie5'],
[2, 'Walnut Brownie6'],
[2, 'Walnut Brownie7'],
[2, 'Walnut Brownie8'],
[2, 'Walnut Brownie9'],
[2, 'Walnut Brownie91'],
[2, 'Walnut Brownie92'],
[2, 'Walnut Brownie93'],
[2, 'Walnut Brownie94'],
[2, 'Walnut Brownie95'],
[2, 'Walnut Brownie96'],
[2, 'Walnut Brownie97'],
]
},
grid: {containLabel: true},
xAxis: {name: 'amount'},
yAxis: {type: 'category',boundaryGap:'20%'},
series: [
{
type: 'bar',
barWidth: 20,
barCategoryGap: "20%",
label:{
normal: {
position: 'right',
show: true,
color: '#000'
}
},
itemStyle:{
normal:{
color: '#fccb04'
},
emphasis:{
color: '#f7d139'
}
},
encode: {
x: 'amount',
y: 'product'
}
}
]
};
var myChart = echarts.init(document.getElementById('main'));
myChart.setOption(option)
window.onresize = function(){
myChart.resize();
}
};
</script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="{% static "inspect/js/echarts.js" %}"></script>
<script type="text/javascript" src="{% static "inspect/js/all.js" %}"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>
"""tjccconfig URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]
"""tjccconfig URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.urls import path, include
from rest_framework import routers
from . import api
# todo: 以下接口未设置权限隔离,用户可以看到所有数据。正式上线需要屏蔽多余的接口(list/retrieve)
router = routers.DefaultRouter()
router.register(r'tools', api.ToolsApi, base_name='tools')
router.register(r'tasks', api.TasksApi, base_name='tasks')
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
path('', include(router.urls)),
]
# coding: utf-8
import time
class TimeIt(object):
"""计时器。
用法::
with httplog.TimeIt() as timeit:
res = requests.post(url, json=payload)
httplog.http_log_from_response('upload_case', res, timeit.duration)
"""
def __init__(self):
self.start = 0
self.end = 0
def __enter__(self):
self.start = time.time()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.end = time.time()
@property
def duration(self):
return int((self.end - self.start) * 1000)
from django.shortcuts import render
# Create your views here.
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
/*
DJANGO Admin styles
*/
@import url(fonts.css);
body {
margin: 0;
padding: 0;
font-size: 14px;
font-family: "Roboto","Lucida Grande","DejaVu Sans","Bitstream Vera Sans",Verdana,Arial,sans-serif;
color: #333;
background: #fff;
}
/* LINKS */
a:link, a:visited {
color: #447e9b;
text-decoration: none;
}
a:focus, a:hover {
color: #036;
}
a:focus {
text-decoration: underline;
}
a img {
border: none;
}
a.section:link, a.section:visited {
color: #fff;
text-decoration: none;
}
a.section:focus, a.section:hover {
text-decoration: underline;
}
/* GLOBAL DEFAULTS */
p, ol, ul, dl {
margin: .2em 0 .8em 0;
}
p {
padding: 0;
line-height: 140%;
}
h1,h2,h3,h4,h5 {
font-weight: bold;
}
h1 {
margin: 0 0 20px;
font-weight: 300;
font-size: 20px;
color: #666;
}
h2 {
font-size: 16px;
margin: 1em 0 .5em 0;
}
h2.subhead {
font-weight: normal;
margin-top: 0;
}
h3 {
font-size: 14px;
margin: .8em 0 .3em 0;
color: #666;
font-weight: bold;
}
h4 {
font-size: 12px;
margin: 1em 0 .8em 0;
padding-bottom: 3px;
}
h5 {
font-size: 10px;
margin: 1.5em 0 .5em 0;
color: #666;
text-transform: uppercase;
letter-spacing: 1px;
}
ul li {
list-style-type: square;
padding: 1px 0;
}
li ul {
margin-bottom: 0;
}
li, dt, dd {
font-size: 13px;
line-height: 20px;
}
dt {
font-weight: bold;
margin-top: 4px;
}
dd {
margin-left: 0;
}
form {
margin: 0;
padding: 0;
}
fieldset {
margin: 0;
padding: 0;
border: none;
border-top: 1px solid #eee;
}
blockquote {
font-size: 11px;
color: #777;
margin-left: 2px;
padding-left: 10px;
border-left: 5px solid #ddd;
}
code, pre {
font-family: "Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace;
color: #666;
font-size: 12px;
}
pre.literal-block {
margin: 10px;
background: #eee;
padding: 6px 8px;
}
code strong {
color: #930;
}
hr {
clear: both;
color: #eee;
background-color: #eee;
height: 1px;
border: none;
margin: 0;
padding: 0;
font-size: 1px;
line-height: 1px;
}
/* TEXT STYLES & MODIFIERS */
.small {
font-size: 11px;
}
.tiny {
font-size: 10px;
}
p.tiny {
margin-top: -2px;
}
.mini {
font-size: 10px;
}
p.mini {
margin-top: -3px;
}
.help, p.help, form p.help, div.help, form div.help, div.help li {
font-size: 11px;
color: #999;
}
div.help ul {
margin-bottom: 0;
}
.help-tooltip {
cursor: help;
}
p img, h1 img, h2 img, h3 img, h4 img, td img {
vertical-align: middle;
}
.quiet, a.quiet:link, a.quiet:visited {
color: #999;
font-weight: normal;
}
.float-right {
float: right;
}
.float-left {
float: left;
}
.clear {
clear: both;
}
.align-left {
text-align: left;
}
.align-right {
text-align: right;
}
.example {
margin: 10px 0;
padding: 5px 10px;
background: #efefef;
}
.nowrap {
white-space: nowrap;
}
/* TABLES */
table {
border-collapse: collapse;
border-color: #ccc;
}
td, th {
font-size: 13px;
line-height: 16px;
border-bottom: 1px solid #eee;
vertical-align: top;
padding: 8px;
font-family: "Roboto", "Lucida Grande", Verdana, Arial, sans-serif;
}
th {
font-weight: 600;
text-align: left;
}
thead th,
tfoot td {
color: #666;
padding: 5px 10px;
font-size: 11px;
background: #fff;
border: none;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
}
tfoot td {
border-bottom: none;
border-top: 1px solid #eee;
}
thead th.required {
color: #000;
}
tr.alt {
background: #f6f6f6;
}
.row1 {
background: #fff;
}
.row2 {
background: #f9f9f9;
}
/* SORTABLE TABLES */
thead th {
padding: 5px 10px;
line-height: normal;
text-transform: uppercase;
background: #f6f6f6;
}
thead th a:link, thead th a:visited {
color: #666;
}
thead th.sorted {
background: #eee;
}
thead th.sorted .text {
padding-right: 42px;
}
table thead th .text span {
padding: 8px 10px;
display: block;
}
table thead th .text a {
display: block;
cursor: pointer;
padding: 8px 10px;
}
table thead th .text a:focus, table thead th .text a:hover {
background: #eee;
}
thead th.sorted a.sortremove {
visibility: hidden;
}
table thead th.sorted:hover a.sortremove {
visibility: visible;
}
table thead th.sorted .sortoptions {
display: block;
padding: 9px 5px 0 5px;
float: right;
text-align: right;
}
table thead th.sorted .sortpriority {
font-size: .8em;
min-width: 12px;
text-align: center;
vertical-align: 3px;
margin-left: 2px;
margin-right: 2px;
}
table thead th.sorted .sortoptions a {
position: relative;
width: 14px;
height: 14px;
display: inline-block;
background: url(../img/sorting-icons.svg) 0 0 no-repeat;
background-size: 14px auto;
}
table thead th.sorted .sortoptions a.sortremove {
background-position: 0 0;
}
table thead th.sorted .sortoptions a.sortremove:after {
content: '\\';
position: absolute;
top: -6px;
left: 3px;
font-weight: 200;
font-size: 18px;
color: #999;
}
table thead th.sorted .sortoptions a.sortremove:focus:after,
table thead th.sorted .sortoptions a.sortremove:hover:after {
color: #447e9b;
}
table thead th.sorted .sortoptions a.sortremove:focus,
table thead th.sorted .sortoptions a.sortremove:hover {
background-position: 0 -14px;
}
table thead th.sorted .sortoptions a.ascending {
background-position: 0 -28px;
}
table thead th.sorted .sortoptions a.ascending:focus,
table thead th.sorted .sortoptions a.ascending:hover {
background-position: 0 -42px;
}
table thead th.sorted .sortoptions a.descending {
top: 1px;
background-position: 0 -56px;
}
table thead th.sorted .sortoptions a.descending:focus,
table thead th.sorted .sortoptions a.descending:hover {
background-position: 0 -70px;
}
/* FORM DEFAULTS */
input, textarea, select, .form-row p, form .button {
margin: 2px 0;
padding: 2px 3px;
vertical-align: middle;
font-family: "Roboto", "Lucida Grande", Verdana, Arial, sans-serif;
font-weight: normal;
font-size: 13px;
}
.form-row div.help {
padding: 2px 3px;
}
textarea {
vertical-align: top;
}
input[type=text], input[type=password], input[type=email], input[type=url],
input[type=number], input[type=tel], textarea, select, .vTextField {
border: 1px solid #ccc;
border-radius: 4px;
padding: 5px 6px;
margin-top: 0;
}
input[type=text]:focus, input[type=password]:focus, input[type=email]:focus,
input[type=url]:focus, input[type=number]:focus, input[type=tel]:focus,
textarea:focus, select:focus, .vTextField:focus {
border-color: #999;
}
select {
height: 30px;
}
select[multiple] {
min-height: 150px;
}
/* FORM BUTTONS */
.button, input[type=submit], input[type=button], .submit-row input, a.button {
background: #79aec8;
padding: 10px 15px;
border: none;
border-radius: 4px;
color: #fff;
cursor: pointer;
}
a.button {
padding: 4px 5px;
}
.button:active, input[type=submit]:active, input[type=button]:active,
.button:focus, input[type=submit]:focus, input[type=button]:focus,
.button:hover, input[type=submit]:hover, input[type=button]:hover {
background: #609ab6;
}
.button[disabled], input[type=submit][disabled], input[type=button][disabled] {
opacity: 0.4;
}
.button.default, input[type=submit].default, .submit-row input.default {
float: right;
border: none;
font-weight: 400;
background: #417690;
}
.button.default:active, input[type=submit].default:active,
.button.default:focus, input[type=submit].default:focus,
.button.default:hover, input[type=submit].default:hover {
background: #205067;
}
.button[disabled].default,
input[type=submit][disabled].default,
input[type=button][disabled].default {
opacity: 0.4;
}
/* MODULES */
.module {
border: none;
margin-bottom: 30px;
background: #fff;
}
.module p, .module ul, .module h3, .module h4, .module dl, .module pre {
padding-left: 10px;
padding-right: 10px;
}
.module blockquote {
margin-left: 12px;
}
.module ul, .module ol {
margin-left: 1.5em;
}
.module h3 {
margin-top: .6em;
}
.module h2, .module caption, .inline-group h2 {
margin: 0;
padding: 8px;
font-weight: 400;
font-size: 13px;
text-align: left;
background: #79aec8;
color: #fff;
}
.module caption,
.inline-group h2 {
font-size: 12px;
letter-spacing: 0.5px;
text-transform: uppercase;
}
.module table {
border-collapse: collapse;
}
/* MESSAGES & ERRORS */
ul.messagelist {
padding: 0;
margin: 0;
}
ul.messagelist li {
display: block;
font-weight: 400;
font-size: 13px;
padding: 10px 10px 10px 65px;
margin: 0 0 10px 0;
background: #dfd url(../img/icon-yes.svg) 40px 12px no-repeat;
background-size: 16px auto;
color: #333;
}
ul.messagelist li.warning {
background: #ffc url(../img/icon-alert.svg) 40px 14px no-repeat;
background-size: 14px auto;
}
ul.messagelist li.error {
background: #ffefef url(../img/icon-no.svg) 40px 12px no-repeat;
background-size: 16px auto;
}
.errornote {
font-size: 14px;
font-weight: 700;
display: block;
padding: 10px 12px;
margin: 0 0 10px 0;
color: #ba2121;
border: 1px solid #ba2121;
border-radius: 4px;
background-color: #fff;
background-position: 5px 12px;
}
ul.errorlist {
margin: 0 0 4px;
padding: 0;
color: #ba2121;
background: #fff;
}
ul.errorlist li {
font-size: 13px;
display: block;
margin-bottom: 4px;
}
ul.errorlist li:first-child {
margin-top: 0;
}
ul.errorlist li a {
color: inherit;
text-decoration: underline;
}
td ul.errorlist {
margin: 0;
padding: 0;
}
td ul.errorlist li {
margin: 0;
}
.form-row.errors {
margin: 0;
border: none;
border-bottom: 1px solid #eee;
background: none;
}
.form-row.errors ul.errorlist li {
padding-left: 0;
}
.errors input, .errors select, .errors textarea {
border: 1px solid #ba2121;
}
div.system-message {
background: #ffc;
margin: 10px;
padding: 6px 8px;
font-size: .8em;
}
div.system-message p.system-message-title {
padding: 4px 5px 4px 25px;
margin: 0;
color: #c11;
background: #ffefef url(../img/icon-no.svg) 5px 5px no-repeat;
}
.description {
font-size: 12px;
padding: 5px 0 0 12px;
}
/* BREADCRUMBS */
div.breadcrumbs {
background: #79aec8;
padding: 10px 40px;
border: none;
font-size: 14px;
color: #c4dce8;
text-align: left;
}
div.breadcrumbs a {
color: #fff;
}
div.breadcrumbs a:focus, div.breadcrumbs a:hover {
color: #c4dce8;
}
/* ACTION ICONS */
.viewlink, .inlineviewlink {
padding-left: 16px;
background: url(../img/icon-viewlink.svg) 0 1px no-repeat;
}
.addlink {
padding-left: 16px;
background: url(../img/icon-addlink.svg) 0 1px no-repeat;
}
.changelink, .inlinechangelink {
padding-left: 16px;
background: url(../img/icon-changelink.svg) 0 1px no-repeat;
}
.deletelink {
padding-left: 16px;
background: url(../img/icon-deletelink.svg) 0 1px no-repeat;
}
a.deletelink:link, a.deletelink:visited {
color: #CC3434;
}
a.deletelink:focus, a.deletelink:hover {
color: #993333;
text-decoration: none;
}
/* OBJECT TOOLS */
.object-tools {
font-size: 10px;
font-weight: bold;
padding-left: 0;
float: right;
position: relative;
margin-top: -48px;
}
.form-row .object-tools {
margin-top: 5px;
margin-bottom: 5px;
float: none;
height: 2em;
padding-left: 3.5em;
}
.object-tools li {
display: block;
float: left;
margin-left: 5px;
height: 16px;
}
.object-tools a {
border-radius: 15px;
}
.object-tools a:link, .object-tools a:visited {
display: block;
float: left;
padding: 3px 12px;
background: #999;
font-weight: 400;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #fff;
}
.object-tools a:focus, .object-tools a:hover {
background-color: #417690;
}
.object-tools a:focus{
text-decoration: none;
}
.object-tools a.viewsitelink, .object-tools a.golink,.object-tools a.addlink {
background-repeat: no-repeat;
background-position: right 7px center;
padding-right: 26px;
}
.object-tools a.viewsitelink, .object-tools a.golink {
background-image: url(../img/tooltag-arrowright.svg);
}
.object-tools a.addlink {
background-image: url(../img/tooltag-add.svg);
}
/* OBJECT HISTORY */
table#change-history {
width: 100%;
}
table#change-history tbody th {
width: 16em;
}
/* PAGE STRUCTURE */
#container {
position: relative;
width: 100%;
min-width: 980px;
padding: 0;
}
#content {
padding: 20px 40px;
}
.dashboard #content {
width: 600px;
}
#content-main {
float: left;
width: 100%;
}
#content-related {
float: right;
width: 260px;
position: relative;
margin-right: -300px;
}
#footer {
clear: both;
padding: 10px;
text-align: center;
color: #999;
}
/* COLUMN TYPES */
.colMS {
margin-right: 300px;
}
.colSM {
margin-left: 300px;
}
.colSM #content-related {
float: left;
margin-right: 0;
margin-left: -300px;
}
.colSM #content-main {
float: right;
}
.popup .colM {
width: auto;
}
/* HEADER */
#header {
width: auto;
height: 40px;
padding: 10px 40px;
background: #417690;
line-height: 40px;
color: #ffc;
overflow: hidden;
}
#header a:link, #header a:visited {
color: #fff;
}
#header a:focus , #header a:hover {
text-decoration: underline;
}
#branding {
float: left;
}
#branding h1 {
padding: 0;
margin: 0 20px 0 0;
font-weight: 300;
font-size: 24px;
color: #f5dd5d;
}
#branding h1, #branding h1 a:link, #branding h1 a:visited {
color: #f5dd5d;
}
#branding h2 {
padding: 0 10px;
font-size: 14px;
margin: -8px 0 8px 0;
font-weight: normal;
color: #ffc;
}
#branding a:hover {
text-decoration: none;
}
#user-tools {
float: right;
padding: 0;
margin: 0 0 0 20px;
font-weight: 300;
font-size: 11px;
letter-spacing: 0.5px;
text-transform: uppercase;
text-align: right;
}
#user-tools a {
border-bottom: 1px solid rgba(255, 255, 255, 0.25);
}
#user-tools a:focus, #user-tools a:hover {
text-decoration: none;
border-bottom-color: #79aec8;
color: #79aec8;
}
/* SIDEBAR */
#content-related {
background: #f8f8f8;
}
#content-related .module {
background: none;
}
#content-related h3 {
font-size: 14px;
color: #666;
padding: 0 16px;
margin: 0 0 16px;
}
#content-related h4 {
font-size: 13px;
}
#content-related p {
padding-left: 16px;
padding-right: 16px;
}
#content-related .actionlist {
padding: 0;
margin: 16px;
}
#content-related .actionlist li {
line-height: 1.2;
margin-bottom: 10px;
padding-left: 18px;
}
#content-related .module h2 {
background: none;
padding: 16px;
margin-bottom: 16px;
border-bottom: 1px solid #eaeaea;
font-size: 18px;
color: #333;
}
.delete-confirmation form input[type="submit"] {
background: #ba2121;
border-radius: 4px;
padding: 10px 15px;
color: #fff;
}
.delete-confirmation form input[type="submit"]:active,
.delete-confirmation form input[type="submit"]:focus,
.delete-confirmation form input[type="submit"]:hover {
background: #a41515;
}
.delete-confirmation form .cancel-link {
display: inline-block;
vertical-align: middle;
height: 15px;
line-height: 15px;
background: #ddd;
border-radius: 4px;
padding: 10px 15px;
color: #333;
margin: 0 0 0 10px;
}
.delete-confirmation form .cancel-link:active,
.delete-confirmation form .cancel-link:focus,
.delete-confirmation form .cancel-link:hover {
background: #ccc;
}
/* POPUP */
.popup #content {
padding: 20px;
}
.popup #container {
min-width: 0;
}
.popup #header {
padding: 10px 20px;
}
/* 定制部分 */
#result_list .field-file_size {
text-align: right;
}
html,body{
height: 100%;
}
body{
padding: 30px;
background: #f7f7f7;
margin:0;
min-width: 850px;
}
#main{
margin: 0 auto;
}
.child-title{
color: rgb(51, 51, 51);
font-weight: 600;
font-size: 18px;
height: auto;
line-height: 18px;
display: inline-block;
padding-bottom: 18px;
border-bottom:2px solid rgb(252,203,4);
margin-bottom: 20px;
}
.child-main{
background-color: #fff;
padding: 30px;
}
.el-date-editor .el-range-input, .el-date-editor .el-range-separator{
font-size: 13px;
width: 76px;
}
.el-range-editor.el-input__inner{
width: 240px;
height: 30px;
border-radius: 0px;
}
.el-date-editor .el-range__icon{
margin-left: 0;
width: 32px;
line-height: 25px;
font-size: 16px;
}
.el-date-editor .el-range-separator{
line-height: 20px;
}
.el-date-editor .el-range__close-icon{
display: none !important;
}
.el-range-editor.el-input__inner:focus, .el-range-editor.el-input__inner:hover{
border-color: #fccb04 !important;
}
.el-range-editor.is-active, .el-range-editor.is-active:hover{
border-color: #fccb04 !important;
}
.el-date-table td.end-date span, .el-date-table td.start-date span{
background-color: #fccb04 !important;
color:#000;
}
.el-date-table td span{
border-radius: 0;
}
.el-date-table td.in-range div{
background-color: #fccb04 !important;
color:#000;
border-radius: 0;
}
.el-date-table td.today span{
background-color: #fccb04 !important;
color:rgba(51,51,51,.2);
}
.el-input .el-input__inner{
height: 30px!important;
line-height: 30px;
border-radius: 2px;
border: 1px solid #ddd;
padding-left: 8px;
}
.el-select .el-input__inner:focus,.el-select .el-input.is-focus .el-input__inner{
border:1px solid #ddd;
}
.el-input{
font-size: 13px;
}
.el-input__suffix{
/*top:4px !important;*/
}
.el-select-dropdown__item.selected{
color: #000;
}
.el-input__icon{
/*height: auto;*/
}
.el-icon-arrow-up:before {
content: "\e6e1";
position: absolute;
top: -5px;
left: 5px;
}
.childTitle > div{
vertical-align: top;
}
.childTitleName{
display: inline-block;
font-size: 16px;
font-weight: bold;
height:30px;
line-height: 30px;
float: left;
}
\ No newline at end of file
new Vue({
el: '#childApp',
data:function(){
return {
ss:'sas',
dateRange:[new Date(), new Date()],
taskList:[{
value: '选项1',
label: '黄金糕'
}, {
value: '选项2',
label: '双皮奶'
}, {
value: '选项3',
label: '蚵仔煎'
}, {
value: '选项4',
label: '龙须面'
}, {
value: '选项5',
label: '北京烤鸭'
}],
taskvalue: '黄金糕',
takeList:[{
value: '选项1',
label: '黄金糕'
}, {
value: '选项2',
label: '双皮奶'
}, {
value: '选项3',
label: '蚵仔煎'
}, {
value: '选项4',
label: '龙须面'
}, {
value: '选项5',
label: '北京烤鸭'
}],
takevalue: '黄金糕'
}
},
methods: {
changeRange(dateRange){
console.log(dateRange);
},
changeTask(msg){
console.log(msg);
},
changeTake(msg){
console.log(msg);
}
}
})
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
{% extends 'admin/app_index.html' %}
{% load i18n static %}<!DOCTYPE html>
{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
<html lang="{{ LANGUAGE_CODE|default:"en-us" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}>
<head>
<title>{% block title %}{% endblock %}</title>
<link rel="stylesheet" type="text/css" href="{% block stylesheet %}{% static "admin/css/base.css" %}{% endblock %}">
{% block extrastyle %}{% endblock %}
{% if LANGUAGE_BIDI %}<link rel="stylesheet" type="text/css" href="{% block stylesheet_rtl %}{% static "admin/css/rtl.css" %}{% endblock %}">{% endif %}
{% block extrahead %}{% endblock %}
{% block responsive %}
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0">
<link rel="stylesheet" type="text/css" href="{% static "admin/css/responsive.css" %}">
{% if LANGUAGE_BIDI %}<link rel="stylesheet" type="text/css" href="{% static "admin/css/responsive_rtl.css" %}">{% endif %}
{% endblock %}
{% block blockbots %}<meta name="robots" content="NONE,NOARCHIVE">{% endblock %}
</head>
{% load i18n %}
<body class="{% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}"
data-admin-utc-offset="{% now "Z" %}">
<!-- Container -->
<div id="container">
{% if not is_popup %}
<!-- Header -->
<div id="header">
<div id="branding">
{% block branding %}{% endblock %}
</div>
{% block usertools %}
{% if has_permission %}
<div id="user-tools">
{% block welcome-msg %}
{% trans 'Welcome,' %}
<strong>{% firstof user.get_short_name user.get_username %}</strong>.
{% endblock %}
{% block userlinks %}
{% if site_url %}
<a href="{{ site_url }}">{% trans 'View site' %}</a> /
{% endif %}
{% if user.is_active and user.is_staff %}
{% url 'django-admindocs-docroot' as docsroot %}
{% if docsroot %}
<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> /
{% endif %}
{% endif %}
{% if user.has_usable_password %}
<a href="{% url 'admin:password_change' %}">{% trans 'Change password' %}</a> /
{% endif %}
<a href="{% url 'admin:logout' %}">{% trans 'Log out' %}</a>
{% endblock %}
</div>
{% endif %}
{% endblock %}
{% block nav-global %}{% endblock %}
</div>
<!-- END Header -->
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
{% if title %} &rsaquo; {{ title }}{% endif %}
</div>
{% endblock %}
{% endif %}
{% block messages %}
{% if messages %}
<ul class="messagelist">{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message|capfirst }}</li>
{% endfor %}</ul>
{% endif %}
{% endblock messages %}
<!-- Content -->
<div id="content" class="{% block coltype %}colM{% endblock %}">
{% block pretitle %}{% endblock %}
{% block content_title %}{% if title %}<h1>{{ title }}</h1>{% endif %}{% endblock %}
{% block content %}
{% block object-tools %}{% endblock %}
{{ content }}
{% endblock %}
{% block sidebar %}{% endblock %}
<br class="clear">
</div>
<!-- END Content -->
{% block footer %}<div id="footer">{% autoescape off %}{{ footer }}{% endautoescape %}</div>{% endblock %}
</div>
<!-- END Container -->
{% block js %}
{% endblock %}
</body>
</html>
{% extends "admin/base_site.html" %}
{% load i18n static %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/dashboard.css" %}">{% endblock %}
{% block coltype %}colMS{% endblock %}
{% block bodyclass %}{{ block.super }} dashboard{% endblock %}
{% block breadcrumbs %}{% endblock %}
{% block content %}
<div id="content-main">
{% if app_list %}
{% for app in app_list %}
<div class="app-{{ app.app_label }} module">
<table>
<caption>
<a href="{{ app.app_url }}" class="section" title="{% blocktrans with name=app.name %}Models in the {{ name }} application{% endblocktrans %}">{{ app.name }}</a>
</caption>
{% for model in app.models %}
<tr class="model-{{ model.object_name|lower }}">
{% if model.admin_url %}
<th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
{% else %}
<th scope="row">{{ model.name }}</th>
{% endif %}
{% if model.admin_url %}
<td><a href="{{ model.admin_url }}" class="viewlink">进入</a></td>
{% else %}
<td>&nbsp;</td>
{% endif %}
</tr>
{% endfor %}
</table>
</div>
{% endfor %}
{% else %}
<p>{% trans "You don't have permission to view or edit anything." %}</p>
{% endif %}
</div>
{% endblock %}
{% block sidebar %}
{#<div id="content-related">#}
{# <div class="module" id="recent-actions-module">#}
{# <h2>{% trans 'Recent actions' %}</h2>#}
{# <h3>{% trans 'My actions' %}</h3>#}
{# {% load log %}#}
{# {% get_admin_log 10 as admin_log for_user user %}#}
{# {% if not admin_log %}#}
{# <p>{% trans 'None available' %}</p>#}
{# {% else %}#}
{# <ul class="actionlist">#}
{# {% for entry in admin_log %}#}
{# <li class="{% if entry.is_addition %}addlink{% endif %}{% if entry.is_change %}changelink{% endif %}{% if entry.is_deletion %}deletelink{% endif %}">#}
{# {% if entry.is_deletion or not entry.get_admin_url %}#}
{# {{ entry.object_repr }}#}
{# {% else %}#}
{# <a href="{{ entry.get_admin_url }}">{{ entry.object_repr }}</a>#}
{# {% endif %}#}
{# <br>#}
{# {% if entry.content_type %}#}
{# <span class="mini quiet">{% filter capfirst %}{{ entry.content_type }}{% endfilter %}</span>#}
{# {% else %}#}
{# <span class="mini quiet">{% trans 'Unknown content' %}</span>#}
{# {% endif %}#}
{# </li>#}
{# {% endfor %}#}
{# </ul>#}
{# {% endif %}#}
{# </div>#}
{#</div>#}
{% endblock %}
{% extends 'admin/change_list.html' %}
{% load i18n admin_urls static admin_list %}
{% block object-tools-items %}
{{ block.super }}
{% endblock %}
{% extends 'admin/base_site.html' %}
{% load i18n admin_urls static admin_list %}
{% block content %}
<form action="" method="POST">
{% csrf_token %}
<div>
<fieldset class="module aligned">
<div class="form-row">
<div>
<label>文件:
</label>
<ul style="list-style-type: none;">
{% for file in file_list %}
<li style="">
{{ file.file_name }}
<input type="hidden" name="_selected_action" value="{{ file.pk }}"/>
</li>
{% endfor %}
</ul>
</div>
</div>
<div class="form-row">
<div>
<label class="required" for="id_user_id">
共享给:
</label>
<ul style="list-style-type: none;">
{% for user in users %}
<li style="list-style-type: none;">
<label>
<input title="{{ user.username }} ({{ user.firstname }}) " type='checkbox'
name="user_ids"
value="{{ user.id }}">
&nbsp;{{ user.username }}{% if user.firstname %} ({{ user.firstname }}
) {% endif %}
</label>
</li>
{% endfor %}
</ul>
</div>
</div>
</fieldset>
<div class="submit-row">
<input type="hidden" name="action" value="share_to_others_view"/>
<input type="submit" name="apply" value="提交"/> &nbsp
<button type="button" name="cancel" onclick="history.back(-1);" class="module">取消</button>
</div>
</div>
</form>
{% endblock %}
{% extends 'admin/base_site.html' %}
{% load i18n admin_urls static admin_list %}
{% block content %}
<form action="" method="POST">
{% csrf_token %}
<div>
<fieldset class="module aligned">
<div class="form-row">
<div>
<label class="required" for="id_local_file_full_path">
文件路径:
</label>
<input type="text" name="local_file_full_path" style="width: 400px;" required
id="id_local_file_full_path">
</div>
</div>
</fieldset>
<div class="submit-row">
<input type="hidden" name="action" value="assign_task"/>
<input type="submit" name="apply" value="提交"/>
&nbsp
<button type="button" name="cancel" onclick="history.back(-1);" class="module">取消</button>
</div>
</div>
</form>
{% endblock %}
{% extends 'admin/change_list.html' %}
{% load i18n admin_urls static admin_list %}
{% block object-tools-items %}
{{ block.super }}
<li>
<a href="/admin/dapi/sharestorage/add_local/" class="addlink">
增加本地文件
</a>
</li>
{% endblock %}
# coding: utf-8
import pytest
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
@pytest.fixture(scope='session')
def django_db_setup():
pass
@pytest.fixture('function')
def client(db):
from rest_framework.test import APIClient
from django.contrib.auth.models import User
user = User.objects.get(username='tjhb')
client = APIClient()
client.force_authenticate(user=user)
return client
# coding: utf-8
def test_convert_all(db):
from iot1.services import convert_all_in_public_table
convert_all_in_public_table()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment