├── .coveragerc ├── .dockerignore ├── .editorconfig ├── .envs └── .local │ ├── .django │ └── .postgres ├── .gitattributes ├── .gitignore ├── .pylintrc ├── .runenv ├── .vscode ├── launch.json ├── settings l.json └── settings.json ├── README.md ├── Untitled.ipynb ├── Untitled1.ipynb ├── __init__.py ├── admin_interface ├── __init__.py ├── admin.py ├── api.py ├── apps.py ├── cache.py ├── fixtures │ ├── admin_interface_theme_bootstrap.json │ ├── admin_interface_theme_django.json │ ├── admin_interface_theme_foundation.json │ ├── admin_interface_theme_uswds.json │ └── initial_data.json ├── locale │ ├── es │ │ └── LC_MESSAGES │ │ │ └── django.po │ ├── fr │ │ └── LC_MESSAGES │ │ │ └── django.po │ └── it │ │ └── LC_MESSAGES │ │ └── django.po ├── migrations │ └── __init__.py ├── models.py ├── settings.py ├── static │ ├── admin_interface │ │ ├── css │ │ │ ├── adminlte.core.css │ │ │ ├── iconfont.css │ │ │ ├── iconfont.eot │ │ │ ├── iconfont.json │ │ │ ├── iconfont.svg │ │ │ ├── iconfont.ttf │ │ │ ├── iconfont.woff │ │ │ ├── iconfont.woff2 │ │ │ ├── jquery.mCustomScrollbar.css │ │ │ ├── jquery.mCustomScrollbar.min.css │ │ │ ├── mCSB_buttons.png │ │ │ ├── sidebar-main.css │ │ │ └── sidebar-themes.css │ │ ├── favico │ │ │ ├── favico-0.3.10-patched.js │ │ │ └── favico-0.3.10-patched.min.js │ │ ├── jquery.mCustomScrollbar.concat.min.js │ │ ├── jquery.mCustomScrollbar.js │ │ ├── magnific-popup │ │ │ ├── jquery.magnific-popup.js │ │ │ └── magnific-popup.css │ │ ├── related-modal │ │ │ └── related-modal.js │ │ └── sidebar-main.js │ └── ckeditor │ │ └── ckeditor │ │ └── skins │ │ └── light │ │ ├── LICENSE │ │ ├── README.md │ │ ├── bower.json │ │ ├── dialog.css │ │ ├── editor.css │ │ ├── editor_gecko.css │ │ ├── editor_ie.css │ │ ├── editor_ie7.css │ │ ├── editor_ie8.css │ │ ├── icons.png │ │ ├── icons_hidpi.png │ │ ├── images │ │ ├── arrow.png │ │ ├── close.png │ │ ├── hidpi │ │ │ ├── close.png │ │ │ ├── lock-open.png │ │ │ ├── lock.png │ │ │ └── refresh.png │ │ ├── lock-open.png │ │ ├── lock.png │ │ └── refresh.png │ │ └── skin.js ├── templates │ ├── admin │ │ ├── base.html │ │ ├── base_site.html │ │ ├── edit_inline │ │ │ ├── stacked.html │ │ │ └── tabular.html │ │ ├── filter.html │ │ ├── login.html │ │ └── popup_response.html │ └── admin_interface │ │ ├── css │ │ ├── admin-interface-fix.css │ │ ├── admin-interface.css │ │ ├── ckeditor.css │ │ ├── jquery.ui.tabs.css │ │ ├── list-filter-dropdown.css │ │ ├── modeltranslation.css │ │ ├── recent-actions.css │ │ ├── related-modal.css │ │ ├── sorl-thumbnail.css │ │ ├── tabbed-admin.css │ │ └── tinymce.css │ │ ├── dropdown_filter.html │ │ ├── favicon.html │ │ ├── language_chooser.html │ │ └── related-modal.html ├── templatetags │ ├── __init__.py │ └── admin_interface_tags.py ├── urls.py └── version.py ├── blog ├── __init__.py ├── admin.py ├── api.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── serializers.py ├── tests.py ├── urls.py └── views.py ├── common ├── jsonrender.py ├── models.py ├── pagination.py ├── selffield.py ├── serializers.py ├── utils.py ├── view.py └── viewset.py ├── compose ├── local │ └── django │ │ ├── Dockerfile │ │ ├── celery │ │ ├── beat │ │ │ └── start │ │ ├── flower │ │ │ └── start │ │ └── worker │ │ │ └── start │ │ └── start └── production │ ├── caddy │ ├── Caddyfile │ └── Dockerfile │ ├── django │ ├── Dockerfile │ ├── celery │ │ ├── beat │ │ │ └── start │ │ ├── flower │ │ │ └── start │ │ └── worker │ │ │ └── start │ ├── entrypoint │ └── start │ └── postgres │ ├── Dockerfile │ └── maintenance │ ├── _sourced │ ├── constants.sh │ ├── countdown.sh │ ├── messages.sh │ └── yes_no.sh │ ├── backup │ ├── backups │ └── restore ├── config ├── __init__.py ├── settings │ ├── __init__.py │ ├── base.py │ ├── local.py │ ├── production.py │ └── test.py ├── urls.py └── wsgi.py ├── conftest.py ├── contrib ├── __init__.py └── sites │ ├── __init__.py │ └── migrations │ └── __init__.py ├── course ├── __init__.py ├── admin.py ├── api.py ├── apps.py ├── filter.py ├── migrations │ └── __init__.py ├── models.py ├── serializers.py ├── tests.py ├── urls.py └── views.py ├── dashboards.py ├── docs ├── Makefile ├── __init__.py ├── conf.py ├── deploy.rst ├── docker_ec2.rst ├── index.rst ├── install.rst └── make.bat ├── exampaper ├── __init__.py ├── admin.py ├── api.py ├── apps.py ├── filter.py ├── migrations │ └── __init__.py ├── models.py ├── serializers.py ├── tests.py ├── urls.py └── views.py ├── examplan ├── __init__.py ├── admin.py ├── api.py ├── apps.py ├── filter.py ├── migrations │ └── __init__.py ├── models.py ├── serializers.py ├── tests.py ├── urls.py └── views.py ├── experimate └── metaclass.py ├── frontend ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── static │ └── frontend │ │ ├── asset-manifest.json │ │ ├── favicon.png │ │ ├── header_bg.bab1e6cd.png │ │ ├── iconfont.js │ │ ├── index.html │ │ ├── login_bg.1c2ab076.png │ │ ├── pdfjsWorker.async.js │ │ ├── umi.css │ │ └── umi.js ├── templates │ └── frontend │ │ └── index.html ├── tests.py ├── urls.py └── views.py ├── geo ├── __init__.py ├── admin.py ├── api.py ├── apps.py ├── city.json ├── migrations │ └── __init__.py ├── models.py ├── province.json ├── tests.py ├── urls.py └── views.py ├── learn ├── __init__.py ├── admin.py ├── api.py ├── apps.py ├── filter.py ├── migrations │ └── __init__.py ├── models.py ├── serializers.py ├── tests.py ├── urls.py └── views.py ├── local.yml ├── locale └── README.rst ├── manage.py ├── merge_production_dotenvs_in_dotenv.py ├── mystatistics ├── VisitTimesMiddleware.py ├── __init__.py ├── admin.py ├── api.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py ├── urls.py └── views.py ├── notifications ├── __init__.py ├── admin.py ├── api.py ├── apps.py ├── filter.py ├── migrations │ └── __init__.py ├── models.py ├── serializers.py ├── settings.py ├── signals.py ├── templates │ └── notifications │ │ ├── list.html │ │ ├── notice.html │ │ └── test_tags.html ├── templatetags │ ├── __init__.py │ └── notifications_tags.py ├── tests │ ├── __init__.py │ ├── settings.py │ ├── static │ │ └── notifications │ │ │ └── live-test.js │ ├── templates │ │ └── test_live.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── urls.py ├── utils.py └── views.py ├── openapi-schema.json ├── orgs ├── __init__.py ├── admin.py ├── api.py ├── apps.py ├── filter.py ├── migrations │ └── __init__.py ├── models.py ├── serializers.py ├── tests.py ├── urls.py └── views.py ├── permissions ├── .ipynb ├── __init__.py ├── admin.py ├── api_views.py ├── apps.py ├── classes.py ├── exceptions.py ├── filterregist.py ├── filters.py ├── fixtures │ ├── RoleOperationshipWithFilter.json │ └── role.json ├── handlers.py ├── icons.py ├── inlineadmin.py ├── links.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ ├── purgepermissions.py │ │ └── rolepermission.py ├── managers.py ├── migrations │ └── __init__.py ├── models.py ├── permissions.py ├── permissonregist.py ├── search.py ├── serializers.py ├── templates │ └── admin │ │ └── edit_inline │ │ ├── stacked.html │ │ └── tabular_select.html ├── tests │ ├── __init__.py │ ├── literals.py │ ├── test_api.py │ ├── test_inlineadmin.py │ ├── test_models.py │ └── test_views.py ├── urls.py └── views.py ├── production.yml ├── pytest.ini ├── questions ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── serializers.py ├── settings.py ├── signals.py ├── templates │ └── notifications │ │ ├── list.html │ │ ├── notice.html │ │ └── test_tags.html ├── templatetags │ ├── __init__.py │ └── notifications_tags.py ├── urls.py ├── utils.py └── views.py ├── requirements ├── base.txt ├── local.txt └── production.txt ├── rest_framework ├── __init__.py ├── apps.py ├── authentication.py ├── authtoken │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ └── drf_create_token.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ └── views.py ├── checks.py ├── compat.py ├── decorators.py ├── documentation.py ├── exceptions.py ├── fields.py ├── filters.py ├── generics.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ └── generateschema.py ├── metadata.py ├── mixins.py ├── negotiation.py ├── pagination.py ├── parsers.py ├── permissions.py ├── relations.py ├── renderers.py ├── request.py ├── response.py ├── reverse.py ├── routers.py ├── schemas │ ├── __init__.py │ ├── coreapi.py │ ├── generators.py │ ├── inspectors.py │ ├── openapi.py │ ├── utils.py │ └── views.py ├── serializers.py ├── settings.py ├── static │ └── rest_framework │ │ ├── css │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap-tweaks.css │ │ ├── bootstrap.min.css │ │ ├── default.css │ │ ├── font-awesome-4.0.3.css │ │ └── prettify.css │ │ ├── docs │ │ ├── css │ │ │ ├── base.css │ │ │ ├── highlight.css │ │ │ └── jquery.json-view.min.css │ │ ├── img │ │ │ ├── favicon.ico │ │ │ └── grid.png │ │ └── js │ │ │ ├── api.js │ │ │ ├── highlight.pack.js │ │ │ └── jquery.json-view.min.js │ │ ├── fonts │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ │ ├── img │ │ ├── glyphicons-halflings-white.png │ │ ├── glyphicons-halflings.png │ │ └── grid.png │ │ └── js │ │ ├── ajax-form.js │ │ ├── bootstrap.min.js │ │ ├── coreapi-0.1.1.js │ │ ├── csrf.js │ │ ├── default.js │ │ ├── jquery-3.4.1.min.js │ │ └── prettify-min.js ├── status.py ├── templates │ └── rest_framework │ │ ├── admin.html │ │ ├── admin │ │ ├── detail.html │ │ ├── dict_value.html │ │ ├── list.html │ │ ├── list_value.html │ │ └── simple_list_value.html │ │ ├── api.html │ │ ├── base.html │ │ ├── docs │ │ ├── auth │ │ │ ├── basic.html │ │ │ ├── session.html │ │ │ └── token.html │ │ ├── document.html │ │ ├── error.html │ │ ├── index.html │ │ ├── interact.html │ │ ├── langs │ │ │ ├── javascript-intro.html │ │ │ ├── javascript.html │ │ │ ├── python-intro.html │ │ │ ├── python.html │ │ │ ├── shell-intro.html │ │ │ └── shell.html │ │ ├── link.html │ │ └── sidebar.html │ │ ├── filters │ │ ├── base.html │ │ ├── ordering.html │ │ └── search.html │ │ ├── horizontal │ │ ├── checkbox.html │ │ ├── checkbox_multiple.html │ │ ├── dict_field.html │ │ ├── fieldset.html │ │ ├── form.html │ │ ├── input.html │ │ ├── list_field.html │ │ ├── list_fieldset.html │ │ ├── radio.html │ │ ├── select.html │ │ ├── select_multiple.html │ │ └── textarea.html │ │ ├── inline │ │ ├── checkbox.html │ │ ├── checkbox_multiple.html │ │ ├── dict_field.html │ │ ├── fieldset.html │ │ ├── form.html │ │ ├── input.html │ │ ├── list_field.html │ │ ├── list_fieldset.html │ │ ├── radio.html │ │ ├── select.html │ │ ├── select_multiple.html │ │ └── textarea.html │ │ ├── login.html │ │ ├── login_base.html │ │ ├── pagination │ │ ├── numbers.html │ │ └── previous_and_next.html │ │ ├── raw_data_form.html │ │ ├── schema.js │ │ └── vertical │ │ ├── checkbox.html │ │ ├── checkbox_multiple.html │ │ ├── dict_field.html │ │ ├── fieldset.html │ │ ├── form.html │ │ ├── input.html │ │ ├── list_field.html │ │ ├── list_fieldset.html │ │ ├── radio.html │ │ ├── select.html │ │ ├── select_multiple.html │ │ └── textarea.html ├── templatetags │ ├── __init__.py │ └── rest_framework.py ├── test.py ├── throttling.py ├── urlpatterns.py ├── urls.py ├── utils │ ├── __init__.py │ ├── breadcrumbs.py │ ├── encoders.py │ ├── field_mapping.py │ ├── formatting.py │ ├── html.py │ ├── humanize_datetime.py │ ├── json.py │ ├── mediatypes.py │ ├── model_meta.py │ ├── representation.py │ ├── serializer_helpers.py │ └── urls.py ├── validators.py ├── versioning.py ├── views.py └── viewsets.py ├── rest_framework_serializer_extensions ├── __init__.py ├── fields.py ├── serializers.py ├── utils.py └── views.py ├── setup.cfg ├── static └── .gitignore ├── supervisord.conf ├── taskapp ├── __init__.py └── celery.py ├── tessss.py ├── traingroup ├── __init__.py ├── admin.py ├── api.py ├── apps.py ├── filter.py ├── migrations │ └── __init__.py ├── models.py ├── serializers.py ├── tests.py ├── urls.py └── views.py ├── uploadfile ├── __init__.py ├── admin.py ├── api.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── serializers.py ├── tests.py ├── urls.py └── views.py ├── users ├── __init__.py ├── admin.py ├── api.py ├── app_settings.py ├── apps.py ├── filter.py ├── fixtures │ └── user.json ├── locale │ ├── cs │ │ └── LC_MESSAGES │ │ │ └── django.po │ ├── de │ │ └── LC_MESSAGES │ │ │ └── django.po │ ├── es │ │ └── LC_MESSAGES │ │ │ └── django.po │ ├── pl │ │ └── LC_MESSAGES │ │ │ └── django.po │ └── ru │ │ └── LC_MESSAGES │ │ └── django.po ├── migrations │ └── __init__.py ├── models.py ├── permissions.py ├── serializers.py ├── tests │ ├── __init__.py │ ├── django_urls.py │ ├── mixins.py │ ├── requirements.pip │ ├── settings.py │ ├── test_api.py │ ├── test_social.py │ └── urls.py ├── urls.py ├── utils.py ├── validators.py └── views.py └── utils.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | include = trainserver/* 3 | omit = *migrations*, *tests* 4 | plugins = 5 | django_coverage_plugin 6 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .* 2 | !.coveragerc 3 | !.env 4 | !.pylintrc 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.{py,rst,ini}] 12 | indent_style = space 13 | indent_size = 4 14 | 15 | [*.py] 16 | line_length=120 17 | known_first_party=trainserver 18 | multi_line_output=3 19 | default_section=THIRDPARTY 20 | 21 | [*.{html,css,scss,json,yml}] 22 | indent_style = space 23 | indent_size = 2 24 | 25 | [*.md] 26 | trim_trailing_whitespace = false 27 | 28 | [Makefile] 29 | indent_style = tab 30 | 31 | [nginx.conf] 32 | indent_style = space 33 | indent_size = 2 34 | -------------------------------------------------------------------------------- /.envs/.local/.django: -------------------------------------------------------------------------------- 1 | # General 2 | # ------------------------------------------------------------------------------ 3 | USE_DOCKER=yes 4 | 5 | # Redis 6 | # ------------------------------------------------------------------------------ 7 | REDIS_URL=redis://redis:6379/0 8 | 9 | # Celery 10 | # ------------------------------------------------------------------------------ 11 | 12 | # Flower 13 | CELERY_FLOWER_USER=debug 14 | CELERY_FLOWER_PASSWORD=debug 15 | 16 | -------------------------------------------------------------------------------- /.envs/.local/.postgres: -------------------------------------------------------------------------------- 1 | # PostgreSQL 2 | # ------------------------------------------------------------------------------ 3 | POSTGRES_HOST=localhost 4 | POSTGRES_PORT=5432 5 | POSTGRES_DB=whlserver 6 | POSTGRES_USER=postgres 7 | POSTGRES_PASSWORD=fangaofeng 8 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | load-plugins=pylint_common, pylint_django, pylint_celery 3 | 4 | [FORMAT] 5 | max-line-length=120 6 | 7 | [MESSAGES CONTROL] 8 | disable=missing-docstring,invalid-name 9 | 10 | [DESIGN] 11 | max-parents=13 12 | 13 | [TYPECHECK] 14 | generated-members=REQUEST,acl_users,aq_parent,"[a-zA-Z]+_set{1,2}",save,delete 15 | -------------------------------------------------------------------------------- /.runenv: -------------------------------------------------------------------------------- 1 | // DJANGO_READ_DOT_ENV_FILE=True 2 | DJANGO_SETTINGS_MODULE=config.settings.production 3 | DATABASE_URL=postgres://postgres:postgres@localhost:5432/whlrest 4 | DJANGO_DEBUG=False 5 | DJANGO_SECRET_KEY=9Z,2oHiQE9rU&eaZ0N|hRIG+df|]-M3qYmJa+EU#$fz>(x/E6B 6 | DJANGO_ADMIN_URL=manager/ 7 | DJANGO_ALLOWED_HOSTS=localhost,192.168.0.104,127.0.0.1 8 | CELERY_BROKER_URL=redis://localhost:6379/0 9 | 10 | REDIS_URL=redis://localhost:6379/1 11 | USE_DOCKER=no 12 | 13 | 14 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.1.0" 2 | __version_info__ = tuple( 3 | [ 4 | int(num) if num.isdigit() else num 5 | for num in __version__.replace("-", ".", 1).split(".") 6 | ] 7 | ) 8 | -------------------------------------------------------------------------------- /admin_interface/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | default_app_config = 'admin_interface.apps.AdminInterfaceConfig' 4 | -------------------------------------------------------------------------------- /admin_interface/api.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from rest_framework.views import APIView 3 | from rest_framework.response import Response 4 | from rest_framework import authentication, permissions 5 | from django.urls import reverse 6 | 7 | 8 | class GetUploadurl(APIView): 9 | """ 10 | View to list all users in the system. 11 | 12 | * Requires token authentication. 13 | * Only admin users are able to access this view. 14 | """ 15 | # authentication_classes = [authentication.TokenAuthentication] 16 | permission_classes = [permissions.IsAuthenticated] 17 | 18 | def geturl(self, request, name): 19 | return ''.join([request.scheme, '://', request.get_host(), reverse(name)]) 20 | 21 | def get(self, request, format=None): 22 | """ 23 | Return a list of all users. 24 | """ 25 | 26 | UPLOAD_PATH = { 27 | 'course': self.geturl(request, 'api:course_upload'), 28 | 'paper': self.geturl(request, 'api:paper_upload'), 29 | 'avatar': self.geturl(request, 'api:account_avatar'), 30 | 'org': self.geturl(request, 'api:org_upload'), 31 | 'user': self.geturl(request, 'api:user_upload'), 32 | 'blogCover': 'http://localhost/' # 假的 33 | } 34 | return Response({'status': 'ok', 'data': UPLOAD_PATH}) 35 | -------------------------------------------------------------------------------- /admin_interface/apps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.apps import AppConfig 4 | from django.db.models.signals import post_migrate 5 | from django.utils.translation import ugettext_lazy as _ 6 | 7 | 8 | class AdminInterfaceConfig(AppConfig): 9 | 10 | name = 'admin_interface' 11 | verbose_name = _('Admin Interface') 12 | icon = 'iconfont icon-shezhi' 13 | 14 | def ready(self): 15 | 16 | from admin_interface import settings 17 | from admin_interface.models import Theme 18 | 19 | settings.check_installed_apps() 20 | post_migrate.connect( 21 | Theme.post_migrate_handler, sender=self) 22 | -------------------------------------------------------------------------------- /admin_interface/cache.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.conf import settings 4 | from django.core.cache import cache, caches 5 | 6 | 7 | def app_cache(): 8 | return caches['admin_interface'] if 'admin_interface' in settings.CACHES else cache 9 | 10 | 11 | def del_cached_active_theme(): 12 | app_cache().delete('admin_interface_theme') 13 | 14 | 15 | def get_cached_active_theme(): 16 | return app_cache().get('admin_interface_theme', None) 17 | 18 | 19 | def set_cached_active_theme(theme): 20 | app_cache().set('admin_interface_theme', theme) 21 | -------------------------------------------------------------------------------- /admin_interface/fixtures/admin_interface_theme_django.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "admin_interface.theme", 4 | "fields": { 5 | "name": "Django", 6 | "active": true, 7 | "title": "Django administration", 8 | "title_color": "#F5DD5D", 9 | "title_visible": true, 10 | "logo": "", 11 | "logo_color": "#FFFFFF", 12 | "logo_visible": true, 13 | "css_header_background_color": "#0C4B33", 14 | "css_header_text_color": "#44B78B", 15 | "css_header_link_color": "#FFFFFF", 16 | "css_header_link_hover_color": "#C9F0DD", 17 | "css_module_background_color": "#44B78B", 18 | "css_module_text_color": "#FFFFFF", 19 | "css_module_link_color": "#FFFFFF", 20 | "css_module_link_hover_color": "#C9F0DD", 21 | "css_module_rounded_corners": true, 22 | "css_generic_link_color": "#0C3C26", 23 | "css_generic_link_hover_color": "#156641", 24 | "css_save_button_background_color": "#0C4B33", 25 | "css_save_button_background_hover_color": "#0C3C26", 26 | "css_save_button_text_color": "#FFFFFF", 27 | "css_delete_button_background_color": "#BA2121", 28 | "css_delete_button_background_hover_color": "#A41515", 29 | "css_delete_button_text_color": "#FFFFFF", 30 | "css": "", 31 | "related_modal_active": true, 32 | "related_modal_background_color": "#000000", 33 | "related_modal_background_opacity": 0.2, 34 | "related_modal_rounded_corners": true, 35 | "list_filter_dropdown": false, 36 | "recent_actions_visible": true 37 | } 38 | } 39 | ] -------------------------------------------------------------------------------- /admin_interface/fixtures/initial_data.json: -------------------------------------------------------------------------------- 1 | [{"model": "admin_interface.theme", "pk": 1, "fields": {"name": "Django", "active": true, "title": "Django administration", "title_visible": true, "logo": "", "logo_visible": true, "css_header_background_color": "#0C4B33", "css_header_title_color": "#F5DD5D", "css_header_text_color": "#44B78B", "css_header_link_color": "#FFFFFF", "css_header_link_hover_color": "#C9F0DD", "css_module_background_color": "#44B78B", "css_module_text_color": "#FFFFFF", "css_module_link_color": "#FFFFFF", "css_module_link_hover_color": "#C9F0DD", "css_module_rounded_corners": true, "css_generic_link_color": "#0C3C26", "css_generic_link_hover_color": "#156641", "css_save_button_background_color": "#0C4B33", "css_save_button_background_hover_color": "#0C3C26", "css_save_button_text_color": "#FFFFFF", "css_delete_button_background_color": "#BA2121", "css_delete_button_background_hover_color": "#A41515", "css_delete_button_text_color": "#FFFFFF", "css": "", "related_modal_active": true, "related_modal_background_color": "#000000", "related_modal_background_opacity": 0.2, "related_modal_rounded_corners": true, "list_filter_dropdown": false}}] -------------------------------------------------------------------------------- /admin_interface/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/admin_interface/migrations/__init__.py -------------------------------------------------------------------------------- /admin_interface/settings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import django 4 | from django.conf import settings 5 | from django.core.exceptions import ImproperlyConfigured 6 | 7 | 8 | def check_installed_apps(): 9 | dj_version = django.VERSION 10 | installed_apps = settings.INSTALLED_APPS 11 | 12 | if 'colorfield' not in installed_apps: 13 | raise ImproperlyConfigured( 14 | '\'colorfield\' needed, ' 15 | 'add it to settings.INSTALLED_APPS.') 16 | 17 | if dj_version < (1, 9): 18 | if 'flat' not in installed_apps: 19 | raise ImproperlyConfigured( 20 | '\'flat\' needed before django 1.9, ' 21 | 'add it to settings.INSTALLED_APPS.') 22 | else: 23 | if 'flat' in installed_apps: 24 | raise ImproperlyConfigured( 25 | '\'flat\' not needed since django 1.9, ' 26 | 'remove it from settings.INSTALLED_APPS.') 27 | 28 | if dj_version < (2, 0): 29 | if 'flat_responsive' not in installed_apps: 30 | raise ImproperlyConfigured( 31 | '\'flat_responsive\' needed before django 2.0, ' 32 | 'add it to settings.INSTALLED_APPS.') 33 | else: 34 | if 'flat_responsive' in installed_apps: 35 | raise ImproperlyConfigured( 36 | '\'flat_responsive\' not needed since django 2.0, ' 37 | 'remove it from settings.INSTALLED_APPS.') 38 | -------------------------------------------------------------------------------- /admin_interface/static/admin_interface/css/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/admin_interface/static/admin_interface/css/iconfont.eot -------------------------------------------------------------------------------- /admin_interface/static/admin_interface/css/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/admin_interface/static/admin_interface/css/iconfont.ttf -------------------------------------------------------------------------------- /admin_interface/static/admin_interface/css/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/admin_interface/static/admin_interface/css/iconfont.woff -------------------------------------------------------------------------------- /admin_interface/static/admin_interface/css/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/admin_interface/static/admin_interface/css/iconfont.woff2 -------------------------------------------------------------------------------- /admin_interface/static/admin_interface/css/mCSB_buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/admin_interface/static/admin_interface/css/mCSB_buttons.png -------------------------------------------------------------------------------- /admin_interface/static/admin_interface/sidebar-main.js: -------------------------------------------------------------------------------- 1 | jQuery(function($) { 2 | // Dropdown menu 3 | $(".sidebar-dropdown > a").click(function() { 4 | $(".sidebar-submenu").slideUp(200); 5 | if ( 6 | $(this) 7 | .parent() 8 | .hasClass("menu-open") 9 | ) { 10 | $(".sidebar-dropdown").removeClass("menu-open"); 11 | $(this) 12 | .parent() 13 | .removeClass("menu-open"); 14 | } else { 15 | $(".sidebar-dropdown").removeClass("menu-open"); 16 | $(this) 17 | .next(".sidebar-submenu") 18 | .slideDown(200); 19 | $(this) 20 | .parent() 21 | .addClass("menu-open"); 22 | } 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /admin_interface/static/ckeditor/ckeditor/skins/light/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Ikimea 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /admin_interface/static/ckeditor/ckeditor/skins/light/README.md: -------------------------------------------------------------------------------- 1 | ckeditor-light-theme 2 | ==================== 3 | -------------------------------------------------------------------------------- /admin_interface/static/ckeditor/ckeditor/skins/light/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ckeditor-light-theme", 3 | "version": "0.0.0", 4 | "homepage": "https://github.com/Ikimea/ckeditor-light-theme", 5 | "authors": [ 6 | "Mbechezi Mlanawo " 7 | ], 8 | "keywords": [ 9 | "ckeditor", 10 | "theme" 11 | ], 12 | "license": "MIT" 13 | } 14 | -------------------------------------------------------------------------------- /admin_interface/static/ckeditor/ckeditor/skins/light/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/admin_interface/static/ckeditor/ckeditor/skins/light/icons.png -------------------------------------------------------------------------------- /admin_interface/static/ckeditor/ckeditor/skins/light/icons_hidpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/admin_interface/static/ckeditor/ckeditor/skins/light/icons_hidpi.png -------------------------------------------------------------------------------- /admin_interface/static/ckeditor/ckeditor/skins/light/images/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/admin_interface/static/ckeditor/ckeditor/skins/light/images/arrow.png -------------------------------------------------------------------------------- /admin_interface/static/ckeditor/ckeditor/skins/light/images/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/admin_interface/static/ckeditor/ckeditor/skins/light/images/close.png -------------------------------------------------------------------------------- /admin_interface/static/ckeditor/ckeditor/skins/light/images/hidpi/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/admin_interface/static/ckeditor/ckeditor/skins/light/images/hidpi/close.png -------------------------------------------------------------------------------- /admin_interface/static/ckeditor/ckeditor/skins/light/images/hidpi/lock-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/admin_interface/static/ckeditor/ckeditor/skins/light/images/hidpi/lock-open.png -------------------------------------------------------------------------------- /admin_interface/static/ckeditor/ckeditor/skins/light/images/hidpi/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/admin_interface/static/ckeditor/ckeditor/skins/light/images/hidpi/lock.png -------------------------------------------------------------------------------- /admin_interface/static/ckeditor/ckeditor/skins/light/images/hidpi/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/admin_interface/static/ckeditor/ckeditor/skins/light/images/hidpi/refresh.png -------------------------------------------------------------------------------- /admin_interface/static/ckeditor/ckeditor/skins/light/images/lock-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/admin_interface/static/ckeditor/ckeditor/skins/light/images/lock-open.png -------------------------------------------------------------------------------- /admin_interface/static/ckeditor/ckeditor/skins/light/images/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/admin_interface/static/ckeditor/ckeditor/skins/light/images/lock.png -------------------------------------------------------------------------------- /admin_interface/static/ckeditor/ckeditor/skins/light/images/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/admin_interface/static/ckeditor/ckeditor/skins/light/images/refresh.png -------------------------------------------------------------------------------- /admin_interface/templates/admin/filter.html: -------------------------------------------------------------------------------- 1 | {% load i18n admin_interface_tags %} 2 | 3 | {% get_admin_interface_theme as theme %} 4 | 5 | {% if theme.list_filter_dropdown %} 6 | 7 | {% include "admin_interface/dropdown_filter.html" %} 8 | 9 | {% else %} 10 | 11 | {# Use the default list filter template -> https://github.com/django/django/blob/master/django/contrib/admin/templates/admin/filter.html #} 12 | 13 |

{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}

14 | 20 | 21 | {% endif %} 22 | 23 | -------------------------------------------------------------------------------- /admin_interface/templates/admin/popup_response.html: -------------------------------------------------------------------------------- 1 | {% load i18n static %} 2 | 3 | {% trans 'Popup closing...' %} 4 | 5 | 14 | 15 | -------------------------------------------------------------------------------- /admin_interface/templates/admin_interface/css/list-filter-dropdown.css: -------------------------------------------------------------------------------- 1 | /* 2 | list-filter-dropdown 3 | */ 4 | 5 | .admin-interface .list-filter-dropdown { 6 | margin-top:10px; 7 | margin-bottom:20px; 8 | } 9 | 10 | .admin-interface .list-filter-dropdown select { 11 | background-color:#FFFFFF; 12 | width:90%; 13 | margin-right:5%; 14 | } -------------------------------------------------------------------------------- /admin_interface/templates/admin_interface/css/modeltranslation.css: -------------------------------------------------------------------------------- 1 | /* 2 | django-modeltranslation support 3 | https://github.com/deschler/django-modeltranslation 4 | */ 5 | 6 | .admin-interface #content h1 select { 7 | text-transform: uppercase; 8 | margin-left: 15px; 9 | min-width: 50px; 10 | } 11 | 12 | .admin-interface .ui-tabs .ui-tabs-panel[id^=tab_id_] { 13 | border: none; 14 | border-top: 1px solid #eeeeee; 15 | padding: 0; 16 | margin-bottom: 0; 17 | } 18 | -------------------------------------------------------------------------------- /admin_interface/templates/admin_interface/css/recent-actions.css: -------------------------------------------------------------------------------- 1 | {% if not theme.recent_actions_visible %} 2 | .admin-interface.dashboard #content { 3 | max-width:600px; 4 | margin-right:0; 5 | } 6 | .admin-interface.dashboard #content #recent-actions-module { 7 | display:none; 8 | } 9 | {% endif %} -------------------------------------------------------------------------------- /admin_interface/templates/admin_interface/css/tabbed-admin.css: -------------------------------------------------------------------------------- 1 | /* 2 | django-tabbed-admin support 3 | https://github.com/omji/django-tabbed-admin 4 | */ 5 | 6 | /* Hide tabs until ready */ 7 | /* 8 | .admin-interface #tabs ul { 9 | display: none; 10 | } 11 | 12 | .admin-interface #tabs ul.ui-tabs-nav { 13 | display: block; 14 | } 15 | */ 16 | 17 | .admin-interface .ui-tabs .ui-tabs-panel[id^=tabs] .module.aligned:last-child { 18 | margin-bottom: 0; 19 | } 20 | 21 | .admin-interface .ui-tabs .ui-tabs-panel[id^=tabs] .module.aligned:last-child .form-row:last-child { 22 | border-bottom: none; 23 | } 24 | 25 | @media (max-width: 350px){ 26 | .admin-interface .ui-tabs .ui-tabs-panel[id^=tabs] .vTextField, 27 | .admin-interface .inline-related .vTextField { 28 | width: 17em; 29 | } 30 | } 31 | 32 | @media (max-width: 767px){ 33 | /* fix horizontal overflow - responsive.css:563 */ 34 | .admin-interface .ui-tabs .ui-tabs-panel[id^=tabs] .aligned .form-row > div:not([class]) { 35 | width: 100% !important; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /admin_interface/templates/admin_interface/css/tinymce.css: -------------------------------------------------------------------------------- 1 | .admin-interface textarea.tinymce ~ p.help { 2 | margin-top:5px !important; 3 | } -------------------------------------------------------------------------------- /admin_interface/templates/admin_interface/dropdown_filter.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 3 |
4 |

{% blocktrans with title as filter_title %} By {{ filter_title }} {% endblocktrans %}

5 |
6 | 11 |
12 |
13 | -------------------------------------------------------------------------------- /admin_interface/templates/admin_interface/favicon.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | {% if theme.favicon %} 4 | 5 | {% if theme.env_visible_in_favicon %} 6 | 7 | 16 | {% endif %} 17 | {% endif %} 18 | -------------------------------------------------------------------------------- /admin_interface/templates/admin_interface/language_chooser.html: -------------------------------------------------------------------------------- 1 | {% load admin_interface_tags %} 2 | 3 | {% if languages %} 4 |
5 | {% for language in languages %} 6 |
7 | {% csrf_token %} 8 | 9 |
10 | {% endfor %} 11 |
12 | {% csrf_token %} 13 | 18 |
19 |
20 | {% endif %} -------------------------------------------------------------------------------- /admin_interface/templates/admin_interface/related-modal.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | {% if theme.related_modal_active %} 4 | 5 | 6 | 7 | {% endif %} 8 | -------------------------------------------------------------------------------- /admin_interface/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/admin_interface/templatetags/__init__.py -------------------------------------------------------------------------------- /admin_interface/urls.py: -------------------------------------------------------------------------------- 1 | 2 | from django.urls import path 3 | from .api import GetUploadurl 4 | 5 | urlpatterns = [ 6 | path('uploadpath', GetUploadurl.as_view(), name='upload-path')] 7 | -------------------------------------------------------------------------------- /admin_interface/version.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | __version__ = '0.12.0' 4 | -------------------------------------------------------------------------------- /blog/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/blog/__init__.py -------------------------------------------------------------------------------- /blog/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Article 3 | # Register your models here. 4 | from imagekit.admin import AdminThumbnail 5 | 6 | 7 | @admin.register(Article) 8 | class ArticlelAdmin(admin.ModelAdmin): 9 | admin_thumbnail = AdminThumbnail(image_field='thumbnail') 10 | list_per_page = 10 11 | search_fields = ('body', 'title') 12 | 13 | list_display = ( 14 | 'id', 'title', 'created', 'views', 'status', 'admin_thumbnail', 'article_order') 15 | list_display_links = ('id', 'title') 16 | 17 | exclude = ('created', 'modified') 18 | view_on_site = True 19 | -------------------------------------------------------------------------------- /blog/api.py: -------------------------------------------------------------------------------- 1 | from rest_framework import viewsets 2 | from .models import Article 3 | from .serializers import ArticleSerializer 4 | from rest_framework.permissions import IsAuthenticated 5 | from rest_framework.parsers import MultiPartParser 6 | from common.jsonrender import EmberJSONRenderer 7 | from common.pagination import ListPagination 8 | from django_filters.rest_framework import DjangoFilterBackend 9 | from permissions.permissions import RolePermission 10 | 11 | 12 | class ArticleViewSet(viewsets.ModelViewSet): 13 | """ 14 | This viewset automatically provides `list` and `detail` actions. 15 | """ 16 | renderer_classes = (EmberJSONRenderer,) 17 | queryset = Article.objects.all().order_by('-pub_time') 18 | serializer_class = ArticleSerializer 19 | parser_classes = (MultiPartParser,) 20 | pagination_class = ListPagination 21 | permission_classes = [RolePermission] 22 | filter_backends = (DjangoFilterBackend,) 23 | filterset_fields = ('status',) 24 | 25 | def get_object(self): 26 | 27 | instance = super(ArticleViewSet, self).get_object() 28 | instance.viewed() 29 | return instance 30 | 31 | def get_serializer_class(self): 32 | if self.action == 'bulkdel': 33 | return ArticleListSerializer 34 | return ArticleSerializer 35 | 36 | @action(detail=False, methods=['PATCH'], name='bulk delete articles') 37 | def bulkdel(self, request, *args, **kwargs): 38 | 39 | serializer = self.get_serializer(data=request.data) 40 | serializer.is_valid(raise_exception=True) 41 | serializer.deletearticles() 42 | 43 | return Response({}) 44 | -------------------------------------------------------------------------------- /blog/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class BlogConfig(AppConfig): 5 | name = 'blog' 6 | icon = 'iconfont icon-gonggao2' 7 | -------------------------------------------------------------------------------- /blog/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/blog/migrations/__init__.py -------------------------------------------------------------------------------- /blog/serializers.py: -------------------------------------------------------------------------------- 1 | 2 | from rest_framework import serializers # , exceptions 3 | from .models import Article 4 | from common.serializers import OwnerFieldSerializer 5 | from common.selffield import ChoiceField 6 | 7 | 8 | class ArticleSerializer(OwnerFieldSerializer): 9 | status = ChoiceField(choices=Article.STATUS_CHOICES, required=False) 10 | arttype = ChoiceField(choices=Article.ARTTYPE, required=False) 11 | comment_status = ChoiceField(choices=Article.COMMENT_STATUS, required=False) 12 | thumbnail = serializers.ImageField(read_only=True) 13 | 14 | class Meta: 15 | model = Article 16 | fields = '__all__' 17 | read_only_fields = ('id', 'modified', 'created', 'views', 'creater', 'thumbnail') 18 | ordering = ['pub_time', 'modified'] 19 | extra_kwargs = {'description': {'required': False}} 20 | 21 | class ArticleListSerializer(serializers.Serializer): 22 | 23 | articles = serializers.PrimaryKeyRelatedField(required=True, many=True, queryset=Article.objects.all()) 24 | 25 | class Meta: 26 | fields = ['articles'] 27 | 28 | def deletearticles(self): 29 | for article in self.validated_data['articles']: 30 | article.delete() 31 | -------------------------------------------------------------------------------- /blog/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /blog/urls.py: -------------------------------------------------------------------------------- 1 | from rest_framework import routers 2 | from .api import ArticleViewSet 3 | 4 | router = routers.SimpleRouter(trailing_slash=False) 5 | router.register(r'article', ArticleViewSet) 6 | 7 | urlpatterns = router.urls 8 | -------------------------------------------------------------------------------- /blog/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /common/jsonrender.py: -------------------------------------------------------------------------------- 1 | from rest_framework.renderers import JSONRenderer 2 | from collections import OrderedDict 3 | from rest_framework.views import exception_handler 4 | from rest_framework.exceptions import APIException 5 | 6 | 7 | class EmberJSONRenderer(JSONRenderer): 8 | 9 | def render(self, data, accepted_media_type=None, renderer_context=None): 10 | if renderer_context and data: 11 | response = renderer_context["response"] 12 | if response.status_code >= 200 and response.status_code < 300: 13 | if isinstance(data, dict): 14 | if not data.pop('NOCHANGE', False): 15 | data = {'status': 'ok', 'data': data} 16 | else: 17 | data = {'status': 'ok', 'data': data} 18 | else: 19 | data.update(status='error') 20 | 21 | return super(EmberJSONRenderer, self).render(data, accepted_media_type, renderer_context) 22 | -------------------------------------------------------------------------------- /common/models.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from django.db import models 3 | from django_extensions.db.models import TimeStampedModel 4 | 5 | 6 | class CreaterTimeStampedModel(TimeStampedModel): 7 | """ TimeStampedModel 8 | An abstract base class model that provides self-managed "created" and 9 | "modified" fields. 10 | """ 11 | 12 | User = get_user_model() 13 | 14 | creater = models.ForeignKey( 15 | User, 16 | on_delete=models.CASCADE, 17 | related_name='+', 18 | blank=True, 19 | null=True 20 | ) 21 | 22 | class Meta: 23 | get_latest_by = 'modified' 24 | ordering = ['-modified', '-created'] 25 | abstract = True 26 | -------------------------------------------------------------------------------- /common/pagination.py: -------------------------------------------------------------------------------- 1 | from rest_framework.pagination import PageNumberPagination 2 | 3 | 4 | class ListPagination(PageNumberPagination): 5 | # 每页显示多少个 6 | page_size = 10 7 | # 默认每页显示3个,可以通过传入pager1/?page=2&size=4,改变默认每页显示的个数 8 | page_size_query_param = "pageSize" 9 | # 最大页数不超过10 10 | max_page_size = 100 11 | # 获取页码数的 12 | page_query_param = "current" 13 | -------------------------------------------------------------------------------- /common/selffield.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from django.utils import six 3 | 4 | 5 | class ChoiceField(serializers.ChoiceField): 6 | 7 | def to_internal_value(self, data): 8 | if data == '' and self.allow_blank: 9 | return '' 10 | 11 | try: 12 | return self.choice_strings_to_values.get(six.text_type(data), data) 13 | except KeyError: 14 | self.fail('invalid_choice', input=data) 15 | 16 | def to_representation(self, value): 17 | if value in ('', None): 18 | return value 19 | return self.choice_values_to_strings.get(six.text_type(value), value) 20 | 21 | def _get_choices(self): 22 | return self._choices 23 | 24 | def _set_choices(self, choices): 25 | super(ChoiceField, self)._set_choices(choices) 26 | # 27 | self.choice_strings_to_values = { 28 | six.text_type(value): key for key, value in self.choices.items() 29 | } 30 | self.choice_values_to_strings = { 31 | six.text_type(key): value for key, value in self.choices.items() 32 | } 33 | 34 | choices = property(_get_choices, _set_choices) 35 | -------------------------------------------------------------------------------- /common/utils.py: -------------------------------------------------------------------------------- 1 | from rest_framework.response import Response 2 | from importlib import import_module 3 | from django.conf import settings 4 | 5 | 6 | def get_admin_site(): 7 | 8 | site_module = getattr(settings, 'ADMIN_SITE', 9 | 'django.contrib.admin.site') 10 | mod, inst = site_module.rsplit('.', 1) 11 | mod = import_module(mod) 12 | return getattr(mod, inst) 13 | -------------------------------------------------------------------------------- /common/view.py: -------------------------------------------------------------------------------- 1 | from rest_framework.views import exception_handler as handler 2 | 3 | 4 | def exception_handler(exc, context): 5 | # Call REST framework's default exception handler first, 6 | # to get the standard error response. 7 | response = handler(exc, context) 8 | 9 | # Now add the HTTP status code to the response. 10 | if response is not None: 11 | response.data['status'] = response.status_code 12 | 13 | return response 14 | -------------------------------------------------------------------------------- /compose/local/django/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6-alpine 2 | 3 | ENV PYTHONUNBUFFERED 1 4 | 5 | RUN apk update \ 6 | # psycopg2 dependencies 7 | && apk add --virtual build-deps gcc python3-dev musl-dev \ 8 | && apk add postgresql-dev \ 9 | # Pillow dependencies 10 | && apk add jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev \ 11 | # CFFI dependencies 12 | && apk add libffi-dev py-cffi \ 13 | # Translations dependencies 14 | && apk add gettext \ 15 | # https://docs.djangoproject.com/en/dev/ref/django-admin/#dbshell 16 | && apk add postgresql-client 17 | 18 | # Requirements are installed here to ensure they will be cached. 19 | COPY ./requirements /requirements 20 | RUN pip install -r /requirements/local.txt 21 | 22 | COPY ./compose/production/django/entrypoint /entrypoint 23 | RUN sed -i 's/\r//' /entrypoint 24 | RUN chmod +x /entrypoint 25 | 26 | COPY ./compose/local/django/start /start 27 | RUN sed -i 's/\r//' /start 28 | RUN chmod +x /start 29 | 30 | COPY ./compose/local/django/celery/worker/start /start-celeryworker 31 | RUN sed -i 's/\r//' /start-celeryworker 32 | RUN chmod +x /start-celeryworker 33 | 34 | COPY ./compose/local/django/celery/beat/start /start-celerybeat 35 | RUN sed -i 's/\r//' /start-celerybeat 36 | RUN chmod +x /start-celerybeat 37 | 38 | COPY ./compose/local/django/celery/flower/start /start-flower 39 | RUN sed -i 's/\r//' /start-flower 40 | RUN chmod +x /start-flower 41 | 42 | WORKDIR /app 43 | 44 | ENTRYPOINT ["/entrypoint"] 45 | -------------------------------------------------------------------------------- /compose/local/django/celery/beat/start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -o errexit 4 | set -o nounset 5 | 6 | 7 | rm -f './celerybeat.pid' 8 | celery -A taskapp beat -l INFO 9 | -------------------------------------------------------------------------------- /compose/local/django/celery/flower/start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -o errexit 4 | set -o nounset 5 | 6 | 7 | celery flower \ 8 | --app=taskapp \ 9 | --broker="${CELERY_BROKER_URL}" \ 10 | --basic_auth="${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}" 11 | -------------------------------------------------------------------------------- /compose/local/django/celery/worker/start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -o errexit 4 | set -o nounset 5 | 6 | 7 | celery -A taskapp worker -l INFO 8 | -------------------------------------------------------------------------------- /compose/local/django/start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | 8 | python manage.py migrate 9 | python manage.py runserver_plus 0.0.0.0:8000 10 | -------------------------------------------------------------------------------- /compose/production/caddy/Caddyfile: -------------------------------------------------------------------------------- 1 | www.{$DOMAIN_NAME} { 2 | redir https://{$DOMAIN_NAME} 3 | } 4 | 5 | {$DOMAIN_NAME} { 6 | proxy / django:5000 { 7 | header_upstream Host {host} 8 | header_upstream X-Real-IP {remote} 9 | header_upstream X-Forwarded-Proto {scheme} 10 | } 11 | log stdout 12 | errors stdout 13 | gzip 14 | } 15 | -------------------------------------------------------------------------------- /compose/production/caddy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM abiosoft/caddy:0.11.0 2 | 3 | COPY ./compose/production/caddy/Caddyfile /etc/Caddyfile 4 | -------------------------------------------------------------------------------- /compose/production/django/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6-alpine 2 | 3 | ENV PYTHONUNBUFFERED 1 4 | 5 | RUN apk update \ 6 | # psycopg2 dependencies 7 | && apk add --virtual build-deps gcc python3-dev musl-dev \ 8 | && apk add postgresql-dev \ 9 | # Pillow dependencies 10 | && apk add jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev \ 11 | # CFFI dependencies 12 | && apk add libffi-dev py-cffi 13 | 14 | RUN addgroup -S django \ 15 | && adduser -S -G django django 16 | 17 | # Requirements are installed here to ensure they will be cached. 18 | COPY ./requirements /requirements 19 | RUN pip install --no-cache-dir -r /requirements/production.txt \ 20 | && rm -rf /requirements 21 | 22 | COPY ./compose/production/django/entrypoint /entrypoint 23 | RUN sed -i 's/\r//' /entrypoint 24 | RUN chmod +x /entrypoint 25 | RUN chown django /entrypoint 26 | 27 | COPY ./compose/production/django/start /start 28 | RUN sed -i 's/\r//' /start 29 | RUN chmod +x /start 30 | RUN chown django /start 31 | 32 | COPY ./compose/production/django/celery/worker/start /start-celeryworker 33 | RUN sed -i 's/\r//' /start-celeryworker 34 | RUN chmod +x /start-celeryworker 35 | RUN chown django /start-celeryworker 36 | 37 | COPY ./compose/production/django/celery/beat/start /start-celerybeat 38 | RUN sed -i 's/\r//' /start-celerybeat 39 | RUN chmod +x /start-celerybeat 40 | RUN chown django /start-celerybeat 41 | 42 | COPY ./compose/production/django/celery/flower/start /start-flower 43 | RUN sed -i 's/\r//' /start-flower 44 | RUN chmod +x /start-flower 45 | 46 | COPY . /app 47 | 48 | RUN chown -R django /app 49 | 50 | USER django 51 | 52 | WORKDIR /app 53 | 54 | ENTRYPOINT ["/entrypoint"] 55 | -------------------------------------------------------------------------------- /compose/production/django/celery/beat/start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | 8 | celery -A taskapp beat -l INFO 9 | -------------------------------------------------------------------------------- /compose/production/django/celery/flower/start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -o errexit 4 | set -o nounset 5 | 6 | 7 | celery flower \ 8 | --app=taskapp \ 9 | --broker="${CELERY_BROKER_URL}" \ 10 | --basic_auth="${CELERY_FLOWER_USER}:${CELERY_FLOWER_PASSWORD}" 11 | -------------------------------------------------------------------------------- /compose/production/django/celery/worker/start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | 8 | celery -A taskapp worker -l INFO 9 | -------------------------------------------------------------------------------- /compose/production/django/entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | 8 | # N.B. If only .env files supported variable expansion... 9 | export CELERY_BROKER_URL="${REDIS_URL}" 10 | 11 | if [ -z "${POSTGRES_USER}" ]; then 12 | base_postgres_image_default_user='postgres' 13 | export POSTGRES_USER="${base_postgres_image_default_user}" 14 | fi 15 | export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}" 16 | 17 | postgres_ready() { 18 | python << END 19 | import sys 20 | 21 | import psycopg2 22 | 23 | try: 24 | psycopg2.connect( 25 | dbname="${POSTGRES_DB}", 26 | user="${POSTGRES_USER}", 27 | password="${POSTGRES_PASSWORD}", 28 | host="${POSTGRES_HOST}", 29 | port="${POSTGRES_PORT}", 30 | ) 31 | except psycopg2.OperationalError: 32 | sys.exit(-1) 33 | sys.exit(0) 34 | 35 | END 36 | } 37 | until postgres_ready; do 38 | >&2 echo 'Waiting for PostgreSQL to become available...' 39 | sleep 1 40 | done 41 | >&2 echo 'PostgreSQL is available' 42 | 43 | exec "$@" 44 | -------------------------------------------------------------------------------- /compose/production/django/start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | 8 | python /app/manage.py collectstatic --noinput 9 | /usr/local/bin/gunicorn config.wsgi --bind 0.0.0.0:5000 --chdir=/app 10 | -------------------------------------------------------------------------------- /compose/production/postgres/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM postgres:10.4 2 | 3 | COPY ./compose/production/postgres/maintenance /usr/local/bin/maintenance 4 | RUN chmod +x /usr/local/bin/maintenance/* 5 | RUN mv /usr/local/bin/maintenance/* /usr/local/bin \ 6 | && rmdir /usr/local/bin/maintenance 7 | -------------------------------------------------------------------------------- /compose/production/postgres/maintenance/_sourced/constants.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | BACKUP_DIR_PATH='/backups' 5 | BACKUP_FILE_PREFIX='backup' 6 | -------------------------------------------------------------------------------- /compose/production/postgres/maintenance/_sourced/countdown.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | countdown() { 5 | declare desc="A simple countdown. Source: https://superuser.com/a/611582" 6 | local seconds="${1}" 7 | local d=$(($(date +%s) + "${seconds}")) 8 | while [ "$d" -ge `date +%s` ]; do 9 | echo -ne "$(date -u --date @$(($d - `date +%s`)) +%H:%M:%S)\r"; 10 | sleep 0.1 11 | done 12 | } 13 | -------------------------------------------------------------------------------- /compose/production/postgres/maintenance/_sourced/messages.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | message_newline() { 5 | echo 6 | } 7 | 8 | message_debug() 9 | { 10 | echo -e "DEBUG: ${@}" 11 | } 12 | 13 | message_welcome() 14 | { 15 | echo -e "\e[1m${@}\e[0m" 16 | } 17 | 18 | message_warning() 19 | { 20 | echo -e "\e[33mWARNING\e[0m: ${@}" 21 | } 22 | 23 | message_error() 24 | { 25 | echo -e "\e[31mERROR\e[0m: ${@}" 26 | } 27 | 28 | message_info() 29 | { 30 | echo -e "\e[37mINFO\e[0m: ${@}" 31 | } 32 | 33 | message_suggestion() 34 | { 35 | echo -e "\e[33mSUGGESTION\e[0m: ${@}" 36 | } 37 | 38 | message_success() 39 | { 40 | echo -e "\e[32mSUCCESS\e[0m: ${@}" 41 | } 42 | -------------------------------------------------------------------------------- /compose/production/postgres/maintenance/_sourced/yes_no.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | yes_no() { 5 | declare desc="Prompt for confirmation. \$\"\{1\}\": confirmation message." 6 | local arg1="${1}" 7 | 8 | local response= 9 | read -r -p "${arg1} (y/[n])? " response 10 | if [[ "${response}" =~ ^[Yy]$ ]] 11 | then 12 | exit 0 13 | else 14 | exit 1 15 | fi 16 | } 17 | -------------------------------------------------------------------------------- /compose/production/postgres/maintenance/backup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | ### Create a database backup. 5 | ### 6 | ### Usage: 7 | ### $ docker-compose -f .yml (exec |run --rm) postgres backup 8 | 9 | 10 | set -o errexit 11 | set -o pipefail 12 | set -o nounset 13 | 14 | 15 | working_dir="$(dirname ${0})" 16 | source "${working_dir}/_sourced/constants.sh" 17 | source "${working_dir}/_sourced/messages.sh" 18 | 19 | 20 | message_welcome "Backing up the '${POSTGRES_DB}' database..." 21 | 22 | 23 | if [[ "${POSTGRES_USER}" == "postgres" ]]; then 24 | message_error "Backing up as 'postgres' user is not supported. Assign 'POSTGRES_USER' env with another one and try again." 25 | exit 1 26 | fi 27 | 28 | export PGHOST="${POSTGRES_HOST}" 29 | export PGPORT="${POSTGRES_PORT}" 30 | export PGUSER="${POSTGRES_USER}" 31 | export PGPASSWORD="${POSTGRES_PASSWORD}" 32 | export PGDATABASE="${POSTGRES_DB}" 33 | 34 | backup_filename="${BACKUP_FILE_PREFIX}_$(date +'%Y_%m_%dT%H_%M_%S').sql.gz" 35 | pg_dump | gzip > "${BACKUP_DIR_PATH}/${backup_filename}" 36 | 37 | 38 | message_success "'${POSTGRES_DB}' database backup '${backup_filename}' has been created and placed in '${BACKUP_DIR_PATH}'." 39 | -------------------------------------------------------------------------------- /compose/production/postgres/maintenance/backups: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | ### View backups. 5 | ### 6 | ### Usage: 7 | ### $ docker-compose -f .yml (exec |run --rm) postgres backups 8 | 9 | 10 | set -o errexit 11 | set -o pipefail 12 | set -o nounset 13 | 14 | 15 | working_dir="$(dirname ${0})" 16 | source "${working_dir}/_sourced/constants.sh" 17 | source "${working_dir}/_sourced/messages.sh" 18 | 19 | 20 | message_welcome "These are the backups you have got:" 21 | 22 | ls -lht "${BACKUP_DIR_PATH}" 23 | -------------------------------------------------------------------------------- /config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/config/__init__.py -------------------------------------------------------------------------------- /config/settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/config/settings/__init__.py -------------------------------------------------------------------------------- /conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from django.conf import settings 3 | from django.test import RequestFactory 4 | 5 | from trainserver.users.tests.factories import UserFactory 6 | 7 | 8 | @pytest.fixture(autouse=True) 9 | def media_storage(settings, tmpdir): 10 | settings.MEDIA_ROOT = tmpdir.strpath 11 | 12 | 13 | @pytest.fixture 14 | def user() -> settings.AUTH_USER_MODEL: 15 | return UserFactory() 16 | 17 | 18 | @pytest.fixture 19 | def request_factory() -> RequestFactory: 20 | return RequestFactory() 21 | -------------------------------------------------------------------------------- /contrib/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | To understand why this file is here, please read: 3 | 4 | http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django 5 | """ 6 | -------------------------------------------------------------------------------- /contrib/sites/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | To understand why this file is here, please read: 3 | 4 | http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django 5 | """ 6 | -------------------------------------------------------------------------------- /contrib/sites/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | To understand why this file is here, please read: 3 | 4 | http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django 5 | """ 6 | -------------------------------------------------------------------------------- /course/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/course/__init__.py -------------------------------------------------------------------------------- /course/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django_json_widget.widgets import JSONEditorWidget 3 | from django.contrib.postgres import fields 4 | # Register your models here. 5 | from simple_history.admin import SimpleHistoryAdmin 6 | 7 | from .models import Coursetype, Courseware, Zipfile 8 | 9 | 10 | @admin.register(Coursetype) 11 | class CoursetypeAdmin(admin.ModelAdmin): 12 | pass 13 | # widgets = { 14 | # 'property': JSONEditorWidget() 15 | # } 16 | 17 | 18 | # @admin.register(Courseware) 19 | # class CoursewareAdmin(admin.ModelAdmin): 20 | # list_display = ('id', 'courseware_no', 'name', 'type') 21 | # formfield_overrides = { 22 | # fields.JSONField: {'widget': JSONEditorWidget}, 23 | # } 24 | 25 | 26 | @admin.register(Zipfile) 27 | class ZipfileAdmin(admin.ModelAdmin): 28 | list_display = ('id', 'zipfile') 29 | 30 | 31 | @admin.register(Courseware) 32 | class SimpleHistoryCoursewareAdmin(SimpleHistoryAdmin): 33 | list_display = ('id', 'courseware_no', 'name', 'type') 34 | history_list_display = ["status"] 35 | 36 | # widgets = { 37 | # 'property': JSONEditorWidget(attrs={'width': 80}), 38 | # } 39 | formfield_overrides = { 40 | fields.JSONField: {'widget': JSONEditorWidget}, 41 | } 42 | -------------------------------------------------------------------------------- /course/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from django.utils.translation import gettext_lazy as _ 3 | 4 | 5 | class CourseConfig(AppConfig): 6 | name = 'course' 7 | icon = 'iconfont icon-course' 8 | verbose_name = _("course manager") 9 | -------------------------------------------------------------------------------- /course/filter.py: -------------------------------------------------------------------------------- 1 | from permissions.filters import RoleFilterBackend 2 | 3 | 4 | class IsManagerFilterBackend(RoleFilterBackend): 5 | """ 6 | Filter that only allows users to see their manager objects by the role in department 7 | """ 8 | name = 'IsManagerFilterBackend' 9 | 10 | def filter_queryset(self, request, queryset, view): 11 | 12 | if hasattr(request.user, 'managerdepartment'): 13 | return queryset.filter(departments=request.user.managerdepartment) 14 | else: 15 | return queryset.none() 16 | 17 | 18 | class IsUserFilterBackend(RoleFilterBackend): 19 | """ 20 | Filter that only allows users to see their manager objects by the role in department 21 | """ 22 | name = 'IsUserFilterBackend' 23 | 24 | def filter_queryset(self, request, queryset, view): 25 | 26 | return queryset.filter(category='公开课') 27 | -------------------------------------------------------------------------------- /course/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/course/migrations/__init__.py -------------------------------------------------------------------------------- /course/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /course/urls.py: -------------------------------------------------------------------------------- 1 | from .api import CoursetypeViewSet, CoursewareViewSet, ZipfileUploadView 2 | from rest_framework import routers 3 | from django.urls import include, path 4 | #from cruds_adminlte.urls import crud_for_app 5 | 6 | router = routers.SimpleRouter(trailing_slash=False) 7 | # router = routers.SimpleRouter(trailing_slash=False) 8 | 9 | router.register(r'type', CoursetypeViewSet, basename='course_type') 10 | router.register(r'ware', CoursewareViewSet, basename='course_ware') 11 | # router.register(r'department', CoursewareMemberModifyViewSet, basename='course_department') 12 | urlpatterns = [ 13 | path('upload', ZipfileUploadView.as_view(), name='course_upload') 14 | ] 15 | urlpatterns += router.urls 16 | # urlpatterns += [path('/departments', CoursewareMemberModifyViewSet.as_view( 17 | # {'get': 'list', 'put': 'update', 'patch': 'partial_update'}), name='courseware_departments')] 18 | -------------------------------------------------------------------------------- /course/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /dashboards.py: -------------------------------------------------------------------------------- 1 | from controlcenter import Dashboard, widgets 2 | from users.models import User 3 | 4 | 5 | class ModelItemList(widgets.ItemList): 6 | model = User 7 | list_display = ('pk', 'name') 8 | 9 | 10 | class MySingleBarChart(widgets.SingleBarChart): 11 | # label and series 12 | values_list = ('name', 'employee_position') 13 | # Data source 14 | queryset = User.objects.order_by() 15 | limit_to = 6 16 | 17 | 18 | class MyDashboard(Dashboard): 19 | 20 | widgets = ( 21 | ModelItemList, 22 | widgets.Group([MySingleBarChart], width=widgets.LARGER, height=300), 23 | ) 24 | -------------------------------------------------------------------------------- /docs/__init__.py: -------------------------------------------------------------------------------- 1 | # Included so that Django's startproject comment runs against the docs directory 2 | -------------------------------------------------------------------------------- /docs/deploy.rst: -------------------------------------------------------------------------------- 1 | Deploy 2 | ======== 3 | 4 | This is where you describe how the project is deployed in production. 5 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. trainserver documentation master file, created by 2 | sphinx-quickstart. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to trainserver's documentation! 7 | ==================================================================== 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | install 15 | deploy 16 | docker_ec2 17 | tests 18 | 19 | 20 | 21 | Indices and tables 22 | ================== 23 | 24 | * :ref:`genindex` 25 | * :ref:`modindex` 26 | * :ref:`search` 27 | -------------------------------------------------------------------------------- /docs/install.rst: -------------------------------------------------------------------------------- 1 | Install 2 | ========= 3 | 4 | This is where you write how to get a new laptop to run this project. 5 | -------------------------------------------------------------------------------- /exampaper/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/exampaper/__init__.py -------------------------------------------------------------------------------- /exampaper/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import ExamPaPer,QuestionExam 3 | 4 | @admin.register(ExamPaPer) 5 | class ExamPaPerAdmin(admin.ModelAdmin): 6 | list_display = ('id','name','status') 7 | @admin.register(QuestionExam) 8 | class QuestionExamAdmin(admin.ModelAdmin): 9 | list_display = ('id','title','type') 10 | -------------------------------------------------------------------------------- /exampaper/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from django.utils.translation import ugettext_lazy as _ 3 | 4 | 5 | class ExamPaperConfig(AppConfig): 6 | name = 'exampaper' 7 | icon = 'iconfont icon-exam2' 8 | verbose_name = _('exampaper manager') 9 | -------------------------------------------------------------------------------- /exampaper/filter.py: -------------------------------------------------------------------------------- 1 | from permissions.filters import RoleFilterBackend 2 | 3 | 4 | class IsManagerFilterBackend(RoleFilterBackend): 5 | """ 6 | Filter that only allows users to see their manager objects by the role in department 7 | """ 8 | name = 'IsManagerFilterBackend' 9 | 10 | def filter_queryset(self, request, queryset, view): 11 | 12 | if hasattr(request.user, 'managerdepartment'): 13 | return queryset.filter(departments=request.user.managerdepartment) 14 | else: 15 | return queryset.none() 16 | -------------------------------------------------------------------------------- /exampaper/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/exampaper/migrations/__init__.py -------------------------------------------------------------------------------- /exampaper/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /exampaper/urls.py: -------------------------------------------------------------------------------- 1 | from rest_framework import routers 2 | from django.urls import path 3 | from .api import ExamPaPerViewSet, ZipfileUploadView 4 | router = routers.SimpleRouter(trailing_slash=False) 5 | router.register(r'paper', ExamPaPerViewSet) 6 | 7 | urlpatterns = [ 8 | path('paper/upload', ZipfileUploadView.as_view(), name='paper_upload') 9 | ] 10 | urlpatterns += router.urls 11 | # urlpatterns += [path('paper//trainmanagers', ExamPaPerMemberModifyViewSet.as_view( 12 | # {'get': 'list', 'put': 'update', 'patch': 'partial_update'}), name='exampaper_trainmanagers')] 13 | -------------------------------------------------------------------------------- /exampaper/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /examplan/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/examplan/__init__.py -------------------------------------------------------------------------------- /examplan/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import ExamPlan, ExamProgress 3 | # Register your models here. 4 | @admin.register(ExamPlan) 5 | class ExamPlanAdmin(admin.ModelAdmin): 6 | list_display = ('id','name','status') 7 | 8 | @admin.register(ExamProgress) 9 | class ExamProgressAdmin(admin.ModelAdmin): 10 | list_display = ('id', 'status','trainer') 11 | -------------------------------------------------------------------------------- /examplan/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from django.utils.translation import ugettext_lazy as _ 3 | 4 | 5 | class ExamPlanConfig(AppConfig): 6 | name = 'examplan' 7 | icon = 'iconfont icon-plan1' 8 | verbose_name = _('exam plans') 9 | -------------------------------------------------------------------------------- /examplan/filter.py: -------------------------------------------------------------------------------- 1 | from permissions.filters import RoleFilterBackend 2 | 3 | 4 | class IsManagerFilterBackend(RoleFilterBackend): 5 | """ 6 | Filter that only allows users to see their manager objects by the role in department 7 | """ 8 | name = 'IsManagerFilterBackend' 9 | 10 | def filter_queryset(self, request, queryset, view): 11 | 12 | if hasattr(request.user, 'managerdepartment'): 13 | return queryset.filter(department=request.user.managerdepartment) 14 | else: 15 | return queryset.none() 16 | 17 | 18 | class IsManagerProgressFilterBackend(RoleFilterBackend): 19 | """ 20 | Filter that only allows users to see their manager objects by the role in department 21 | """ 22 | name = 'IsManagerFilterBackend' 23 | 24 | def filter_queryset(self, request, queryset, view): 25 | 26 | if hasattr(request.user, 'managerdepartment'): 27 | return queryset.filter(plan__department=request.user.managerdepartment) 28 | else: 29 | return queryset.none() 30 | 31 | 32 | class IsOwnerFilterBackend(RoleFilterBackend): 33 | """ 34 | Filter that only allows users to see their own objects. 35 | """ 36 | name = 'IsOwnerFilterBackend' 37 | 38 | def filter_queryset(self, request, queryset, view): 39 | return queryset.filter(trainer=request.user) 40 | -------------------------------------------------------------------------------- /examplan/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/examplan/migrations/__init__.py -------------------------------------------------------------------------------- /examplan/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /examplan/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from rest_framework import routers 3 | from .api import ExamPlanViewSet, ExamProgressViewSet, ExamProgressViewGroupMemberSet, \ 4 | ExamProgressViewQuestionSet, ExamProgressViewAggregationSet 5 | 6 | # from cruds_adminlte.urls import crud_for_app 7 | router = routers.SimpleRouter(trailing_slash=False) 8 | router.register(r'plan', ExamPlanViewSet) 9 | router.register(r'progress', ExamProgressViewSet) 10 | 11 | 12 | urlpatterns = router.urls 13 | urlpatterns += [path('aggregation', ExamProgressViewAggregationSet.as_view(), name='examprogess_aggregation'), 14 | path('plan//group//member', 15 | ExamProgressViewGroupMemberSet.as_view({'get': 'list'}), name='examplan_group_progresses')] 16 | -------------------------------------------------------------------------------- /examplan/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /experimate/metaclass.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import types 3 | from inspect import isfunction 4 | 5 | 6 | class A(): 7 | def log(self, p): 8 | print(p) 9 | 10 | 11 | class B(A): 12 | def log(self, p): 13 | super().log('b'+p) 14 | print(p) 15 | 16 | 17 | class Base(type): 18 | 19 | def __new__(metacls, cls, bases, namespace): 20 | for base in bases: 21 | print(base) 22 | 23 | upper_namespace = {} 24 | for k, v in namespace.items(): 25 | if not isfunction(v) and not k.startswith('__'): 26 | upper_namespace[k.upper()] = v 27 | else: 28 | upper_namespace[k] = v 29 | return super().__new__(metacls, cls, bases, upper_namespace) 30 | 31 | 32 | class BmixinL(): 33 | def log(self, p): 34 | super(B, self).log('m') 35 | print('Bmixin') 36 | 37 | 38 | class BmixinR(): 39 | def log5(self, p): 40 | super(B, self).log('C') 41 | print('BmixinR') 42 | 43 | 44 | class C(B, BmixinR): 45 | def log(self, p): 46 | super(B, self).log('c'+p) 47 | self.log5('tt') 48 | print(p) 49 | 50 | 51 | class D(C): 52 | def log(self, p): 53 | super().log('d'+p) 54 | print(p) 55 | 56 | 57 | class E(D, metaclass=Base): 58 | e = 10 59 | 60 | def log(self, p): 61 | super().log('e'+p) 62 | print(p) 63 | 64 | 65 | if __name__ == '__main__': 66 | 67 | testE = D() 68 | print(D.__dict__) 69 | testE.log('test') 70 | -------------------------------------------------------------------------------- /frontend/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/frontend/__init__.py -------------------------------------------------------------------------------- /frontend/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /frontend/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class FrontendConfig(AppConfig): 5 | name = 'frontend' 6 | -------------------------------------------------------------------------------- /frontend/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/frontend/migrations/__init__.py -------------------------------------------------------------------------------- /frontend/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /frontend/static/frontend/asset-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "pdfjsWorker.js": "/pdfjsWorker.async.js", 3 | "umi.css": "/umi.css", 4 | "umi.js": "/umi.js", 5 | "favicon.png": "/favicon.png", 6 | "index.html": "/index.html", 7 | "static/index.less": "/static/header_bg.bab1e6cd.png", 8 | "static/UserLayout.less": "/static/login_bg.1c2ab076.png" 9 | } -------------------------------------------------------------------------------- /frontend/static/frontend/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/frontend/static/frontend/favicon.png -------------------------------------------------------------------------------- /frontend/static/frontend/header_bg.bab1e6cd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/frontend/static/frontend/header_bg.bab1e6cd.png -------------------------------------------------------------------------------- /frontend/static/frontend/index.html: -------------------------------------------------------------------------------- 1 | 南京博纳德网络科技有限公司
-------------------------------------------------------------------------------- /frontend/static/frontend/login_bg.1c2ab076.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/frontend/static/frontend/login_bg.1c2ab076.png -------------------------------------------------------------------------------- /frontend/templates/frontend/index.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {% block title %} 11 | 12 | 13 | {{ title }} 14 | 15 | {% endblock %} 16 | {% comment %} {% include "admin_interface/favicon.html" %} {% endcomment %} 17 | 18 | 19 | 20 | 24 | 25 | 26 |
27 | 28 | {% load static %} 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /frontend/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /frontend/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import re_path 2 | from . import views 3 | urlpatterns = [ 4 | re_path('.*', views.index), 5 | ] 6 | -------------------------------------------------------------------------------- /frontend/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.views.decorators.http import condition 3 | import pendulum 4 | 5 | 6 | def etag_func(request, *args, **kwargs): 7 | return '1233333333333' 8 | 9 | 10 | def last_modified_func(request, *args, **kwargs): 11 | return pendulum.datetime(2000, 1, 1) 12 | 13 | 14 | # @condition(last_modified_func=last_modified_func) 15 | def index(request): 16 | return render(request, 'frontend/index.html', {'title': '乐迪网络'}) 17 | -------------------------------------------------------------------------------- /geo/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/geo/__init__.py -------------------------------------------------------------------------------- /geo/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /geo/api.py: -------------------------------------------------------------------------------- 1 | 2 | from rest_framework.views import APIView 3 | from rest_framework.response import Response 4 | from rest_framework import authentication, permissions 5 | from rest_framework import status 6 | from common.jsonrender import EmberJSONRenderer 7 | 8 | import json 9 | 10 | 11 | class GetCitys(APIView): 12 | """ 13 | View to Statistics in the system. 14 | 15 | * Requires token authentication. 16 | * Only admin users are able to access this view. 17 | """ 18 | authentication_classes = [authentication.TokenAuthentication] 19 | #permission_classes = [permissions.IsAdminUser] 20 | renderer_classes = (EmberJSONRenderer,) 21 | 22 | def get(self, request, province, format=None): 23 | with open("geo/city.json", 'r', encoding="utf-8") as f: 24 | data = json.loads(f.read()) 25 | 26 | return Response(data.get(province, {})) 27 | return Response({}) 28 | 29 | 30 | class GetProvince(APIView): 31 | """ 32 | View to Statistics in the system. 33 | 34 | * Requires token authentication. 35 | * Only admin users are able to access this view. 36 | """ 37 | authentication_classes = [authentication.TokenAuthentication] 38 | #permission_classes = [permissions.IsAdminUser] 39 | renderer_classes = (EmberJSONRenderer,) 40 | 41 | def get(self, request, format=None): 42 | with open("geo/province.json", 'r', encoding="utf-8") as f: 43 | data = json.loads(f.read()) 44 | return Response(data) 45 | return Response({}) 46 | -------------------------------------------------------------------------------- /geo/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class GeoConfig(AppConfig): 5 | name = 'geo' 6 | -------------------------------------------------------------------------------- /geo/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/geo/migrations/__init__.py -------------------------------------------------------------------------------- /geo/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /geo/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /geo/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .api import GetCitys, GetProvince 3 | urlpatterns = [ 4 | path('province', GetProvince.as_view()), 5 | path('city/', GetCitys.as_view()), 6 | ] 7 | -------------------------------------------------------------------------------- /geo/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /learn/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/learn/__init__.py -------------------------------------------------------------------------------- /learn/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import LearnPlan, LearnProgress 3 | # Register your models here. 4 | @admin.register(LearnPlan) 5 | class LearnPlanAdmin(admin.ModelAdmin): 6 | list_display = ('id', 'name', 'status') 7 | 8 | 9 | @admin.register(LearnProgress) 10 | class LearnProgressAdmin(admin.ModelAdmin): 11 | list_display = ('id', 'status', 'trainer') 12 | -------------------------------------------------------------------------------- /learn/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from django.utils.translation import ugettext_lazy as _ 3 | 4 | 5 | class LearnConfig(AppConfig): 6 | name = 'learn' 7 | icon = 'iconfont icon-xuexi' 8 | verbose_name = _('learn manager') 9 | -------------------------------------------------------------------------------- /learn/filter.py: -------------------------------------------------------------------------------- 1 | from permissions.filters import RoleFilterBackend 2 | 3 | 4 | class IsManagerFilterBackend(RoleFilterBackend): 5 | """ 6 | Filter that only allows users to see their manager objects by the role in department 7 | """ 8 | name = 'IsManagerFilterBackend' 9 | 10 | def filter_queryset(self, request, queryset, view): 11 | 12 | if hasattr(request.user, 'managerdepartment'): 13 | return queryset.filter(department=request.user.managerdepartment) 14 | else: 15 | return queryset.none() 16 | 17 | 18 | class IsManagerProgressFilterBackend(RoleFilterBackend): 19 | """ 20 | Filter that only allows users to see their manager objects by the role in department 21 | """ 22 | name = 'IsManagerFilterBackend' 23 | 24 | def filter_queryset(self, request, queryset, view): 25 | 26 | if hasattr(request.user, 'managerdepartment'): 27 | return queryset.filter(plan__department=request.user.managerdepartment) 28 | else: 29 | return queryset.none() 30 | 31 | 32 | class IsOwnerFilterBackend(RoleFilterBackend): 33 | """ 34 | Filter that only allows users to see their own objects. 35 | """ 36 | name = 'IsOwnerFilterBackend' 37 | 38 | def filter_queryset(self, request, queryset, view): 39 | return queryset.filter(trainer=request.user) 40 | -------------------------------------------------------------------------------- /learn/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/learn/migrations/__init__.py -------------------------------------------------------------------------------- /learn/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /learn/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from rest_framework import routers 3 | from .api import LearnPlanViewSet, LearnProgressViewSet, LearnProgressViewGroupMemberSet, \ 4 | LearnProgressViewAggregationSet, PublicLearnProgressViewSet 5 | 6 | router = routers.SimpleRouter(trailing_slash=False) 7 | router.register(r'plan', LearnPlanViewSet) 8 | router.register(r'progress', LearnProgressViewSet) 9 | router.register(r'publicprogress', PublicLearnProgressViewSet) 10 | 11 | urlpatterns = router.urls 12 | urlpatterns += [path('plan//group//member', 13 | LearnProgressViewGroupMemberSet.as_view(), name='learnplan_group_progresses'), 14 | path('aggregation', LearnProgressViewAggregationSet.as_view(), name='learnprogess_aggregation') 15 | ] 16 | -------------------------------------------------------------------------------- /learn/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /local.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | volumes: 4 | local_postgres_data: {} 5 | local_postgres_data_backups: {} 6 | 7 | services: 8 | django: &django 9 | build: 10 | context: . 11 | dockerfile: ./compose/local/django/Dockerfile 12 | image: trainserver_local_django 13 | depends_on: 14 | - postgres 15 | - mailhog 16 | volumes: 17 | - .:/app 18 | env_file: 19 | - ./.envs/.local/.django 20 | - ./.envs/.local/.postgres 21 | ports: 22 | - "8000:8000" 23 | command: /start 24 | 25 | postgres: 26 | build: 27 | context: . 28 | dockerfile: ./compose/production/postgres/Dockerfile 29 | image: trainserver_production_postgres 30 | volumes: 31 | - local_postgres_data:/var/lib/postgresql/data 32 | - local_postgres_data_backups:/backups 33 | env_file: 34 | - ./.envs/.local/.postgres 35 | 36 | mailhog: 37 | image: mailhog/mailhog:v1.0.0 38 | ports: 39 | - "8025:8025" 40 | 41 | redis: 42 | image: redis:3.2 43 | 44 | celeryworker: 45 | <<: *django 46 | image: trainserver_local_celeryworker 47 | depends_on: 48 | - redis 49 | - postgres 50 | - mailhog 51 | ports: [] 52 | command: /start-celeryworker 53 | 54 | celerybeat: 55 | <<: *django 56 | image: trainserver_local_celerybeat 57 | depends_on: 58 | - redis 59 | - postgres 60 | - mailhog 61 | ports: [] 62 | command: /start-celerybeat 63 | 64 | flower: 65 | <<: *django 66 | image: trainserver_local_flower 67 | ports: 68 | - "5555:5555" 69 | command: /start-flower 70 | -------------------------------------------------------------------------------- /locale/README.rst: -------------------------------------------------------------------------------- 1 | Translations 2 | ============ 3 | 4 | Translations will be placed in this folder when running:: 5 | 6 | python manage.py makemessages 7 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local") 7 | 8 | try: 9 | from django.core.management import execute_from_command_line 10 | except ImportError: 11 | # The above import may fail for some other reason. Ensure that the 12 | # issue is really that Django is missing to avoid masking other 13 | # exceptions on Python 2. 14 | try: 15 | import django # noqa 16 | except ImportError: 17 | raise ImportError( 18 | "Couldn't import Django. Are you sure it's installed and " 19 | "available on your PYTHONPATH environment variable? Did you " 20 | "forget to activate a virtual environment?" 21 | ) 22 | 23 | raise 24 | 25 | # This allows easy placement of apps within the interior 26 | # trainserver directory. 27 | current_path = os.path.dirname(os.path.abspath(__file__)) 28 | sys.path.append(os.path.join(current_path, "trainserver")) 29 | 30 | execute_from_command_line(sys.argv) 31 | -------------------------------------------------------------------------------- /mystatistics/VisitTimesMiddleware.py: -------------------------------------------------------------------------------- 1 | from django.utils.deprecation import MiddlewareMixin 2 | 3 | from .models import Website_views 4 | 5 | 6 | class VisitTimes(MiddlewareMixin): 7 | 8 | def process_request(self, request): 9 | 10 | # 统计访问的url以及次数 11 | path = request.path 12 | try: 13 | # 取出表格中的东西 14 | visit = Website_views.objects.get(name='webvisitor') 15 | if visit: 16 | # 更改表格中的东西要保存 17 | visit.views() 18 | 19 | except Exception as e: 20 | Website_views.objects.create(name='webvisitor', views=1) 21 | -------------------------------------------------------------------------------- /mystatistics/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/mystatistics/__init__.py -------------------------------------------------------------------------------- /mystatistics/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /mystatistics/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class MystatisticsConfig(AppConfig): 5 | name = 'mystatistics' 6 | icon = 'iconfont .icon-tongji1' 7 | -------------------------------------------------------------------------------- /mystatistics/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/mystatistics/migrations/__init__.py -------------------------------------------------------------------------------- /mystatistics/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | 5 | 6 | class Website_views(models.Model): 7 | """ 8 | 网站访问量统计表:字段ID、总访问量 9 | """ 10 | 11 | views = models.IntegerField(default=0) 12 | name = models.CharField(max_length=20) 13 | 14 | def viewed(self): 15 | self.views += 1 16 | self.save(update_fields=['views']) 17 | -------------------------------------------------------------------------------- /mystatistics/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /mystatistics/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .api import GetStatistics 3 | urlpatterns = [ 4 | path('stats', GetStatistics.as_view()), 5 | 6 | ] 7 | -------------------------------------------------------------------------------- /mystatistics/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /notifications/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | django-notifications 4 | ~~~~~ 5 | A GitHub notification alike app for Django. 6 | :copyright: (c) 2015 by django-notifications team. 7 | :license: BSD, see LICENSE.txt for more details. 8 | """ 9 | 10 | # PEP 386-compliant version number: N.N[.N]+[{a|b|c|rc}N[.N]+][.postN][.devN] 11 | __version__ = '1.4.0' 12 | 13 | default_app_config = 'notifications.apps.notificationsConfig' # pylint: disable=invalid-name 14 | -------------------------------------------------------------------------------- /notifications/apps.py: -------------------------------------------------------------------------------- 1 | ''' Django notifications apps file ''' 2 | # -*- coding: utf-8 -*- 3 | from django.apps import AppConfig 4 | 5 | 6 | class notificationsConfig(AppConfig): 7 | name = "notifications" 8 | icon = 'iconfont icontongzhi1' 9 | 10 | def ready(self): 11 | super(notificationsConfig, self).ready() 12 | # this is for backwards compability 13 | # import notifications.signals 14 | # notifications.notify = notifications.signals.notify 15 | -------------------------------------------------------------------------------- /notifications/filter.py: -------------------------------------------------------------------------------- 1 | from permissions.filters import RoleFilterBackend 2 | 3 | 4 | class IsOwnerFilterBackend(RoleFilterBackend): 5 | """ 6 | Filter that only allows users to see their manager objects by the role in department 7 | """ 8 | name = 'IsOwnerFilterBackend' 9 | 10 | def filter_queryset(self, request, queryset, view): 11 | return queryset.filter(recipient=request.user) 12 | 13 | 14 | class IsOwnerFilterTaskBackend(RoleFilterBackend): 15 | """ 16 | Filter that only allows users to see their manager objects by the role in department 17 | """ 18 | name = 'IsOwnerFilterBackend' 19 | 20 | def filter_queryset(self, request, queryset, view): 21 | return queryset.filter(creater=request.user) 22 | -------------------------------------------------------------------------------- /notifications/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/notifications/migrations/__init__.py -------------------------------------------------------------------------------- /notifications/settings.py: -------------------------------------------------------------------------------- 1 | ''' Django notifications settings file ''' 2 | # -*- coding: utf-8 -*- 3 | from django.conf import settings 4 | 5 | 6 | CONFIG_DEFAULTS = { 7 | 'PAGINATE_BY': 20, 8 | 'USE_JSONFIELD': False, 9 | 'SOFT_DELETE': False, 10 | 'NUM_TO_FETCH': 10, 11 | } 12 | 13 | 14 | def get_config(): 15 | user_config = getattr(settings, 'DJANGO_NOTIFICATIONS_CONFIG', {}) 16 | 17 | config = CONFIG_DEFAULTS.copy() 18 | config.update(user_config) 19 | 20 | return config 21 | -------------------------------------------------------------------------------- /notifications/signals.py: -------------------------------------------------------------------------------- 1 | ''' Django notifications signal file ''' 2 | # -*- coding: utf-8 -*- 3 | from django.dispatch import Signal 4 | 5 | # notify = Signal(providing_args=[ # pylint: disable=invalid-name 6 | # 'recipient', 'actor', 'verb', 'action', 'target', 'description', 7 | # 'created', 'level' 8 | # ]) 9 | notifytask_done = Signal(providing_args=['instance', 'cleaned_data', 'change']) 10 | -------------------------------------------------------------------------------- /notifications/templates/notifications/list.html: -------------------------------------------------------------------------------- 1 |
    2 | {% for notice in notifications %} 3 | {% include 'notifications/notice.html' %} 4 | {% endfor %} 5 |
6 | -------------------------------------------------------------------------------- /notifications/templates/notifications/notice.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 |

7 | 8 | {{ notice.actor }} 9 | {{ notice.verb }} 10 | {% if notice.target %} 11 | of {{ notice.target }} 12 | {% endif %} 13 |

14 | 15 |

{{ notice.timesince }} ago

16 | 17 |

{{ notice.description|linebreaksbr }}

18 | 19 |
20 | {% for action in notice.data.actions %} 21 | {{ action.title }} 22 | {% endfor %} 23 |
24 |
-------------------------------------------------------------------------------- /notifications/templates/notifications/test_tags.html: -------------------------------------------------------------------------------- 1 | {% load notifications_tags %} 2 | 3 | {% register_notify_callbacks callbacks='fill_notification_menu,fill_notification_badge' %} 4 | 5 | {% notifications_unread as unread %} 6 | {{ unread }} 7 | {% live_notify_badge %} 8 | {% live_notify_list %} 9 | -------------------------------------------------------------------------------- /notifications/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/notifications/templatetags/__init__.py -------------------------------------------------------------------------------- /notifications/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/notifications/tests/__init__.py -------------------------------------------------------------------------------- /notifications/tests/static/notifications/live-test.js: -------------------------------------------------------------------------------- 1 | console.log('running tester') 2 | 3 | function make_notification() { 4 | var r = new XMLHttpRequest(); 5 | r.open("GET", '/test_make/', true); 6 | r.send(); 7 | } -------------------------------------------------------------------------------- /notifications/tests/templates/test_live.html: -------------------------------------------------------------------------------- 1 | {% load static notifications_tags %} 2 | 3 | 4 | 5 | {% register_notify_callbacks callbacks='fill_notification_list,fill_notification_badge' fetch=20 refresh_period=5 %} 6 | 7 | There are this many notifications pending: {% live_notify_badge %} 8 | 9 | 10 | {% live_notify_list %} 11 | -------------------------------------------------------------------------------- /notifications/tests/urls.py: -------------------------------------------------------------------------------- 1 | ''' Django notification urls for tests ''' 2 | # -*- coding: utf-8 -*- 3 | from distutils.version import StrictVersion # pylint: disable=no-name-in-module,import-error 4 | 5 | from django import get_version 6 | from django.contrib import admin 7 | from django.contrib.auth.views import login 8 | from notifications.tests.views import (live_tester, # pylint: disable=no-name-in-module,import-error 9 | make_notification) 10 | 11 | if StrictVersion(get_version()) >= StrictVersion('2.0'): 12 | from django.urls import include, path # noqa 13 | urlpatterns = [ 14 | path('test_make/', make_notification), 15 | path('test/', live_tester), 16 | path('login/', login, name='login'), # reverse for django login is not working 17 | path('admin/', admin.site.urls), 18 | path('', include('notifications.urls', namespace='notifications')), 19 | ] 20 | else: 21 | from django.conf.urls import include, url 22 | urlpatterns = [ 23 | url(r'^login/$', login, name='login'), # reverse for django login is not working 24 | url(r'^test_make/', make_notification), 25 | url(r'^test/', live_tester), 26 | url(r'^', include('notifications.urls', namespace='notifications')), 27 | url(r'^admin/', admin.site.urls), 28 | ] 29 | -------------------------------------------------------------------------------- /notifications/tests/views.py: -------------------------------------------------------------------------------- 1 | ''' Django notifications views for tests ''' 2 | # -*- coding: utf-8 -*- 3 | import random 4 | 5 | from django.contrib.auth.decorators import login_required 6 | from django.shortcuts import render 7 | from notifications.signals import notify 8 | 9 | 10 | @login_required 11 | def live_tester(request): 12 | notify.send(sender=request.user, recipient=request.user, verb='you loaded the page') 13 | 14 | return render(request, 'test_live.html', { 15 | 'unread_count': request.user.notifications.unread().count(), 16 | 'notifications': request.user.notifications.all() 17 | }) 18 | 19 | 20 | def make_notification(request): 21 | 22 | the_notification = random.choice([ 23 | 'reticulating splines', 24 | 'cleaning the car', 25 | 'jumping the shark', 26 | 'testing the app', 27 | 'attaching the plumbus', 28 | ]) 29 | 30 | notify.send(sender=request.user, recipient=request.user, 31 | verb='you asked for a notification - you are ' + the_notification) 32 | -------------------------------------------------------------------------------- /notifications/utils.py: -------------------------------------------------------------------------------- 1 | '''' Django notifications utils file ''' 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | 5 | 6 | if sys.version > '3': 7 | long = int # pylint: disable=invalid-name 8 | 9 | 10 | def slug2id(slug): 11 | return long(slug) - 110909 12 | 13 | 14 | def id2slug(notification_id): 15 | return notification_id + 110909 16 | -------------------------------------------------------------------------------- /openapi-schema.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/openapi-schema.json -------------------------------------------------------------------------------- /orgs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/orgs/__init__.py -------------------------------------------------------------------------------- /orgs/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Department, Excelfile 3 | # Register your models here. 4 | 5 | from import_export.admin import ImportExportModelAdmin, ImportExportMixin 6 | from import_export import resources 7 | 8 | from mptt.admin import MPTTModelAdmin 9 | 10 | 11 | class DepartmentResource(resources.ModelResource): 12 | 13 | class Meta: 14 | model = Department 15 | fields = ('id', 'name', 'parent',) 16 | readonly_fields = ('slug',) 17 | 18 | 19 | @admin.register(Department) 20 | class OrgsAdmin(MPTTModelAdmin): 21 | mptt_level_indent = 40 22 | resource_class = DepartmentResource 23 | list_display = ('name', 'slug', 'parent', 'id') 24 | readonly_fields = ('slug', 'id',) 25 | 26 | 27 | @admin.register(Excelfile) 28 | class ExcelfilefileAdmin(admin.ModelAdmin): 29 | list_display = ('id', 'excelfile') 30 | -------------------------------------------------------------------------------- /orgs/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from django.utils.translation import ugettext_lazy as _ 3 | 4 | 5 | class OrgsConfig(AppConfig): 6 | name = 'orgs' 7 | icon = 'iconfont icontongzhi1' 8 | verbose_name = _("organization manager") 9 | -------------------------------------------------------------------------------- /orgs/filter.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from rest_framework.filters import BaseFilterBackend 4 | from .models import Department 5 | 6 | 7 | class IsManagerFilterBackend(BaseFilterBackend): 8 | """ 9 | Filter that only allows users to see their manager objects by the role in department 10 | """ 11 | name = 'IsManagerFilterBackend' 12 | 13 | def filter_queryset(self, request, queryset, view): 14 | 15 | if hasattr(request.user, 'managerdepartment'): 16 | if view.action == 'list': 17 | if not (request.query_params.get('id', None) or request.query_params.get('slug', None) or 18 | request.query_params.get('name', None)): 19 | return queryset.filter(id=request.user.managerdepartment.id) 20 | return queryset.filter(slug__startswith=request.user.managerdepartment.slug) 21 | else: 22 | return queryset.none() 23 | 24 | 25 | class IsUserFilterBackend(BaseFilterBackend): 26 | """ 27 | Filter that only allows users to see their own objects. 28 | """ 29 | name = 'IsUserFilterBackend' 30 | 31 | def filter_queryset(self, request, queryset, view): 32 | return queryset.filter(id=request.user.department.id) 33 | 34 | 35 | class IsAdminFilterBackend(BaseFilterBackend): 36 | """ 37 | Filter that only allows admin to see their all objects. 38 | """ 39 | name = 'IsAdminFilterBackend' 40 | 41 | def filter_queryset(self, request, queryset, view): 42 | if view.action == 'list': 43 | if not (request.query_params.get('id', None) or request.query_params.get('slug', None) or 44 | request.query_params.get('name', None)): 45 | return Department.objects.root_nodes() 46 | 47 | return queryset 48 | -------------------------------------------------------------------------------- /orgs/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/orgs/migrations/__init__.py -------------------------------------------------------------------------------- /orgs/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /orgs/urls.py: -------------------------------------------------------------------------------- 1 | from rest_framework import routers 2 | from .api import OrgViewSet 3 | from .api import ExcelfileUploadView 4 | from django.urls import path 5 | router = routers.SimpleRouter(trailing_slash=False) 6 | router.register(r'departments', OrgViewSet) 7 | # router.register(r'departments/detail', DepartmentDetailView) 8 | # 9 | urlpatterns = router.urls 10 | urlpatterns += [ 11 | # path('departments/', OrgViewSet.as_view({'get': 'list'})), 12 | path('upload', ExcelfileUploadView.as_view(), name='org_upload') 13 | # #path('departments//', OrganizationViewSet.as_view({'get': 'list'})), 14 | ] 15 | -------------------------------------------------------------------------------- /orgs/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /permissions/.ipynb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/permissions/.ipynb -------------------------------------------------------------------------------- /permissions/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | default_app_config = 'permissions.apps.PermissionsApp' 4 | -------------------------------------------------------------------------------- /permissions/apps.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.apps import apps 4 | from django.utils.translation import ugettext_lazy as _ 5 | 6 | 7 | from django.apps import AppConfig 8 | 9 | 10 | class PermissionsConfig(AppConfig): 11 | has_rest_api = True 12 | has_tests = True 13 | icon = 'iconfont iconpermissions' 14 | name = 'permissions' 15 | verbose_name = _('permissions') 16 | 17 | def ready(self): 18 | pass 19 | # from .permissonregist import OperationGenerator 20 | # from .filterregist import RestFilterGenerator 21 | # permissions = OperationGenerator().update_restPermissionModel() 22 | # rolefilters = RestFilterGenerator().update_roleFilterModel() 23 | 24 | # print('permissions count is {}'.format(len(permissions))) 25 | # print('rolefilters count is {}'.format(len(rolefilters))) 26 | -------------------------------------------------------------------------------- /permissions/exceptions.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | 4 | class PermissionError(Exception): 5 | pass 6 | 7 | 8 | class InvalidNamespace(PermissionError): 9 | pass 10 | -------------------------------------------------------------------------------- /permissions/fixtures/role.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "permissions.role", 4 | "pk": 2, 5 | "fields": { 6 | "name": "stu", 7 | "display": "stu" 8 | } 9 | }, 10 | { 11 | "model": "permissions.role", 12 | "pk": 5, 13 | "fields": { 14 | "name": "admin", 15 | "display": "admin" 16 | } 17 | }, 18 | { 19 | "model": "permissions.role", 20 | "pk": 6, 21 | "fields": { 22 | "name": "trainmanager", 23 | "display": "trainmanager" 24 | } 25 | } 26 | ] -------------------------------------------------------------------------------- /permissions/handlers.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.core import management 4 | 5 | 6 | def purge_permissions(**kwargs): 7 | management.call_command('purgepermissions') 8 | -------------------------------------------------------------------------------- /permissions/icons.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | # from appearance.classes import Icon 4 | 5 | # icon_role_create = Icon(driver_name='fontawesome', symbol='plus') 6 | # icon_role_list = Icon(driver_name='fontawesome', symbol='user-secret') 7 | -------------------------------------------------------------------------------- /permissions/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/permissions/management/__init__.py -------------------------------------------------------------------------------- /permissions/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/permissions/management/commands/__init__.py -------------------------------------------------------------------------------- /permissions/management/commands/purgepermissions.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.core.management.base import BaseCommand 4 | from ...models import Operation 5 | 6 | 7 | class Command(BaseCommand): 8 | help = 'Remove obsolete permissions from the cache' 9 | 10 | def handle(self, *args, **options): 11 | for permission in Operation.objects.all(): 12 | try: 13 | permission.get( 14 | pk='{}.{}'.format(permission.namespace, permission.name), 15 | proxy_only=True 16 | ) 17 | except KeyError: 18 | permission.delete() 19 | -------------------------------------------------------------------------------- /permissions/management/commands/rolepermission.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | 3 | 4 | from ...permissonregist import OperationGenerator 5 | from ...filterregist import RestFilterGenerator 6 | 7 | 8 | class Command(BaseCommand): 9 | help = 'add perission permissions to the database' 10 | 11 | def handle(self, *args, **options): 12 | 13 | permissions = OperationGenerator().update_restPermissionModel() 14 | print('permissions count is {}'.format(len(permissions))) 15 | rolefilters = RestFilterGenerator().update_roleFilterModel() 16 | print('rolefilters count is {}'.format(len(rolefilters))) 17 | -------------------------------------------------------------------------------- /permissions/managers.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | import logging 4 | 5 | from django.db import models 6 | from django.contrib.contenttypes.models import ContentType 7 | 8 | logger = logging.getLogger(__name__) 9 | 10 | 11 | class RoleManager(models.Manager): 12 | use_in_migrations = True 13 | 14 | def get_by_natural_key(self, name): 15 | return self.get(name=name) 16 | 17 | 18 | class StoredPermissionManager(models.Manager): 19 | def get_by_natural_key(self, namespace, name): 20 | return self.get(namespace=namespace, name=name) 21 | 22 | def get_for_holder(self, holder): 23 | ct = ContentType.objects.get_for_model(holder) 24 | return self.model.objects.filter( 25 | permissionholder__holder_type=ct 26 | ).filter(permissionholder__holder_id=holder.pk) 27 | 28 | 29 | class OperationManager(models.Manager): 30 | use_in_migrations = True 31 | 32 | def get_by_natural_key(self, viewname, action): 33 | return self.get(viewName=viewname, action=action) 34 | 35 | 36 | class RestPermissionManager(models.Manager): 37 | use_in_migrations = True 38 | 39 | def get_by_natural_key(self, viewname, action): 40 | return self.get(viewName=viewname, action=action) 41 | # def get_for_holder(self, holder): 42 | # ct = ContentType.objects.get_for_model(holder) 43 | # return self.model.objects.filter( 44 | # permissionholder__holder_type=ct 45 | # ).filter(permissionholder__holder_id=holder.pk) 46 | -------------------------------------------------------------------------------- /permissions/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/permissions/migrations/__init__.py -------------------------------------------------------------------------------- /permissions/search.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | # from django.utils.translation import ugettext_lazy as _ 4 | 5 | # from dynamic_search.classes import SearchModel 6 | 7 | # from .permissions import permission_role_view 8 | 9 | # role_search = SearchModel( 10 | # app_label='permissions', model_name='Role', 11 | # permission=permission_role_view, 12 | # serializer_string='permissions.serializers.RoleSerializer' 13 | # ) 14 | 15 | # role_search.add_model_field( 16 | # field='name', name=_('Label') 17 | # ) 18 | 19 | # role_search.add_model_field( 20 | # field='groups__name', name=_('Group name') 21 | # ) 22 | -------------------------------------------------------------------------------- /permissions/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from .literals import * # NOQA 2 | -------------------------------------------------------------------------------- /permissions/tests/literals.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | TEST_ROLE_2_LABEL = 'test role 2' 4 | TEST_ROLE_LABEL = 'test role' 5 | TEST_ROLE_LABEL_EDITED = 'test role name edited' 6 | -------------------------------------------------------------------------------- /permissions/tests/test_inlineadmin.py: -------------------------------------------------------------------------------- 1 | 2 | from django.test import TestCase 3 | from common.utils import get_admin_site 4 | 5 | 6 | from ..inlineadmin import CheckboxInlineModelAdmin 7 | from ..models import Role, RoleOperationshipWithFilter, Operation 8 | 9 | 10 | class RoleOperationshipWithFilterInline(CheckboxInlineModelAdmin): 11 | 12 | model = RoleOperationshipWithFilter 13 | fk_name = 'role' 14 | 15 | 16 | class InlineadminTestCase(TestCase): 17 | def setUp(self): 18 | super(InlineadminTestCase, self).setUp() 19 | self.testInline = RoleOperationshipWithFilterInline(Role, get_admin_site()) 20 | kwargs = {"name": 'api_account_info_retrieve', "viewName": 'users.api.AccountDetailView', 21 | "method": 'get', "path": '/api/account/info', "action": 'retrieve'} 22 | Operation(**kwargs).save() 23 | 24 | def test_get_unique_name(self): 25 | # self.testInline = RoleOperationshipWithFilterInline(Role, get_admin_site()) 26 | 27 | self.assertTrue(self.testInline._get_unique_name() == 'operation') 28 | 29 | def test_get_unique_foreign_model(self): 30 | # self.testInline = RoleOperationshipWithFilterInline(Role, get_admin_site()) 31 | 32 | model = self.testInline._get_unique_foreign_model() 33 | self.assertEqual(type(model), type(Operation)) 34 | 35 | def test_get_max_num(self): 36 | 37 | number = self.testInline.get_max_num(request=None) 38 | self.assertEqual(number, 1) 39 | -------------------------------------------------------------------------------- /permissions/tests/test_models.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.core.exceptions import PermissionDenied 4 | 5 | from common.tests import BaseTestCase 6 | 7 | from ..classes import Permission 8 | from ..permissions import permission_role_view 9 | 10 | 11 | class PermissionTestCase(BaseTestCase): 12 | def setUp(self): 13 | super(PermissionTestCase, self).setUp() 14 | 15 | def test_no_permissions(self): 16 | with self.assertRaises(PermissionDenied): 17 | Permission.check_permissions( 18 | requester=self.user, permissions=(permission_role_view,) 19 | ) 20 | 21 | def test_with_permissions(self): 22 | self.group.user_set.add(self.user) 23 | self.role.permissions.add(permission_role_view.stored_permission) 24 | self.role.groups.add(self.group) 25 | 26 | try: 27 | Permission.check_permissions( 28 | requester=self.user, permissions=(permission_role_view,) 29 | ) 30 | except PermissionDenied: 31 | self.fail('PermissionDenied exception was not expected.') 32 | -------------------------------------------------------------------------------- /permissions/urls.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.conf.urls import url 4 | 5 | # from .api_views import APIPermissionList, APIRoleListView, APIRoleView 6 | 7 | 8 | # api_urls = [ 9 | # url(r'^permissions/$', APIPermissionList.as_view(), name='permission-list'), 10 | # url(r'^roles/$', APIRoleListView.as_view(), name='role-list'), 11 | # url(r'^roles/(?P[0-9]+)/$', APIRoleView.as_view(), name='role-detail'), 12 | # ] 13 | -------------------------------------------------------------------------------- /production.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | volumes: 4 | production_postgres_data: {} 5 | production_postgres_data_backups: {} 6 | production_caddy: {} 7 | 8 | services: 9 | django: &django 10 | build: 11 | context: . 12 | dockerfile: ./compose/production/django/Dockerfile 13 | image: trainserver_production_django 14 | depends_on: 15 | - postgres 16 | - redis 17 | env_file: 18 | - ./.envs/.production/.django 19 | - ./.envs/.production/.postgres 20 | command: /start 21 | 22 | postgres: 23 | build: 24 | context: . 25 | dockerfile: ./compose/production/postgres/Dockerfile 26 | image: trainserver_production_postgres 27 | volumes: 28 | - production_postgres_data:/var/lib/postgresql/data 29 | - production_postgres_data_backups:/backups 30 | env_file: 31 | - ./.envs/.production/.postgres 32 | 33 | caddy: 34 | build: 35 | context: . 36 | dockerfile: ./compose/production/caddy/Dockerfile 37 | image: trainserver_production_caddy 38 | depends_on: 39 | - django 40 | volumes: 41 | - production_caddy:/root/.caddy 42 | env_file: 43 | - ./.envs/.production/.caddy 44 | ports: 45 | - "0.0.0.0:80:80" 46 | - "0.0.0.0:443:443" 47 | 48 | redis: 49 | image: redis:3.2 50 | 51 | celeryworker: 52 | <<: *django 53 | image: trainserver_production_celeryworker 54 | command: /start-celeryworker 55 | 56 | celerybeat: 57 | <<: *django 58 | image: trainserver_production_celerybeat 59 | command: /start-celerybeat 60 | 61 | flower: 62 | <<: *django 63 | image: trainserver_production_flower 64 | ports: 65 | - "5555:5555" 66 | command: /start-flower 67 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | DJANGO_SETTINGS_MODULE=config.settings.test 3 | -------------------------------------------------------------------------------- /questions/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | django-notifications 4 | ~~~~~ 5 | A GitHub notification alike app for Django. 6 | :copyright: (c) 2015 by django-notifications team. 7 | :license: BSD, see LICENSE.txt for more details. 8 | """ 9 | 10 | # PEP 386-compliant version number: N.N[.N]+[{a|b|c|rc}N[.N]+][.postN][.devN] 11 | __version__ = '1.4.0' 12 | 13 | default_app_config = 'notifications.apps.notificationsConfig' # pylint: disable=invalid-name 14 | -------------------------------------------------------------------------------- /questions/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | -------------------------------------------------------------------------------- /questions/apps.py: -------------------------------------------------------------------------------- 1 | ''' Django questions apps file ''' 2 | # -*- coding: utf-8 -*- 3 | from django.apps import AppConfig 4 | from django.utils.translation import ugettext_lazy as _ 5 | 6 | 7 | class questionsConfig(AppConfig): 8 | name = "questions" 9 | icon = 'iconfont icon-wenti' 10 | verbose_name = _("questions manager") 11 | 12 | def ready(self): 13 | super(questionsConfig, self).ready() 14 | # this is for backwards compability 15 | import questions.signals 16 | questions.ask = questions.signals.ask 17 | -------------------------------------------------------------------------------- /questions/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/questions/migrations/__init__.py -------------------------------------------------------------------------------- /questions/serializers.py: -------------------------------------------------------------------------------- 1 | # from django.conf import settings 2 | # from django.utils.http import urlsafe_base64_decode as uid_decoder 3 | # from django.utils.translation import ugettext_lazy as _ 4 | # from django.utils.encoding import force_text 5 | # from rest_framework.exceptions import ValidationError 6 | from rest_framework import serializers # , exceptions 7 | 8 | from .models import Question 9 | 10 | 11 | class QuestionSerializer(serializers.ModelSerializer): 12 | class Meta: 13 | model = Question 14 | fields = ('id', 'created_time', 'answer_user_id', 'unanswer', 'verb', 'title', 15 | 'ask_content', 'ask_time', 'answer_content', 'answer_time', 'position') 16 | read_only_fields = ('created_time', 'group_no',) 17 | ordering = ['created_time'] 18 | -------------------------------------------------------------------------------- /questions/settings.py: -------------------------------------------------------------------------------- 1 | ''' Django notifications settings file ''' 2 | # -*- coding: utf-8 -*- 3 | from django.conf import settings 4 | 5 | 6 | CONFIG_DEFAULTS = { 7 | 'PAGINATE_BY': 20, 8 | 'USE_JSONFIELD': False, 9 | 'SOFT_DELETE': False, 10 | 'NUM_TO_FETCH': 10, 11 | } 12 | 13 | 14 | def get_config(): 15 | user_config = getattr(settings, 'DJANGO_NOTIFICATIONS_CONFIG', {}) 16 | 17 | config = CONFIG_DEFAULTS.copy() 18 | config.update(user_config) 19 | 20 | return config 21 | -------------------------------------------------------------------------------- /questions/signals.py: -------------------------------------------------------------------------------- 1 | ''' Django notifications signal file ''' 2 | # -*- coding: utf-8 -*- 3 | from django.dispatch import Signal 4 | 5 | ask = Signal(providing_args=[ # pylint: disable=invalid-name 6 | 'answer', 'actor', 'verb', 'action', 'target', 'description', 7 | 'created', 'level' 8 | ]) 9 | -------------------------------------------------------------------------------- /questions/templates/notifications/list.html: -------------------------------------------------------------------------------- 1 |
    2 | {% for notice in notifications %} 3 | {% include 'notifications/notice.html' %} 4 | {% endfor %} 5 |
6 | -------------------------------------------------------------------------------- /questions/templates/notifications/notice.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 |

7 | 8 | {{ notice.actor }} 9 | {{ notice.verb }} 10 | {% if notice.target %} 11 | of {{ notice.target }} 12 | {% endif %} 13 |

14 | 15 |

{{ notice.timesince }} ago

16 | 17 |

{{ notice.description|linebreaksbr }}

18 | 19 |
20 | {% for action in notice.data.actions %} 21 | {{ action.title }} 22 | {% endfor %} 23 |
24 |
-------------------------------------------------------------------------------- /questions/templates/notifications/test_tags.html: -------------------------------------------------------------------------------- 1 | {% load notifications_tags %} 2 | 3 | {% register_notify_callbacks callbacks='fill_notification_menu,fill_notification_badge' %} 4 | 5 | {% notifications_unread as unread %} 6 | {{ unread }} 7 | {% live_notify_badge %} 8 | {% live_notify_list %} 9 | -------------------------------------------------------------------------------- /questions/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/questions/templatetags/__init__.py -------------------------------------------------------------------------------- /questions/urls.py: -------------------------------------------------------------------------------- 1 | ''' Django notification urls file ''' 2 | # -*- coding: utf-8 -*- 3 | from distutils.version import StrictVersion # pylint: disable=no-name-in-module,import-error 4 | 5 | from django import get_version 6 | 7 | from . import views 8 | 9 | if StrictVersion(get_version()) >= StrictVersion('2.0'): 10 | from django.urls import re_path as pattern 11 | else: 12 | from django.conf.urls import url as pattern 13 | 14 | """ 15 | urlpatterns = [ 16 | 17 | pattern(r'^$', views.AllNotificationsList.as_view(), name='all'), 18 | pattern(r'^unread/$', views.UnreadNotificationsList.as_view(), name='unread'), 19 | pattern(r'^mark-all-as-read/$', views.mark_all_as_read, name='mark_all_as_read'), 20 | pattern(r'^mark-as-read/(?P\d+)/$', views.mark_as_read, name='mark_as_read'), 21 | pattern(r'^mark-as-unread/(?P\d+)/$', views.mark_as_unread, name='mark_as_unread'), 22 | pattern(r'^delete/(?P\d+)/$', views.delete, name='delete'), 23 | pattern(r'^api/unread_count/$', views.live_unread_notification_count, name='live_unread_notification_count'), 24 | pattern(r'^api/all_count/$', views.live_all_notification_count, name='live_all_notification_count'), 25 | pattern(r'^api/unread_list/$', views.live_unread_notification_list, name='live_unread_notification_list'), 26 | pattern(r'^api/all_list/', views.live_all_notification_list, name='live_all_notification_list'), 27 | 28 | ] 29 | """ 30 | app_name = 'questions' 31 | -------------------------------------------------------------------------------- /questions/utils.py: -------------------------------------------------------------------------------- 1 | '''' Django notifications utils file ''' 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | 5 | 6 | if sys.version > '3': 7 | long = int # pylint: disable=invalid-name 8 | 9 | 10 | def slug2id(slug): 11 | return long(slug) - 110909 12 | 13 | 14 | def id2slug(questions_id): 15 | return notification_id + 110909 16 | -------------------------------------------------------------------------------- /requirements/base.txt: -------------------------------------------------------------------------------- 1 | django==2.2.10 # pyup: < 3.0 # https://www.djangoproject.com/ 2 | django-environ==0.4.5 # https://github.com/joke2k/django-environ 3 | django-colorfield==0.1.15 4 | django-filter==2.4.0 5 | django-imagekit==4.0.2 6 | django-json-widget==0.2.0 7 | django-jsoneditor==0.1.4 8 | django-mptt==0.10.0 9 | django-redis==4.11.0 10 | django-reverse-admin==2.8.1 11 | django-reversion==3.0.4 12 | django-simple-history==2.8.0 13 | djangorestframework==3.10.3 # djangorestframework 使用自行修改过的,安装依赖----------------------------------------------------------------- 14 | djangorestframework_filters==1.0.0.dev0 15 | djangorestframework_recursive==0.1.2 16 | drf_flex_fields==0.7.5 17 | django-import-export 18 | django-cacheops 19 | django-admin-timeline 20 | django-model-utils 21 | django-extensions==2.2.8 # https://github.com/django-extensions/django-extensions 22 | pandas==0.24.2 23 | ffmpeg-python==0.1.17 24 | uritemplate==3.0.0 25 | openpyxl==2.6.1 26 | coreschema==0.0.4 27 | xlrd==1.2.0 28 | pytz==2018.5 29 | django_json_widget==0.2.0 30 | argon2-cffi==19.2.0 31 | 32 | moviepy==1.0.0 # 依赖 装在比较慢 imageio-ffmpeg 33 | Pillow==9.3.0 34 | requests==2.21.0 35 | Markdown==3.0.1 36 | Pygments==2.7.4 37 | psycopg2-binary==2.8.4 38 | 39 | six==1.14.0 40 | pendulum==2.0.5 41 | PyPDF2==1.27.5 42 | PyYAML==5.4 43 | -------------------------------------------------------------------------------- /requirements/local.txt: -------------------------------------------------------------------------------- 1 | -r ./base.txt 2 | 3 | django-cors-headers==3.2.0 4 | drf_yasg==1.17.0 5 | # format 6 | autopep8 7 | 8 | Werkzeug==1.0.0 # https://github.com/pallets/werkzeug 9 | ipdb==0.12.3 # https://github.com/gotcha/ipdb 10 | # Sphinx==2.4.0 # https://github.com/sphinx-doc/sphinx {% endcomment %} 11 | 12 | # Testing 13 | # ------------------------------------------------------------------------------ 14 | mypy==0.761 # https://github.com/python/mypy 15 | mypy-extensions 16 | django-stubs==1.4.0 # https://github.com/typeddjango/django-stubs 17 | pytest==5.3.5 # https://github.com/pytest-dev/pytest 18 | pytest-sugar==0.9.2 # https://github.com/Frozenball/pytest-sugar 19 | 20 | # Code quality 21 | # ------------------------------------------------------------------------------ 22 | flake8==3.7.9 # https://github.com/PyCQA/flake8 23 | coverage==5.0.3 # https://github.com/nedbat/coveragepy 24 | black==19.10b0 # https://github.com/ambv/black 25 | pylint-django==2.0.13 # https://github.com/PyCQA/pylint-django 26 | pylint-celery==0.3 # https://github.com/PyCQA/pylint-celery 27 | pre-commit==2.0.1 # https://github.com/pre-commit/pre-commit 28 | 29 | # Django 30 | # ------------------------------------------------------------------------------ 31 | factory-boy==2.12.0 # https://github.com/FactoryBoy/factory_boy 32 | 33 | django-debug-toolbar==2.2 # https://github.com/jazzband/django-debug-toolbar 34 | 35 | django-coverage-plugin==1.8.0 # https://github.com/nedbat/django_coverage_plugin 36 | pytest-django==3.8.0 # https://github.com/pytest-dev/pytest-django 37 | -------------------------------------------------------------------------------- /requirements/production.txt: -------------------------------------------------------------------------------- 1 | # PRECAUTION: avoid production dependencies that aren't in development 2 | 3 | -r ./base.txt 4 | 5 | gunicorn==20.0.4 # https://github.com/benoitc/gunicorn 6 | # django-compressor 7 | # whitenoise 8 | # Brotli 9 | supervison 10 | django-admin-honeypot==1.1.0 11 | psycopg2-binary==2.8.4 # https://github.com/psycopg/psycopg2 12 | # raven==6.9.0 # https://github.com/getsentry/raven-python 13 | 14 | # Django 15 | # ------------------------------------------------------------------------------ 16 | # django-storages[boto3]==1.7.1 # https://github.com/jschneier/django-storages 17 | # django-anymail[mailgun]==4.3 # https://github.com/anymail/django-anymail 18 | -------------------------------------------------------------------------------- /rest_framework/__init__.py: -------------------------------------------------------------------------------- 1 | r""" 2 | ______ _____ _____ _____ __ 3 | | ___ \ ___/ ___|_ _| / _| | | 4 | | |_/ / |__ \ `--. | | | |_ _ __ __ _ _ __ ___ _____ _____ _ __| |__ 5 | | /| __| `--. \ | | | _| '__/ _` | '_ ` _ \ / _ \ \ /\ / / _ \| '__| |/ / 6 | | |\ \| |___/\__/ / | | | | | | | (_| | | | | | | __/\ V V / (_) | | | < 7 | \_| \_\____/\____/ \_/ |_| |_| \__,_|_| |_| |_|\___| \_/\_/ \___/|_| |_|\_| 8 | """ 9 | 10 | __title__ = 'Django REST framework' 11 | __version__ = '3.10.3' 12 | __author__ = 'Tom Christie' 13 | __license__ = 'BSD 2-Clause' 14 | __copyright__ = 'Copyright 2011-2019 Encode OSS Ltd' 15 | 16 | # Version synonym 17 | VERSION = __version__ 18 | 19 | # Header encoding (see RFC5987) 20 | HTTP_HEADER_ENCODING = 'iso-8859-1' 21 | 22 | # Default datetime input and output formats 23 | ISO_8601 = 'iso-8601' 24 | 25 | default_app_config = 'rest_framework.apps.RestFrameworkConfig' 26 | 27 | 28 | class RemovedInDRF311Warning(DeprecationWarning): 29 | pass 30 | 31 | 32 | class RemovedInDRF312Warning(PendingDeprecationWarning): 33 | pass 34 | -------------------------------------------------------------------------------- /rest_framework/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class RestFrameworkConfig(AppConfig): 5 | name = 'rest_framework' 6 | verbose_name = "Django REST framework" 7 | 8 | def ready(self): 9 | # Add System checks 10 | from .checks import pagination_system_check # NOQA 11 | -------------------------------------------------------------------------------- /rest_framework/authtoken/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'rest_framework.authtoken.apps.AuthTokenConfig' 2 | -------------------------------------------------------------------------------- /rest_framework/authtoken/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from rest_framework.authtoken.models import Token 4 | 5 | 6 | class TokenAdmin(admin.ModelAdmin): 7 | list_display = ('key', 'user', 'created') 8 | fields = ('user',) 9 | ordering = ('-created',) 10 | 11 | 12 | admin.site.register(Token, TokenAdmin) 13 | -------------------------------------------------------------------------------- /rest_framework/authtoken/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from django.utils.translation import gettext_lazy as _ 3 | 4 | 5 | class AuthTokenConfig(AppConfig): 6 | name = 'rest_framework.authtoken' 7 | verbose_name = _("Auth Token") 8 | -------------------------------------------------------------------------------- /rest_framework/authtoken/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/rest_framework/authtoken/management/__init__.py -------------------------------------------------------------------------------- /rest_framework/authtoken/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/rest_framework/authtoken/management/commands/__init__.py -------------------------------------------------------------------------------- /rest_framework/authtoken/management/commands/drf_create_token.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from django.core.management.base import BaseCommand, CommandError 3 | 4 | from rest_framework.authtoken.models import Token 5 | 6 | UserModel = get_user_model() 7 | 8 | 9 | class Command(BaseCommand): 10 | help = 'Create DRF Token for a given user' 11 | 12 | def create_user_token(self, username, reset_token): 13 | user = UserModel._default_manager.get_by_natural_key(username) 14 | 15 | if reset_token: 16 | Token.objects.filter(user=user).delete() 17 | 18 | token = Token.objects.get_or_create(user=user) 19 | return token[0] 20 | 21 | def add_arguments(self, parser): 22 | parser.add_argument('username', type=str) 23 | 24 | parser.add_argument( 25 | '-r', 26 | '--reset', 27 | action='store_true', 28 | dest='reset_token', 29 | default=False, 30 | help='Reset existing User token and create a new one', 31 | ) 32 | 33 | def handle(self, *args, **options): 34 | username = options['username'] 35 | reset_token = options['reset_token'] 36 | 37 | try: 38 | token = self.create_user_token(username, reset_token) 39 | except UserModel.DoesNotExist: 40 | raise CommandError( 41 | 'Cannot create the Token: user {} does not exist'.format( 42 | username) 43 | ) 44 | self.stdout.write( 45 | 'Generated token {} for user {}'.format(token.key, username)) 46 | -------------------------------------------------------------------------------- /rest_framework/authtoken/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/rest_framework/authtoken/migrations/__init__.py -------------------------------------------------------------------------------- /rest_framework/authtoken/models.py: -------------------------------------------------------------------------------- 1 | import binascii 2 | import os 3 | 4 | from django.conf import settings 5 | from django.db import models 6 | from django.utils.translation import gettext_lazy as _ 7 | 8 | 9 | class Token(models.Model): 10 | """ 11 | The default authorization token model. 12 | """ 13 | key = models.CharField(_("Key"), max_length=40, primary_key=True) 14 | user = models.OneToOneField( 15 | settings.AUTH_USER_MODEL, related_name='auth_token', 16 | on_delete=models.CASCADE, verbose_name=_("User") 17 | ) 18 | created = models.DateTimeField(_("Created"), auto_now_add=True) 19 | 20 | class Meta: 21 | # Work around for a bug in Django: 22 | # https://code.djangoproject.com/ticket/19422 23 | # 24 | # Also see corresponding ticket: 25 | # https://github.com/encode/django-rest-framework/issues/705 26 | abstract = 'rest_framework.authtoken' not in settings.INSTALLED_APPS 27 | verbose_name = _("Token") 28 | verbose_name_plural = _("Tokens") 29 | 30 | def save(self, *args, **kwargs): 31 | if not self.key: 32 | self.key = self.generate_key() 33 | return super().save(*args, **kwargs) 34 | 35 | def generate_key(self): 36 | return binascii.hexlify(os.urandom(20)).decode() 37 | 38 | def __str__(self): 39 | return self.key 40 | -------------------------------------------------------------------------------- /rest_framework/authtoken/serializers.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import authenticate 2 | from django.utils.translation import gettext_lazy as _ 3 | 4 | from rest_framework import serializers 5 | 6 | 7 | class AuthTokenSerializer(serializers.Serializer): 8 | username = serializers.CharField(label=_("Username")) 9 | password = serializers.CharField( 10 | label=_("Password"), 11 | style={'input_type': 'password'}, 12 | trim_whitespace=False 13 | ) 14 | 15 | def validate(self, attrs): 16 | username = attrs.get('username') 17 | password = attrs.get('password') 18 | 19 | if username and password: 20 | user = authenticate(request=self.context.get('request'), 21 | username=username, password=password) 22 | 23 | # The authenticate call simply returns None for is_active=False 24 | # users. (Assuming the default ModelBackend authentication 25 | # backend.) 26 | if not user: 27 | msg = _('Unable to log in with provided credentials.') 28 | raise serializers.ValidationError(msg, code='authorization') 29 | else: 30 | msg = _('Must include "username" and "password".') 31 | raise serializers.ValidationError(msg, code='authorization') 32 | 33 | attrs['user'] = user 34 | return attrs 35 | -------------------------------------------------------------------------------- /rest_framework/checks.py: -------------------------------------------------------------------------------- 1 | from django.core.checks import Tags, Warning, register 2 | 3 | 4 | @register(Tags.compatibility) 5 | def pagination_system_check(app_configs, **kwargs): 6 | errors = [] 7 | # Use of default page size setting requires a default Paginator class 8 | from rest_framework.settings import api_settings 9 | if api_settings.PAGE_SIZE and not api_settings.DEFAULT_PAGINATION_CLASS: 10 | errors.append( 11 | Warning( 12 | "You have specified a default PAGE_SIZE pagination rest_framework setting," 13 | "without specifying also a DEFAULT_PAGINATION_CLASS.", 14 | hint="The default for DEFAULT_PAGINATION_CLASS is None. " 15 | "In previous versions this was PageNumberPagination. " 16 | "If you wish to define PAGE_SIZE globally whilst defining " 17 | "pagination_class on a per-view basis you may silence this check.", 18 | id="rest_framework.W001" 19 | ) 20 | ) 21 | return errors 22 | -------------------------------------------------------------------------------- /rest_framework/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/rest_framework/management/__init__.py -------------------------------------------------------------------------------- /rest_framework/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/rest_framework/management/commands/__init__.py -------------------------------------------------------------------------------- /rest_framework/schemas/utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | utils.py # Shared helper functions 3 | 4 | See schemas.__init__.py for package overview. 5 | """ 6 | from django.db import models 7 | from django.utils.translation import ugettext_lazy as _ 8 | 9 | from rest_framework.mixins import RetrieveModelMixin, ListModelMixin 10 | 11 | 12 | def is_list_view(path, method, view): 13 | """ 14 | Return True if the given path/method appears to represent a list view. 15 | """ 16 | if hasattr(view, 'action'): 17 | # Viewsets have an explicitly defined action, which we can inspect. 18 | return view.action == 'list' 19 | 20 | if method.lower() != 'get': 21 | return False 22 | if isinstance(view, RetrieveModelMixin): 23 | return False 24 | if isinstance(view, ListModelMixin): 25 | return True 26 | path_components = path.strip('/').split('/') 27 | if path_components and '{' in path_components[-1]: 28 | return False 29 | return True 30 | 31 | 32 | def get_pk_description(model, model_field): 33 | if isinstance(model_field, models.AutoField): 34 | value_type = _('unique integer value') 35 | elif isinstance(model_field, models.UUIDField): 36 | value_type = _('UUID string') 37 | else: 38 | value_type = _('unique value') 39 | 40 | return _('A {value_type} identifying this {name}.').format( 41 | value_type=value_type, 42 | name=model._meta.verbose_name, 43 | ) 44 | -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/css/default.css: -------------------------------------------------------------------------------- 1 | /* The navbar is fixed at >= 980px wide, so add padding to the body to prevent 2 | content running up underneath it. */ 3 | 4 | h1 { 5 | font-weight: 300; 6 | } 7 | 8 | h2, h3 { 9 | font-weight: 300; 10 | } 11 | 12 | .resource-description, .response-info { 13 | margin-bottom: 2em; 14 | } 15 | 16 | .version:before { 17 | content: "v"; 18 | opacity: 0.6; 19 | padding-right: 0.25em; 20 | } 21 | 22 | .version { 23 | font-size: 70%; 24 | } 25 | 26 | .format-option { 27 | font-family: Menlo, Consolas, "Andale Mono", "Lucida Console", monospace; 28 | } 29 | 30 | .button-form { 31 | float: right; 32 | margin-right: 1em; 33 | } 34 | 35 | td.nested { 36 | padding: 0 !important; 37 | } 38 | 39 | td.nested > table { 40 | margin: 0; 41 | } 42 | 43 | form select, form input, form textarea { 44 | width: 90%; 45 | } 46 | 47 | form select[multiple] { 48 | height: 150px; 49 | } 50 | 51 | /* To allow tooltips to work on disabled elements */ 52 | .disabled-tooltip-shield { 53 | position: absolute; 54 | top: 0; 55 | right: 0; 56 | bottom: 0; 57 | left: 0; 58 | } 59 | 60 | .errorlist { 61 | margin-top: 0.5em; 62 | } 63 | 64 | pre { 65 | overflow: auto; 66 | word-wrap: normal; 67 | white-space: pre; 68 | font-size: 12px; 69 | } 70 | 71 | .page-header { 72 | border-bottom: none; 73 | padding-bottom: 0px; 74 | } 75 | 76 | #filtersModal form input[type=submit] { 77 | width: auto; 78 | } 79 | 80 | #filtersModal .modal-body h2 { 81 | margin-top: 0 82 | } 83 | -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/css/prettify.css: -------------------------------------------------------------------------------- 1 | .com { color: #93a1a1; } 2 | .lit { color: #195f91; } 3 | .pun, .opn, .clo { color: #93a1a1; } 4 | .fun { color: #dc322f; } 5 | .str, .atv { color: #D14; } 6 | .kwd, .prettyprint .tag { color: #1e347b; } 7 | .typ, .atn, .dec, .var { color: teal; } 8 | .pln { color: #48484c; } 9 | 10 | .prettyprint { 11 | padding: 8px; 12 | background-color: #f7f7f9; 13 | border: 1px solid #e1e1e8; 14 | } 15 | .prettyprint.linenums { 16 | -webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 17 | -moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 18 | box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 19 | } 20 | 21 | /* Specify class=linenums on a pre to get line numbering */ 22 | ol.linenums { 23 | margin: 0 0 0 33px; /* IE indents via margin-left */ 24 | } 25 | ol.linenums li { 26 | padding-left: 12px; 27 | color: #bebec5; 28 | line-height: 20px; 29 | text-shadow: 0 1px 0 #fff; 30 | } -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/docs/css/jquery.json-view.min.css: -------------------------------------------------------------------------------- 1 | .json-view{position:relative} 2 | .json-view .collapser{width:20px;height:18px;display:block;position:absolute;left:-1.7em;top:-.2em;z-index:5;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAD1JREFUeNpiYGBgOADE%2F3Hgw0DM4IRHgSsDFOzFInmMAQnY49ONzZRjDFiADT7dMLALiE8y4AGW6LoBAgwAuIkf%2F%2FB7O9sAAAAASUVORK5CYII%3D);background-repeat:no-repeat;background-position:center center;opacity:.5;cursor:pointer} 3 | .json-view .collapsed{-ms-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-khtml-transform:rotate(-90deg);-webkit-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)} 4 | .json-view .bl{display:block;padding-left:20px;margin-left:-20px;position:relative} 5 | .json-view{font-family:monospace} 6 | .json-view ul{list-style-type:none;padding-left:2em;border-left:1px dotted;margin:.3em} 7 | .json-view ul li{position:relative} 8 | .json-view .comments,.json-view .dots{display:none;-moz-user-select:none;-ms-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none} 9 | .json-view .comments{padding-left:.8em;font-style:italic;color:#888} 10 | .json-view .bool,.json-view .null,.json-view .num,.json-view .undef{font-weight:700;color:#1A01CC} 11 | .json-view .str{color:#800} -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/docs/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/rest_framework/static/rest_framework/docs/img/favicon.ico -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/docs/img/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/rest_framework/static/rest_framework/docs/img/grid.png -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/rest_framework/static/rest_framework/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/rest_framework/static/rest_framework/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/rest_framework/static/rest_framework/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/rest_framework/static/rest_framework/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/rest_framework/static/rest_framework/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/img/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/rest_framework/static/rest_framework/img/grid.png -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/js/default.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | // JSON highlighting. 3 | prettyPrint(); 4 | 5 | // Bootstrap tooltips. 6 | $('.js-tooltip').tooltip({ 7 | delay: 1000, 8 | container: 'body' 9 | }); 10 | 11 | // Deal with rounded tab styling after tab clicks. 12 | $('a[data-toggle="tab"]:first').on('shown', function(e) { 13 | $(e.target).parents('.tabbable').addClass('first-tab-active'); 14 | }); 15 | 16 | $('a[data-toggle="tab"]:not(:first)').on('shown', function(e) { 17 | $(e.target).parents('.tabbable').removeClass('first-tab-active'); 18 | }); 19 | 20 | $('a[data-toggle="tab"]').click(function() { 21 | document.cookie = "tabstyle=" + this.name + "; path=/"; 22 | }); 23 | 24 | // Store tab preference in cookies & display appropriate tab on load. 25 | var selectedTab = null; 26 | var selectedTabName = getCookie('tabstyle'); 27 | 28 | if (selectedTabName) { 29 | selectedTabName = selectedTabName.replace(/[^a-z-]/g, ''); 30 | } 31 | 32 | if (selectedTabName) { 33 | selectedTab = $('.form-switcher a[name=' + selectedTabName + ']'); 34 | } 35 | 36 | if (selectedTab && selectedTab.length > 0) { 37 | // Display whichever tab is selected. 38 | selectedTab.tab('show'); 39 | } else { 40 | // If no tab selected, display rightmost tab. 41 | $('.form-switcher a:first').tab('show'); 42 | } 43 | 44 | $(window).on('load', function() { 45 | $('#errorModal').modal('show'); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/admin/detail.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | 3 | 4 | {% for key, value in results|items %} 5 | {% if key in details %} 6 | 7 | {% endif %} 8 | {% endfor %} 9 | 10 |
{{ key|capfirst }}{{ value|format_value }}
11 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/admin/dict_value.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | 3 | 4 | {% for k, v in value|items %} 5 | 6 | 7 | 8 | 9 | {% endfor %} 10 | 11 |
{{ k|format_value }}{{ v|format_value }}
12 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/admin/list.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | 3 | 4 | {% for column in columns%}{% endfor %} 5 | 6 | 7 | {% for row in results %} 8 | 9 | {% for key, value in row|items %} 10 | {% if key in columns %} 11 | 14 | {% endif %} 15 | {% endfor %} 16 | 23 | 24 | {% endfor %} 25 | 26 |
{{ column|capfirst }}
12 | {{ value|format_value }} 13 | 17 | {% if row.url %} 18 | 19 | {% else %} 20 | 21 | {% endif %} 22 |
27 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/admin/list_value.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | 3 | 4 | {% for item in value %} 5 | 6 | 7 | 8 | 9 | {% endfor %} 10 | 11 |
{{ forloop.counter0 }}{{ item|format_value }}
12 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/admin/simple_list_value.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | {% for item in value %}{% if not forloop.first%},{% endif %} {{item|format_value}}{% endfor %} 3 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/api.html: -------------------------------------------------------------------------------- 1 | {% extends "rest_framework/base.html" %} 2 | 3 | {# Override this template in your own templates directory to customize #} 4 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/docs/auth/basic.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | 3 | 4 | 39 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/docs/auth/session.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | 3 | 4 | 36 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/docs/document.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | 3 |
4 |
5 |

{{ document.title }}

6 | {% if document.description %} 7 |

{% render_markdown document.description %}

8 | {% endif %} 9 |
10 |
11 | {% for html in lang_intro_htmls %} 12 | {% include html %} 13 | {% endfor %} 14 |
15 |
16 | {% if document|data %} 17 | {% for section_key, section in document|data|items %} 18 | {% if section_key %} 19 |

{{ section_key }} 20 |

21 | {% endif %} 22 | 23 | {% for link_key, link in section|schema_links|items %} 24 | {% include "rest_framework/docs/link.html" %} 25 | {% endfor %} 26 | {% endfor %} 27 | 28 | {% for link_key, link in document.links|items %} 29 | {% include "rest_framework/docs/link.html" %} 30 | {% endfor %} 31 | {% endif %} 32 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/docs/langs/javascript-intro.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | {% load static %} 3 |
{% code html %}
4 | 
5 | {% endcode %}
6 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/docs/langs/javascript.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 |
{% code javascript %}var coreapi = window.coreapi  // Loaded by `coreapi.js`
 3 | var schema = window.schema    // Loaded by `schema.js`
 4 | 
 5 | // Initialize a client
 6 | var client = new coreapi.Client()
 7 | 
 8 | // Interact with the API endpoint
 9 | var action = [{% if section_key %}"{{ section_key }}", {% endif %}"{{ link_key }}"]
10 | {% if link.fields %}var params = {
11 | {% for field in link.fields %}    {{ field.name }}: ...{% if not loop.last %},{% endif %}
12 | {% endfor %}}
13 | {% endif %}client.action(schema, action{% if link.fields %}, params{% endif %}).then(function(result) {
14 |     // Return value is in 'result'
15 | }){% endcode %}
16 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/docs/langs/python-intro.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 |
{% code bash %}# Install the Python client library
3 | $ pip install coreapi{% endcode %}
4 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/docs/langs/python.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 |
{% code python %}import coreapi
 3 | 
 4 | # Initialize a client & load the schema document
 5 | client = coreapi.Client()
 6 | schema = client.get("{{ document.url }}"{% if schema_format %}, format="{{ schema_format }}"{% endif %})
 7 | 
 8 | # Interact with the API endpoint
 9 | action = [{% if section_key %}"{{ section_key }}", {% endif %}"{{ link_key }}"]
10 | {% if link.fields %}params = {
11 | {% for field in link.fields %}    "{{ field.name }}": ...{% if not loop.last %},{% endif %}
12 | {% endfor %}}
13 | {% endif %}result = client.action(schema, action{% if link.fields %}, params=params{% endif %}){% endcode %}
14 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/docs/langs/shell-intro.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 |
{% code bash %}# Install the command line client
3 | $ pip install coreapi-cli{% endcode %}
4 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/docs/langs/shell.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 |
{% code bash %}# Load the schema document
3 | $ coreapi get {{ document.url }}{% if schema_format %} --format {{ schema_format }}{% endif %}
4 | 
5 | # Interact with the API endpoint
6 | $ coreapi action {% if section_key %}{{ section_key }} {% endif %}{{ link_key|cut:"> " }}{% for field in link.fields %} -p {{ field.name }}=...{% endfor %}{% endcode %}
7 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/filters/base.html: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/filters/ordering.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | {% load i18n %} 3 |

{% trans "Ordering" %}

4 |
5 | {% for key, label in options %} 6 | {% if key == current %} 7 | 8 | {{ label }} 9 | 10 | {% else %} 11 | {{ label }} 12 | {% endif %} 13 | {% endfor %} 14 |
15 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/filters/search.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 |

{% trans "Search" %}

3 |
4 |
5 |
6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/checkbox.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 6 | {% endif %} 7 | 8 |
9 | 10 | 11 | {% if field.errors %} 12 | {% for error in field.errors %} 13 | {{ error }} 14 | {% endfor %} 15 | {% endif %} 16 | 17 | {% if field.help_text %} 18 | {{ field.help_text|safe }} 19 | {% endif %} 20 |
21 |
22 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/checkbox_multiple.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | 3 |
4 | {% if field.label %} 5 | 8 | {% endif %} 9 | 10 |
11 | {% if style.inline %} 12 | {% for key, text in field.choices|items %} 13 | 17 | {% endfor %} 18 | {% else %} 19 | {% for key, text in field.choices|items %} 20 |
21 | 25 |
26 | {% endfor %} 27 | {% endif %} 28 | 29 | {% if field.errors %} 30 | {% for error in field.errors %} 31 | {{ error }} 32 | {% endfor %} 33 | {% endif %} 34 | 35 | {% if field.help_text %} 36 | {{ field.help_text|safe }} 37 | {% endif %} 38 |
39 |
40 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/dict_field.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 6 | {% endif %} 7 | 8 |
9 |

Dictionaries are not currently supported in HTML input.

10 |
11 |
12 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/fieldset.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 |
3 | {% if field.label %} 4 |
5 | 6 | {{ field.label }} 7 | 8 |
9 | {% endif %} 10 | 11 | {% for nested_field in field %} 12 | {% if not nested_field.read_only %} 13 | {% render_field nested_field style=style %} 14 | {% endif %} 15 | {% endfor %} 16 |
17 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/form.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | {% for field in form %} 3 | {% if not field.read_only %} 4 | {% render_field field style=style %} 5 | {% endif %} 6 | {% endfor %} 7 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/input.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 6 | {% endif %} 7 | 8 |
9 | 10 | 11 | {% if field.errors %} 12 | {% for error in field.errors %} 13 | {{ error }} 14 | {% endfor %} 15 | {% endif %} 16 | 17 | {% if field.help_text %} 18 | {{ field.help_text|safe }} 19 | {% endif %} 20 |
21 |
22 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/list_field.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 6 | {% endif %} 7 | 8 |
9 |

Lists are not currently supported in HTML input.

10 |
11 |
12 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/list_fieldset.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | 3 |
4 | {% if field.label %} 5 |
6 | 7 | {{ field.label }} 8 | 9 |
10 | {% endif %} 11 | 12 |

Lists are not currently supported in HTML input.

13 |
14 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/select.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | 3 |
4 | {% if field.label %} 5 | 8 | {% endif %} 9 | 10 |
11 | 25 | 26 | {% if field.errors %} 27 | {% for error in field.errors %} 28 | {{ error }} 29 | {% endfor %} 30 | {% endif %} 31 | 32 | {% if field.help_text %} 33 | {{ field.help_text|safe }} 34 | {% endif %} 35 |
36 |
37 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/select_multiple.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% load rest_framework %} 3 | 4 | {% trans "No items to select." as no_items %} 5 | 6 |
7 | {% if field.label %} 8 | 11 | {% endif %} 12 | 13 |
14 | 27 | 28 | {% if field.errors %} 29 | {% for error in field.errors %} 30 | {{ error }} 31 | {% endfor %} 32 | {% endif %} 33 | 34 | {% if field.help_text %} 35 | {{ field.help_text|safe }} 36 | {% endif %} 37 |
38 |
39 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/textarea.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 6 | {% endif %} 7 | 8 |
9 | 10 | 11 | {% if field.errors %} 12 | {% for error in field.errors %} 13 | {{ error }} 14 | {% endfor %} 15 | {% endif %} 16 | 17 | {% if field.help_text %} 18 | {{ field.help_text|safe }} 19 | {% endif %} 20 |
21 |
22 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/checkbox.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 7 |
8 |
9 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/checkbox_multiple.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | 3 |
4 | {% if field.label %} 5 | 6 | {% endif %} 7 | 8 | {% for key, text in field.choices|items %} 9 |
10 | 14 |
15 | {% endfor %} 16 |
17 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/dict_field.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 6 | {% endif %} 7 | 8 |

Dictionaries are not currently supported in HTML input.

9 |
10 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/fieldset.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | {% for nested_field in field %} 3 | {% if not nested_field.read_only %} 4 | {% render_field nested_field style=style %} 5 | {% endif %} 6 | {% endfor %} 7 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/form.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | {% for field in form %} 3 | {% if not field.read_only %} 4 | {% render_field field style=style %} 5 | {% endif %} 6 | {% endfor %} 7 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/input.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 6 | {% endif %} 7 | 8 | 9 |
10 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/list_field.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 6 | {% endif %} 7 | 8 |

Lists are not currently supported in HTML input.

9 |
10 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/list_fieldset.html: -------------------------------------------------------------------------------- 1 | Lists are not currently supported in HTML input. 2 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/radio.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% load rest_framework %} 3 | {% trans "None" as none_choice %} 4 | 5 |
6 | {% if field.label %} 7 | 10 | {% endif %} 11 | 12 | {% if field.allow_null or field.allow_blank %} 13 |
14 | 18 |
19 | {% endif %} 20 | 21 | {% for key, text in field.choices|items %} 22 |
23 | 27 |
28 | {% endfor %} 29 |
30 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/select.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | 3 |
4 | {% if field.label %} 5 | 8 | {% endif %} 9 | 10 | 24 |
25 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/select_multiple.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% load rest_framework %} 3 | {% trans "No items to select." as no_items %} 4 | 5 |
6 | {% if field.label %} 7 | 10 | {% endif %} 11 | 12 | 25 |
26 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/textarea.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 6 | {% endif %} 7 | 8 | 9 |
10 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/login.html: -------------------------------------------------------------------------------- 1 | {% extends "rest_framework/login_base.html" %} 2 | 3 | {# Override this template in your own templates directory to customize #} 4 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/pagination/numbers.html: -------------------------------------------------------------------------------- 1 |
    2 | {% if previous_url %} 3 |
  • 4 | 5 | 6 | 7 |
  • 8 | {% else %} 9 |
  • 10 | 11 | 12 | 13 |
  • 14 | {% endif %} 15 | 16 | {% for page_link in page_links %} 17 | {% if page_link.is_break %} 18 |
  • 19 | 20 |
  • 21 | {% else %} 22 | {% if page_link.is_active %} 23 |
  • 24 | {{ page_link.number }} 25 |
  • 26 | {% else %} 27 |
  • 28 | {{ page_link.number }} 29 |
  • 30 | {% endif %} 31 | {% endif %} 32 | {% endfor %} 33 | 34 | {% if next_url %} 35 |
  • 36 | 37 | 38 | 39 |
  • 40 | {% else %} 41 |
  • 42 | 43 | 44 | 45 |
  • 46 | {% endif %} 47 |
48 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/pagination/previous_and_next.html: -------------------------------------------------------------------------------- 1 |
    2 | {% if previous_url %} 3 | 6 | {% else %} 7 | 10 | {% endif %} 11 | 12 | {% if next_url %} 13 | 16 | {% else %} 17 | 20 | {% endif %} 21 |
22 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/raw_data_form.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | {{ form.non_field_errors }} 3 | {% for field in form %} 4 |
5 | {{ field.label_tag|add_class:"col-sm-2 control-label" }} 6 |
7 | {{ field|add_class:"form-control" }} 8 | {{ field.help_text|safe }} 9 |
10 |
11 | {% endfor %} 12 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/schema.js: -------------------------------------------------------------------------------- 1 | var codec = new window.coreapi.codecs.CoreJSONCodec() 2 | var coreJSON = window.atob('{{ schema }}') 3 | window.schema = codec.decode(coreJSON) 4 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/checkbox.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 7 |
8 | 9 | {% if field.errors %} 10 | {% for error in field.errors %} 11 | {{ error }} 12 | {% endfor %} 13 | {% endif %} 14 | 15 | {% if field.help_text %} 16 | {{ field.help_text|safe }} 17 | {% endif %} 18 |
19 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/checkbox_multiple.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | 3 |
4 | {% if field.label %} 5 | 6 | {% endif %} 7 | 8 | {% if style.inline %} 9 |
10 | {% for key, text in field.choices|items %} 11 | 15 | {% endfor %} 16 |
17 | {% else %} 18 | {% for key, text in field.choices|items %} 19 |
20 | 24 |
25 | {% endfor %} 26 | {% endif %} 27 | 28 | {% if field.errors %} 29 | {% for error in field.errors %} 30 | {{ error }} 31 | {% endfor %} 32 | {% endif %} 33 | 34 | {% if field.help_text %} 35 | {{ field.help_text|safe }} 36 | {% endif %} 37 |
38 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/dict_field.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 4 | {% endif %} 5 | 6 |

Dictionaries are not currently supported in HTML input.

7 |
8 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/fieldset.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | 3 |
4 | {% if field.label %} 5 | 6 | {{ field.label }} 7 | 8 | {% endif %} 9 | 10 | {% for nested_field in field %} 11 | {% if not nested_field.read_only %} 12 | {% render_field nested_field style=style %} 13 | {% endif %} 14 | {% endfor %} 15 |
16 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/form.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | {% for field in form %} 3 | {% if not field.read_only %} 4 | {% render_field field style=style %} 5 | {% endif %} 6 | {% endfor %} 7 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/input.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 4 | {% endif %} 5 | 6 | 7 | 8 | {% if field.errors %} 9 | {% for error in field.errors %} 10 | {{ error }} 11 | {% endfor %} 12 | {% endif %} 13 | 14 | {% if field.help_text %} 15 | {{ field.help_text|safe }} 16 | {% endif %} 17 |
18 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/list_field.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 4 | {% endif %} 5 | 6 |

Lists are not currently supported in HTML input.

7 |
8 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/list_fieldset.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 4 | {{ field.label }} 5 | 6 | {% endif %} 7 | 8 |

Lists are not currently supported in HTML input.

9 |
10 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/select.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | 3 |
4 | {% if field.label %} 5 | 8 | {% endif %} 9 | 10 | 24 | 25 | {% if field.errors %} 26 | {% for error in field.errors %} 27 | {{ error }} 28 | {% endfor %} 29 | {% endif %} 30 | 31 | {% if field.help_text %} 32 | {{ field.help_text|safe }} 33 | {% endif %} 34 |
35 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/select_multiple.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% load rest_framework %} 3 | {% trans "No items to select." as no_items %} 4 | 5 |
6 | {% if field.label %} 7 | 10 | {% endif %} 11 | 12 | 25 | 26 | {% if field.errors %} 27 | {% for error in field.errors %}{{ error }}{% endfor %} 28 | {% endif %} 29 | 30 | {% if field.help_text %} 31 | {{ field.help_text|safe }} 32 | {% endif %} 33 |
34 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/textarea.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 6 | {% endif %} 7 | 8 | 9 | 10 | {% if field.errors %} 11 | {% for error in field.errors %}{{ error }}{% endfor %} 12 | {% endif %} 13 | 14 | {% if field.help_text %} 15 | {{ field.help_text|safe }} 16 | {% endif %} 17 |
18 | -------------------------------------------------------------------------------- /rest_framework/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/rest_framework/templatetags/__init__.py -------------------------------------------------------------------------------- /rest_framework/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | Login and logout views for the browsable API. 3 | 4 | Add these to your root URLconf if you're using the browsable API and 5 | your API requires authentication: 6 | 7 | urlpatterns = [ 8 | ... 9 | url(r'^auth/', include('rest_framework.urls')) 10 | ] 11 | 12 | You should make sure your authentication settings include `SessionAuthentication`. 13 | """ 14 | from django.conf.urls import url 15 | from django.contrib.auth import views 16 | 17 | app_name = 'rest_framework' 18 | urlpatterns = [ 19 | url(r'^login/$', views.LoginView.as_view(template_name='rest_framework/login.html'), name='login'), 20 | url(r'^logout/$', views.LogoutView.as_view(), name='logout'), 21 | ] 22 | -------------------------------------------------------------------------------- /rest_framework/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/rest_framework/utils/__init__.py -------------------------------------------------------------------------------- /rest_framework/utils/humanize_datetime.py: -------------------------------------------------------------------------------- 1 | """ 2 | Helper functions that convert strftime formats into more readable representations. 3 | """ 4 | from rest_framework import ISO_8601 5 | 6 | 7 | def datetime_formats(formats): 8 | format = ', '.join(formats).replace( 9 | ISO_8601, 10 | 'YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]' 11 | ) 12 | return humanize_strptime(format) 13 | 14 | 15 | def date_formats(formats): 16 | format = ', '.join(formats).replace(ISO_8601, 'YYYY-MM-DD') 17 | return humanize_strptime(format) 18 | 19 | 20 | def time_formats(formats): 21 | format = ', '.join(formats).replace(ISO_8601, 'hh:mm[:ss[.uuuuuu]]') 22 | return humanize_strptime(format) 23 | 24 | 25 | def humanize_strptime(format_string): 26 | # Note that we're missing some of the locale specific mappings that 27 | # don't really make sense. 28 | mapping = { 29 | "%Y": "YYYY", 30 | "%y": "YY", 31 | "%m": "MM", 32 | "%b": "[Jan-Dec]", 33 | "%B": "[January-December]", 34 | "%d": "DD", 35 | "%H": "hh", 36 | "%I": "hh", # Requires '%p' to differentiate from '%H'. 37 | "%M": "mm", 38 | "%S": "ss", 39 | "%f": "uuuuuu", 40 | "%a": "[Mon-Sun]", 41 | "%A": "[Monday-Sunday]", 42 | "%p": "[AM|PM]", 43 | "%z": "[+HHMM|-HHMM]" 44 | } 45 | for key, val in mapping.items(): 46 | format_string = format_string.replace(key, val) 47 | return format_string 48 | -------------------------------------------------------------------------------- /rest_framework/utils/json.py: -------------------------------------------------------------------------------- 1 | """ 2 | Wrapper for the builtin json module that ensures compliance with the JSON spec. 3 | 4 | REST framework should always import this wrapper module in order to maintain 5 | spec-compliant encoding/decoding. Support for non-standard features should be 6 | handled by users at the renderer and parser layer. 7 | """ 8 | import functools 9 | import json # noqa 10 | 11 | 12 | def strict_constant(o): 13 | raise ValueError('Out of range float values are not JSON compliant: ' + repr(o)) 14 | 15 | 16 | @functools.wraps(json.dump) 17 | def dump(*args, **kwargs): 18 | kwargs.setdefault('allow_nan', False) 19 | return json.dump(*args, **kwargs) 20 | 21 | 22 | @functools.wraps(json.dumps) 23 | def dumps(*args, **kwargs): 24 | kwargs.setdefault('allow_nan', False) 25 | return json.dumps(*args, **kwargs) 26 | 27 | 28 | @functools.wraps(json.load) 29 | def load(*args, **kwargs): 30 | kwargs.setdefault('parse_constant', strict_constant) 31 | return json.load(*args, **kwargs) 32 | 33 | 34 | @functools.wraps(json.loads) 35 | def loads(*args, **kwargs): 36 | kwargs.setdefault('parse_constant', strict_constant) 37 | return json.loads(*args, **kwargs) 38 | -------------------------------------------------------------------------------- /rest_framework/utils/urls.py: -------------------------------------------------------------------------------- 1 | from urllib import parse 2 | 3 | from django.utils.encoding import force_str 4 | 5 | 6 | def replace_query_param(url, key, val): 7 | """ 8 | Given a URL and a key/val pair, set or replace an item in the query 9 | parameters of the URL, and return the new URL. 10 | """ 11 | (scheme, netloc, path, query, fragment) = parse.urlsplit(force_str(url)) 12 | query_dict = parse.parse_qs(query, keep_blank_values=True) 13 | query_dict[force_str(key)] = [force_str(val)] 14 | query = parse.urlencode(sorted(list(query_dict.items())), doseq=True) 15 | return parse.urlunsplit((scheme, netloc, path, query, fragment)) 16 | 17 | 18 | def remove_query_param(url, key): 19 | """ 20 | Given a URL and a key/val pair, remove an item in the query 21 | parameters of the URL, and return the new URL. 22 | """ 23 | (scheme, netloc, path, query, fragment) = parse.urlsplit(force_str(url)) 24 | query_dict = parse.parse_qs(query, keep_blank_values=True) 25 | query_dict.pop(key, None) 26 | query = parse.urlencode(sorted(list(query_dict.items())), doseq=True) 27 | return parse.urlunsplit((scheme, netloc, path, query, fragment)) 28 | -------------------------------------------------------------------------------- /rest_framework_serializer_extensions/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.6.0' 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules 4 | 5 | [pycodestyle] 6 | max-line-length = 120 7 | exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules 8 | 9 | [mypy] 10 | python_version = 3.6 11 | check_untyped_defs = True 12 | ignore_errors = False 13 | ignore_missing_imports = True 14 | strict_optional = True 15 | warn_unused_ignores = True 16 | warn_redundant_casts = True 17 | warn_unused_configs = True 18 | 19 | [mypy-*.migrations.*] 20 | # Django migrations should not produce any errors: 21 | ignore_errors = True 22 | -------------------------------------------------------------------------------- /static/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/static/.gitignore -------------------------------------------------------------------------------- /taskapp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/taskapp/__init__.py -------------------------------------------------------------------------------- /taskapp/celery.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | from celery import Celery 4 | from django.apps import apps, AppConfig 5 | from django.conf import settings 6 | 7 | 8 | if not settings.configured: 9 | # set the default Django settings module for the 'celery' program. 10 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.local') # pragma: no cover 11 | 12 | 13 | app = Celery('trainserver') 14 | 15 | 16 | class CeleryAppConfig(AppConfig): 17 | name = 'taskapp' 18 | verbose_name = 'Celery Config' 19 | 20 | def ready(self): 21 | # Using a string here means the worker will not have to 22 | # pickle the object when using Windows. 23 | # - namespace='CELERY' means all celery-related configuration keys 24 | # should have a `CELERY_` prefix. 25 | app.config_from_object('django.conf:settings', namespace='CELERY') 26 | installed_apps = [app_config.name for app_config in apps.get_app_configs()] 27 | app.autodiscover_tasks(lambda: installed_apps, force=True) 28 | 29 | if hasattr(settings, 'RAVEN_CONFIG'): 30 | # Celery signal registration 31 | 32 | from raven import Client as RavenClient 33 | from raven.contrib.celery import register_signal as raven_register_signal 34 | from raven.contrib.celery import register_logger_signal as raven_register_logger_signal 35 | 36 | raven_client = RavenClient(dsn=settings.RAVEN_CONFIG['dsn']) 37 | raven_register_logger_signal(raven_client) 38 | raven_register_signal(raven_client) 39 | 40 | 41 | @app.task(bind=True) 42 | def debug_task(self): 43 | print(f'Request: {self.request!r}') # pragma: no cover 44 | -------------------------------------------------------------------------------- /tessss.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import time 3 | 4 | 5 | async def say_after(delay, what): 6 | await asyncio.sleep(delay) 7 | print(what) 8 | 9 | 10 | async def nested(): 11 | return 42 12 | 13 | 14 | async def main(): 15 | task1 = asyncio.create_task( 16 | say_after(1, 'hello')) 17 | 18 | task2 = asyncio.create_task( 19 | say_after(2, 'world')) 20 | 21 | print(f"started at {time.strftime('%X')}") 22 | 23 | # Wait until both tasks are completed (should take 24 | # around 2 seconds.) 25 | await task1 26 | print('we22') 27 | await task2 28 | print('we') 29 | # await say_after(1, 'hello') 30 | # await say_after(2, 'world') 31 | print(f"finished at {time.strftime('%X')}") 32 | # async def main(): 33 | # # Nothing happens if we just call "nested()". 34 | # # A coroutine object is created but not awaited, 35 | # # so it *won't run at all*. 36 | # # with "main()". 37 | # task = asyncio.create_task(nested()) 38 | 39 | # # "task" can now be used to cancel "nested()", or 40 | # # can simply be awaited to wait until it is complete: 41 | # ttt = await task 42 | 43 | # # Let's do it differently now and await it: 44 | # print(ttt) # will print "42". 45 | # print(34) 46 | asyncio.run(main()) 47 | -------------------------------------------------------------------------------- /traingroup/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/traingroup/__init__.py -------------------------------------------------------------------------------- /traingroup/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import TrainGroup 3 | # Register your models here. 4 | 5 | 6 | @admin.register(TrainGroup) 7 | class TrainAdmin(admin.ModelAdmin): 8 | 9 | list_display = ('id', 'name', 'group_no', 'department') 10 | 11 | 12 | # @admin.register(TrainManagerPermission) 13 | # class TrainPermissionAdmin(admin.ModelAdmin): 14 | 15 | # list_display = ('id', 'department', 'administrator') 16 | -------------------------------------------------------------------------------- /traingroup/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from django.utils.translation import gettext_lazy as _ 3 | 4 | 5 | class traingroupConfig(AppConfig): 6 | name = 'traingroup' 7 | icon = 'iconfont icon-department' 8 | verbose_name = _("train group manager") 9 | -------------------------------------------------------------------------------- /traingroup/filter.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from rest_framework.filters import BaseFilterBackend 4 | 5 | 6 | class IsManagerFilterBackend(BaseFilterBackend): 7 | """ 8 | Filter that only allows users to see their manager objects by the role in department 9 | """ 10 | name = 'IsManagerFilterBackend' 11 | 12 | def filter_queryset(self, request, queryset, view): 13 | 14 | if hasattr(request.user, 'managerdepartment'): 15 | return queryset.filter(department__slug__startswith=request.user.managerdepartment.slug) 16 | else: 17 | return queryset.none() 18 | 19 | 20 | class IsOwnerFilterBackend(BaseFilterBackend): 21 | """ 22 | Filter that only allows users to see their own objects. 23 | """ 24 | name = 'IsOwnerFilterBackend' 25 | 26 | def filter_queryset(self, request, queryset, view): 27 | return queryset.filter(department=request.user.department) 28 | 29 | 30 | class IsAdminFilterBackend(BaseFilterBackend): 31 | """ 32 | Filter that only allows users to see their all objects. 33 | """ 34 | name = 'IsAdminFilterBackend' 35 | 36 | def filter_queryset(self, request, queryset, view): 37 | return queryset 38 | -------------------------------------------------------------------------------- /traingroup/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/traingroup/migrations/__init__.py -------------------------------------------------------------------------------- /traingroup/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /traingroup/urls.py: -------------------------------------------------------------------------------- 1 | from rest_framework import routers 2 | from .api import TrainGroupViewSet, TrainGropMemberModifyViewSet 3 | from django.urls import path 4 | router = routers.SimpleRouter(trailing_slash=False) 5 | router.register(r'group', TrainGroupViewSet) 6 | urlpatterns = router.urls 7 | urlpatterns += [path('group//members', TrainGropMemberModifyViewSet.as_view( 8 | {'get': 'list', 'put': 'update', 'patch': 'partial_update'}), name='group_members')] 9 | -------------------------------------------------------------------------------- /traingroup/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /uploadfile/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/uploadfile/__init__.py -------------------------------------------------------------------------------- /uploadfile/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /uploadfile/api.py: -------------------------------------------------------------------------------- 1 | 2 | from rest_framework import generics 3 | from rest_framework.parsers import MultiPartParser, FileUploadParser 4 | from rest_framework.response import Response 5 | from rest_framework.permissions import IsAuthenticated 6 | from rest_framework import status 7 | from .serializers import UploadfileSerializer 8 | from permissions.permissions import RolePermission 9 | 10 | 11 | class UploadView(generics.CreateAPIView): 12 | 13 | """The API view to handle font upload and convert the file into json format""" 14 | serializer_class = UploadfileSerializer 15 | parser_classes = (MultiPartParser,) 16 | permission_classes = [RolePermission] 17 | throttle_scope = 'uploads' 18 | 19 | def create(self, request, *args, **kwargs): 20 | serializer = self.get_serializer(data=request.data) 21 | serializer.is_valid(raise_exception=True) 22 | self.perform_create(serializer) 23 | headers = self.get_success_headers(serializer.data) 24 | return Response(serializer.data, status=status.HTTP_200_OK, headers=headers) 25 | -------------------------------------------------------------------------------- /uploadfile/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from django.utils.translation import gettext_lazy as _ 3 | 4 | 5 | class UploadfileConfig(AppConfig): 6 | name = 'uploadfile' 7 | verbose_name = _("uploadfile") 8 | -------------------------------------------------------------------------------- /uploadfile/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/uploadfile/migrations/__init__.py -------------------------------------------------------------------------------- /uploadfile/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.core.validators import FileExtensionValidator 3 | from django.utils.translation import gettext_lazy as _ 4 | # Create your models here. 5 | 6 | 7 | class uploadfile(models.Model): 8 | 9 | file = models.FileField(upload_to='./uploadfile/', blank=True) 10 | FILE_TYPE_CHOICES = ( 11 | ('PDF', 'PDF'), 12 | ('MP4', 'MP4'), 13 | ('JPG', 'JPG'), 14 | ('PNG', 'PNG'), 15 | ('TXT', 'TXT'), 16 | ('NO', 'NO'), 17 | ) 18 | type = models.CharField( 19 | max_length=3, 20 | choices=FILE_TYPE_CHOICES, 21 | default='NO', 22 | ) 23 | 24 | class Meta: 25 | verbose_name = _('upload file') 26 | verbose_name_plural = _('upload_file') 27 | 28 | def __str__(self): 29 | return str(self.id) 30 | -------------------------------------------------------------------------------- /uploadfile/serializers.py: -------------------------------------------------------------------------------- 1 | 2 | from rest_framework import serializers # , exceptions 3 | from .models import uploadfile 4 | 5 | 6 | class UploadfileSerializer(serializers.ModelSerializer): 7 | fileid = serializers.IntegerField(source='id', read_only=True) 8 | 9 | class Meta: 10 | model = uploadfile 11 | fields = ['file', 'fileid'] 12 | read_only_fields = ['fileid'] 13 | -------------------------------------------------------------------------------- /uploadfile/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /uploadfile/urls.py: -------------------------------------------------------------------------------- 1 | from .api import UploadView 2 | from rest_framework import routers 3 | from django.urls import include, path 4 | 5 | 6 | # router = routers.SimpleRouter(trailing_slash=False) 7 | # router.register(r'file', UploadView, basename='upload') 8 | urlpatterns = [ 9 | path('file', UploadView.as_view()), 10 | ] 11 | -------------------------------------------------------------------------------- /uploadfile/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /users/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/users/__init__.py -------------------------------------------------------------------------------- /users/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from django.utils.translation import gettext_lazy as _ 3 | 4 | 5 | class UsersAppConfig(AppConfig): 6 | 7 | name = "users" 8 | icon = 'iconfont icon-usersetting' 9 | verbose_name = _("user manager") 10 | -------------------------------------------------------------------------------- /users/filter.py: -------------------------------------------------------------------------------- 1 | from permissions.filters import RoleFilterBackend 2 | 3 | 4 | class IsManagerFilterBackend(RoleFilterBackend): 5 | """ 6 | Filter that only allows users to see their manager objects by the role in department 7 | """ 8 | name = 'IsManagerFilterBackend' 9 | 10 | def filter_queryset(self, request, queryset, view): 11 | 12 | if hasattr(request.user, 'managerdepartment'): 13 | return queryset.filter(department__slug=request.user.managerdepartment.slug) 14 | else: 15 | return queryset.none() 16 | 17 | 18 | class IsOwnerFilterBackend(RoleFilterBackend): 19 | """ 20 | Filter that only allows users to see their manager objects by the role in department 21 | """ 22 | name = 'IsOwnerFilterBackend' 23 | 24 | def filter_queryset(self, request, queryset, view): 25 | 26 | if hasattr(request.user, 'managerdepartment'): 27 | return queryset.filter(id=request.user.id) 28 | else: 29 | return queryset.none() 30 | -------------------------------------------------------------------------------- /users/fixtures/user.json: -------------------------------------------------------------------------------- 1 | admin/ 2 | [{"model": "users.user", "pk": 1, "fields": {"password": "argon2$argon2i$v=19$m=512,t=2,p=2$bUN3NG53RTdJMnF0$ThbPPyRPwmWT6BpJplXziw", "last_login": "2020-02-18T23:34:51.836", "is_superuser": true, "username": "fgf", "first_name": "", "last_name": "", "email": "fgf@whl.com", "is_staff": true, "is_active": true, "date_joined": "2019-03-16T22:08:02", "user_no": "201908170003", "name": "\u5218\u547c\u5170", "department": null, "employee_position": "\u7ecf\u7406", "info": "", "block_password_change": false, "avatar": "avatar/4b90f603738da977a970da61be51f8198618e319_myoU2A4.jpg", "importid": "", "groups": [], "user_permissions": [16, 17, 18, 99, 79, 80, 81, 135, 4, 5, 6, 95, 1, 2, 3, 94, 19, 20, 21, 100, 76, 77, 78, 120, 7, 8, 9, 96, 58, 59, 60, 114, 61, 62, 63, 115, 136, 137, 138, 139, 64, 65, 66, 116, 22, 23, 24, 101, 25, 26, 27, 102, 28, 29, 30, 103, 31, 32, 33, 104, 34, 35, 36, 105, 37, 38, 39, 106, 40, 41, 42, 107, 91, 92, 93, 108, 88, 89, 90, 122, 82, 83, 84, 109, 43, 44, 45, 110, 46, 47, 48, 111, 140, 141, 142, 143, 134, 127, 128, 129, 130, 126, 144, 145, 146, 147, 49, 50, 51, 112, 10, 11, 12, 97, 13, 14, 15, 98, 52, 53, 54, 113, 55, 56, 57, 85, 86, 87, 121, 70, 71, 72, 118, 67, 68, 69, 117, 73, 74, 75, 119], "roles": [5]}}] 3 | -------------------------------------------------------------------------------- /users/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/users/migrations/__init__.py -------------------------------------------------------------------------------- /users/permissions.py: -------------------------------------------------------------------------------- 1 | from rest_framework import permissions 2 | 3 | 4 | class IsUserOrReadOnly(permissions.BasePermission): 5 | """ 6 | Object-level permission to only allow owners of an object to edit it. 7 | """ 8 | 9 | def has_object_permission(self, request, view, obj): 10 | 11 | if request.method in permissions.SAFE_METHODS: 12 | return True 13 | 14 | return obj == request.user 15 | -------------------------------------------------------------------------------- /users/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fangaofeng/trainserver/da5db0442fba13a74d410ba31d1dffcc1279a46d/users/tests/__init__.py -------------------------------------------------------------------------------- /users/tests/requirements.pip: -------------------------------------------------------------------------------- 1 | django-allauth>=0.25.0 2 | responses>=0.3.0 3 | flake8==2.4.0 4 | djangorestframework-jwt>=1.7.2 5 | djangorestframework>=3.6.4 6 | -------------------------------------------------------------------------------- /users/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import include, path, re_path 2 | from rest_framework import routers 3 | 4 | 5 | from .api import ( 6 | LoginView, LogoutView, AccountDetailView, PasswordChangeView, 7 | PasswordResetView, PasswordResetConfirmView, ExcelfileUploadView, UserAvatarView, UserView 8 | ) 9 | 10 | router = routers.SimpleRouter(trailing_slash=False) 11 | router.register(r'user', UserView) 12 | 13 | urlpatterns = [ 14 | # URLs that do not require a session or valid token 15 | path('auth/password/reset', PasswordResetView.as_view(), 16 | name='user_password_reset'), 17 | path('auth/password/reset/confirm', PasswordResetConfirmView.as_view(), 18 | name='user_password_reset_confirm'), 19 | path('auth/password/change', PasswordChangeView.as_view(), 20 | name='user_password_change'), 21 | path('auth/login', LoginView.as_view(), name='user_login'), 22 | path('auth/logout', LogoutView.as_view(), name='auth_logout'), 23 | path('account/info', AccountDetailView.as_view(), name='account_details'), 24 | path('account/avatar', UserAvatarView.as_view(), name='account_avatar'), 25 | 26 | path('user/upload', ExcelfileUploadView.as_view(), name='user_upload'), 27 | 28 | ] 29 | urlpatterns += router.urls 30 | -------------------------------------------------------------------------------- /users/utils.py: -------------------------------------------------------------------------------- 1 | from six import string_types 2 | from importlib import import_module 3 | 4 | 5 | def import_callable(path_or_callable): 6 | if hasattr(path_or_callable, '__call__'): 7 | return path_or_callable 8 | else: 9 | assert isinstance(path_or_callable, string_types) 10 | package, attr = path_or_callable.rsplit('.', 1) 11 | return getattr(import_module(package), attr) 12 | 13 | 14 | def default_create_token(token_model, user, serializer): 15 | token, _ = token_model.objects.get_or_create(user=user) 16 | return token 17 | 18 | 19 | def jwt_encode(user): 20 | try: 21 | from rest_framework_jwt.settings import api_settings 22 | except ImportError: 23 | raise ImportError("djangorestframework_jwt needs to be installed") 24 | 25 | jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER 26 | jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER 27 | 28 | payload = jwt_payload_handler(user) 29 | return jwt_encode_handler(payload) 30 | -------------------------------------------------------------------------------- /users/validators.py: -------------------------------------------------------------------------------- 1 | # import re 2 | 3 | from django.core import validators 4 | from django.utils.deconstruct import deconstructible 5 | from django.utils.translation import gettext_lazy as _ 6 | 7 | 8 | @deconstructible 9 | class DigitserNoValidator(validators.RegexValidator): 10 | regex = r'^[\w.@+-]+$' 11 | message = _( 12 | 'Enter a valid username no. This value may contain only English letters, ' 13 | 'numbers, and @/./+/-/_ characters.' 14 | ) 15 | flags = 0 16 | -------------------------------------------------------------------------------- /users/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | # import re 2 | 3 | from django.core import validators 4 | from django.utils.deconstruct import deconstructible 5 | from django.utils.translation import gettext_lazy as _ 6 | 7 | 8 | @deconstructible 9 | class DigitserNoValidator(validators.RegexValidator): 10 | regex = r'^[\w.@+-]+$' 11 | message = _( 12 | 'Enter a valid username no. This value may contain only English letters, ' 13 | 'numbers, and @/./+/-/_ characters.' 14 | ) 15 | flags = 0 16 | --------------------------------------------------------------------------------