├── .env ├── .gitignore ├── README.TXT ├── django └── django_project │ ├── Dockerfile │ ├── django_project │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py │ ├── entrypoint.sh │ ├── manage.py │ ├── media │ └── photo │ │ └── nophoto.png │ ├── requirements.txt │ ├── static │ ├── admin │ │ ├── css │ │ │ ├── autocomplete.css │ │ │ ├── base.css │ │ │ ├── changelists.css │ │ │ ├── dark_mode.css │ │ │ ├── dashboard.css │ │ │ ├── fonts.css │ │ │ ├── forms.css │ │ │ ├── login.css │ │ │ ├── nav_sidebar.css │ │ │ ├── responsive.css │ │ │ ├── responsive_rtl.css │ │ │ ├── rtl.css │ │ │ ├── vendor │ │ │ │ └── select2 │ │ │ │ │ ├── LICENSE-SELECT2.md │ │ │ │ │ ├── select2.css │ │ │ │ │ └── select2.min.css │ │ │ └── widgets.css │ │ ├── fonts │ │ │ ├── LICENSE.txt │ │ │ ├── README.txt │ │ │ ├── Roboto-Bold-webfont.woff │ │ │ ├── Roboto-Light-webfont.woff │ │ │ └── Roboto-Regular-webfont.woff │ │ ├── img │ │ │ ├── LICENSE │ │ │ ├── README.txt │ │ │ ├── calendar-icons.svg │ │ │ ├── gis │ │ │ │ ├── move_vertex_off.svg │ │ │ │ └── move_vertex_on.svg │ │ │ ├── icon-addlink.svg │ │ │ ├── icon-alert.svg │ │ │ ├── icon-calendar.svg │ │ │ ├── icon-changelink.svg │ │ │ ├── icon-clock.svg │ │ │ ├── icon-deletelink.svg │ │ │ ├── icon-no.svg │ │ │ ├── icon-unknown-alt.svg │ │ │ ├── icon-unknown.svg │ │ │ ├── icon-viewlink.svg │ │ │ ├── icon-yes.svg │ │ │ ├── inline-delete.svg │ │ │ ├── search.svg │ │ │ ├── selector-icons.svg │ │ │ ├── sorting-icons.svg │ │ │ ├── tooltag-add.svg │ │ │ └── tooltag-arrowright.svg │ │ └── js │ │ │ ├── SelectBox.js │ │ │ ├── SelectFilter2.js │ │ │ ├── actions.js │ │ │ ├── admin │ │ │ ├── DateTimeShortcuts.js │ │ │ └── RelatedObjectLookups.js │ │ │ ├── autocomplete.js │ │ │ ├── calendar.js │ │ │ ├── cancel.js │ │ │ ├── change_form.js │ │ │ ├── collapse.js │ │ │ ├── core.js │ │ │ ├── filters.js │ │ │ ├── inlines.js │ │ │ ├── jquery.init.js │ │ │ ├── nav_sidebar.js │ │ │ ├── popup_response.js │ │ │ ├── prepopulate.js │ │ │ ├── prepopulate_init.js │ │ │ ├── urlify.js │ │ │ └── vendor │ │ │ ├── jquery │ │ │ ├── LICENSE.txt │ │ │ ├── jquery.js │ │ │ └── jquery.min.js │ │ │ ├── select2 │ │ │ ├── LICENSE.md │ │ │ ├── i18n │ │ │ │ ├── af.js │ │ │ │ ├── ar.js │ │ │ │ ├── az.js │ │ │ │ ├── bg.js │ │ │ │ ├── bn.js │ │ │ │ ├── bs.js │ │ │ │ ├── ca.js │ │ │ │ ├── cs.js │ │ │ │ ├── da.js │ │ │ │ ├── de.js │ │ │ │ ├── dsb.js │ │ │ │ ├── el.js │ │ │ │ ├── en.js │ │ │ │ ├── es.js │ │ │ │ ├── et.js │ │ │ │ ├── eu.js │ │ │ │ ├── fa.js │ │ │ │ ├── fi.js │ │ │ │ ├── fr.js │ │ │ │ ├── gl.js │ │ │ │ ├── he.js │ │ │ │ ├── hi.js │ │ │ │ ├── hr.js │ │ │ │ ├── hsb.js │ │ │ │ ├── hu.js │ │ │ │ ├── hy.js │ │ │ │ ├── id.js │ │ │ │ ├── is.js │ │ │ │ ├── it.js │ │ │ │ ├── ja.js │ │ │ │ ├── ka.js │ │ │ │ ├── km.js │ │ │ │ ├── ko.js │ │ │ │ ├── lt.js │ │ │ │ ├── lv.js │ │ │ │ ├── mk.js │ │ │ │ ├── ms.js │ │ │ │ ├── nb.js │ │ │ │ ├── ne.js │ │ │ │ ├── nl.js │ │ │ │ ├── pl.js │ │ │ │ ├── ps.js │ │ │ │ ├── pt-BR.js │ │ │ │ ├── pt.js │ │ │ │ ├── ro.js │ │ │ │ ├── ru.js │ │ │ │ ├── sk.js │ │ │ │ ├── sl.js │ │ │ │ ├── sq.js │ │ │ │ ├── sr-Cyrl.js │ │ │ │ ├── sr.js │ │ │ │ ├── sv.js │ │ │ │ ├── th.js │ │ │ │ ├── tk.js │ │ │ │ ├── tr.js │ │ │ │ ├── uk.js │ │ │ │ ├── vi.js │ │ │ │ ├── zh-CN.js │ │ │ │ └── zh-TW.js │ │ │ ├── select2.full.js │ │ │ └── select2.full.min.js │ │ │ └── xregexp │ │ │ ├── LICENSE.txt │ │ │ ├── xregexp.js │ │ │ └── xregexp.min.js │ └── rest_framework │ │ ├── css │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap-theme.min.css.map │ │ ├── bootstrap-tweaks.css │ │ ├── bootstrap.min.css │ │ ├── bootstrap.min.css.map │ │ ├── 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.5.1.min.js │ │ └── prettify-min.js │ ├── students │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ └── my_command.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_students.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests.py │ └── views.py │ └── worker.py ├── docker-compose.yml ├── nginx ├── Dockerfile └── nginx.conf └── reactapp ├── README.md ├── dockerfile ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── components ├── app │ ├── App.css │ ├── App.js │ └── App.test.js ├── appHeader │ └── Header.js ├── appHome │ └── Home.js ├── appListStudents │ └── ListStudents.js ├── appModalStudent │ └── ModalStudent.js ├── appPhotoModal │ └── ModalPhoto.js ├── appRemoveStudent │ └── appRemoveStudent.js └── appStudentForm │ └── StudentForm.js ├── index.css ├── index.js ├── logo.svg ├── reportWebVitals.js └── setupTests.js /.env: -------------------------------------------------------------------------------- 1 | SECRET_KEY=django-insecure-cfr4pr9x3dbm9vnmvclxn&^a^ml-cl*=c#scbsxn_+m*5mt%z1 2 | DEBUG=1 3 | ALLOWED_HOSTS=* 4 | 5 | 6 | POSTGRES_ENGINE=django.db.backends.postgresql 7 | POSTGRES_DB=django_db 8 | POSTGRES_USER=admin 9 | POSTGRES_PASSWORD=strong_password 10 | POSTGRES_HOST=postgres 11 | POSTGRES_PORT=5432 12 | DATABASE=postgres -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode/ 2 | /reactapp/node_modules/ 3 | /django/env/ 4 | .idea 5 | -------------------------------------------------------------------------------- /README.TXT: -------------------------------------------------------------------------------- 1 | Репозиторий к статье на хабре 2 | https://habr.com/ru/post/713490/ 3 | Написание простого web приложения на react с бэкендом на Django, с БД на postgres, rabbit, nginx и всё завернуто в docker. 4 | 5 | Запуск проекта: 6 | 1) git clone https://github.com/eldalex/ProjectStudent 7 | перейти в ProjectStudent 8 | 2) chmod +x django/django_project/entrypoint.sh 9 | 3) docker-compose up -d 10 | -------------------------------------------------------------------------------- /django/django_project/Dockerfile: -------------------------------------------------------------------------------- 1 | # Стартовый образ, возмем 3.11 python на базе alpine, он поменше. 2 | FROM python:3.11-alpine 3 | 4 | # рабочая директория 5 | WORKDIR /usr/src/app 6 | # Каталоги для статики 7 | RUN mkdir -p $WORKDIR/static 8 | RUN mkdir -p $WORKDIR/media 9 | 10 | # переменные окружения для python 11 | #не создавать файлы кэша .pyc 12 | ENV PYTHONDONTWRITEBYTECODE 1 13 | # не помещать в буфер потоки stdout и stderr 14 | ENV PYTHONUNBUFFERED 1 15 | 16 | RUN apk update \ 17 | && apk add postgresql-dev gcc python3-dev musl-dev 18 | 19 | # обновим pip 20 | RUN pip install --upgrade pip 21 | 22 | # скопируем и установим зависимости. эта операция закешируется 23 | # и будет перезапускаться только при изменении requirements.txt 24 | COPY ./requirements.txt . 25 | RUN pip install -r requirements.txt 26 | 27 | # копируем всё что осталось. 28 | COPY . . 29 | 30 | # Сделаем первую миграцию. 31 | ENTRYPOINT ["/usr/src/app/entrypoint.sh"] 32 | -------------------------------------------------------------------------------- /django/django_project/django_project/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/django_project/__init__.py -------------------------------------------------------------------------------- /django/django_project/django_project/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for django_project project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_project.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /django/django_project/django_project/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for django_project project. 3 | 4 | Generated by 'django-admin startproject' using Django 4.1.5. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/4.1/ref/settings/ 11 | """ 12 | 13 | import os 14 | from pathlib import Path 15 | from os import environ 16 | 17 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 18 | BASE_DIR = Path(__file__).resolve().parent.parent 19 | 20 | 21 | # Quick-start development settings - unsuitable for production 22 | # See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ 23 | 24 | # SECURITY WARNING: keep the secret key used in production secret! 25 | SECRET_KEY = environ.get('SECRET_KEY') 26 | 27 | # SECURITY WARNING: don't run with debug turned on in production! 28 | DEBUG = int(environ.get('DEBUG', default=0)) 29 | 30 | # ALLOWED_HOSTS = environ.get('ALLOWED_HOSTS').split(' ') 31 | ALLOWED_HOSTS = ["*"] 32 | 33 | # Application definition 34 | 35 | INSTALLED_APPS = [ 36 | 'django.contrib.admin', 37 | 'django.contrib.auth', 38 | 'django.contrib.contenttypes', 39 | 'django.contrib.sessions', 40 | 'django.contrib.messages', 41 | 'django.contrib.staticfiles', 42 | 'rest_framework', 43 | 'corsheaders', 44 | 'students' 45 | ] 46 | 47 | MIDDLEWARE = [ 48 | 'django.middleware.security.SecurityMiddleware', 49 | 'django.contrib.sessions.middleware.SessionMiddleware', 50 | 'django.middleware.common.CommonMiddleware', 51 | 'django.middleware.csrf.CsrfViewMiddleware', 52 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 53 | 'django.contrib.messages.middleware.MessageMiddleware', 54 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 55 | 'corsheaders.middleware.CorsMiddleware', 56 | 'django.middleware.common.CommonMiddleware', 57 | ] 58 | 59 | CORS_ORIGIN_ALLOW_ALL = True 60 | 61 | ROOT_URLCONF = 'django_project.urls' 62 | 63 | TEMPLATES = [ 64 | { 65 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 66 | 'DIRS': [], 67 | 'APP_DIRS': True, 68 | 'OPTIONS': { 69 | 'context_processors': [ 70 | 'django.template.context_processors.debug', 71 | 'django.template.context_processors.request', 72 | 'django.contrib.auth.context_processors.auth', 73 | 'django.contrib.messages.context_processors.messages', 74 | ], 75 | }, 76 | }, 77 | ] 78 | 79 | WSGI_APPLICATION = 'django_project.wsgi.application' 80 | 81 | 82 | # Database 83 | # https://docs.djangoproject.com/en/4.1/ref/settings/#databases 84 | 85 | # DATABASES = { 86 | # 'default': { 87 | # 'ENGINE': 'django.db.backends.sqlite3', 88 | # 'NAME': BASE_DIR / 'db.sqlite3', 89 | # } 90 | # } 91 | DATABASES = { 92 | 'default': { 93 | 'ENGINE': environ.get('POSTGRES_ENGINE', 'django.db.backends.sqlite3'), 94 | 'NAME': environ.get('POSTGRES_DB', BASE_DIR / 'db.sqlite3'), 95 | 'USER': environ.get('POSTGRES_USER', 'user'), 96 | 'PASSWORD': environ.get('POSTGRES_PASSWORD', 'password'), 97 | 'HOST': environ.get('POSTGRES_HOST', 'localhost'), 98 | 'PORT': environ.get('POSTGRES_PORT', '5432'), 99 | } 100 | } 101 | 102 | # Password validation 103 | # https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators 104 | 105 | AUTH_PASSWORD_VALIDATORS = [ 106 | { 107 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 108 | }, 109 | { 110 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 111 | }, 112 | { 113 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 114 | }, 115 | { 116 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 117 | }, 118 | ] 119 | 120 | 121 | # Internationalization 122 | # https://docs.djangoproject.com/en/4.1/topics/i18n/ 123 | 124 | LANGUAGE_CODE = 'en-us' 125 | 126 | TIME_ZONE = 'UTC' 127 | 128 | USE_I18N = True 129 | 130 | USE_TZ = True 131 | 132 | 133 | # Static files (CSS, JavaScript, Images) 134 | # https://docs.djangoproject.com/en/4.1/howto/static-files/ 135 | 136 | STATIC_URL = '/static/' 137 | STATIC_ROOT = os.path.join(BASE_DIR, 'static') 138 | 139 | 140 | MEDIA_URL = '/media/' 141 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 142 | 143 | # Default primary key field type 144 | # https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field 145 | 146 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 147 | -------------------------------------------------------------------------------- /django/django_project/django_project/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.urls import path, re_path 3 | from students import views 4 | from django.conf import settings 5 | from django.conf.urls.static import static 6 | 7 | urlpatterns = [ 8 | path('admin/', admin.site.urls), 9 | re_path(r'^api/students/$', views.students_list), 10 | re_path(r'^api/students/(\d+)$', views.students_detail), 11 | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 12 | -------------------------------------------------------------------------------- /django/django_project/django_project/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for django_project project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_project.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /django/django_project/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ "$DATABASE" = "postgres" ] 4 | then 5 | # если база еще не запущена 6 | echo "Рано..." 7 | 8 | # Проверяем доступность хоста и порта 9 | while ! nc -z $POSTGRES_HOST $POSTGRES_PORT; do 10 | sleep 0.1 11 | done 12 | 13 | echo "Пора!" 14 | fi 15 | 16 | # Выполняем миграции 17 | python manage.py migrate 18 | 19 | exec "$@" -------------------------------------------------------------------------------- /django/django_project/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_project.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /django/django_project/media/photo/nophoto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/media/photo/nophoto.png -------------------------------------------------------------------------------- /django/django_project/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/requirements.txt -------------------------------------------------------------------------------- /django/django_project/static/admin/css/changelists.css: -------------------------------------------------------------------------------- 1 | /* CHANGELISTS */ 2 | 3 | #changelist { 4 | display: flex; 5 | align-items: flex-start; 6 | justify-content: space-between; 7 | } 8 | 9 | #changelist .changelist-form-container { 10 | flex: 1 1 auto; 11 | min-width: 0; 12 | } 13 | 14 | #changelist table { 15 | width: 100%; 16 | } 17 | 18 | .change-list .hiddenfields { display:none; } 19 | 20 | .change-list .filtered table { 21 | border-right: none; 22 | } 23 | 24 | .change-list .filtered { 25 | min-height: 400px; 26 | } 27 | 28 | .change-list .filtered .results, .change-list .filtered .paginator, 29 | .filtered #toolbar, .filtered div.xfull { 30 | width: auto; 31 | } 32 | 33 | .change-list .filtered table tbody th { 34 | padding-right: 1em; 35 | } 36 | 37 | #changelist-form .results { 38 | overflow-x: auto; 39 | width: 100%; 40 | } 41 | 42 | #changelist .toplinks { 43 | border-bottom: 1px solid var(--hairline-color); 44 | } 45 | 46 | #changelist .paginator { 47 | color: var(--body-quiet-color); 48 | border-bottom: 1px solid var(--hairline-color); 49 | background: var(--body-bg); 50 | overflow: hidden; 51 | } 52 | 53 | /* CHANGELIST TABLES */ 54 | 55 | #changelist table thead th { 56 | padding: 0; 57 | white-space: nowrap; 58 | vertical-align: middle; 59 | } 60 | 61 | #changelist table thead th.action-checkbox-column { 62 | width: 1.5em; 63 | text-align: center; 64 | } 65 | 66 | #changelist table tbody td.action-checkbox { 67 | text-align: center; 68 | } 69 | 70 | #changelist table tfoot { 71 | color: var(--body-quiet-color); 72 | } 73 | 74 | /* TOOLBAR */ 75 | 76 | #toolbar { 77 | padding: 8px 10px; 78 | margin-bottom: 15px; 79 | border-top: 1px solid var(--hairline-color); 80 | border-bottom: 1px solid var(--hairline-color); 81 | background: var(--darkened-bg); 82 | color: var(--body-quiet-color); 83 | } 84 | 85 | #toolbar form input { 86 | border-radius: 4px; 87 | font-size: 0.875rem; 88 | padding: 5px; 89 | color: var(--body-fg); 90 | } 91 | 92 | #toolbar #searchbar { 93 | height: 19px; 94 | border: 1px solid var(--border-color); 95 | padding: 2px 5px; 96 | margin: 0; 97 | vertical-align: top; 98 | font-size: 0.8125rem; 99 | max-width: 100%; 100 | } 101 | 102 | #toolbar #searchbar:focus { 103 | border-color: var(--body-quiet-color); 104 | } 105 | 106 | #toolbar form input[type="submit"] { 107 | border: 1px solid var(--border-color); 108 | font-size: 0.8125rem; 109 | padding: 4px 8px; 110 | margin: 0; 111 | vertical-align: middle; 112 | background: var(--body-bg); 113 | box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset; 114 | cursor: pointer; 115 | color: var(--body-fg); 116 | } 117 | 118 | #toolbar form input[type="submit"]:focus, 119 | #toolbar form input[type="submit"]:hover { 120 | border-color: var(--body-quiet-color); 121 | } 122 | 123 | #changelist-search img { 124 | vertical-align: middle; 125 | margin-right: 4px; 126 | } 127 | 128 | #changelist-search .help { 129 | word-break: break-word; 130 | } 131 | 132 | /* FILTER COLUMN */ 133 | 134 | #changelist-filter { 135 | flex: 0 0 240px; 136 | order: 1; 137 | background: var(--darkened-bg); 138 | border-left: none; 139 | margin: 0 0 0 30px; 140 | } 141 | 142 | #changelist-filter h2 { 143 | font-size: 0.875rem; 144 | text-transform: uppercase; 145 | letter-spacing: 0.5px; 146 | padding: 5px 15px; 147 | margin-bottom: 12px; 148 | border-bottom: none; 149 | } 150 | 151 | #changelist-filter h3, 152 | #changelist-filter details summary { 153 | font-weight: 400; 154 | padding: 0 15px; 155 | margin-bottom: 10px; 156 | } 157 | 158 | #changelist-filter details summary > * { 159 | display: inline; 160 | } 161 | 162 | #changelist-filter details > summary { 163 | list-style-type: none; 164 | } 165 | 166 | #changelist-filter details > summary::-webkit-details-marker { 167 | display: none; 168 | } 169 | 170 | #changelist-filter details > summary::before { 171 | content: '→'; 172 | font-weight: bold; 173 | color: var(--link-hover-color); 174 | } 175 | 176 | #changelist-filter details[open] > summary::before { 177 | content: '↓'; 178 | } 179 | 180 | #changelist-filter ul { 181 | margin: 5px 0; 182 | padding: 0 15px 15px; 183 | border-bottom: 1px solid var(--hairline-color); 184 | } 185 | 186 | #changelist-filter ul:last-child { 187 | border-bottom: none; 188 | } 189 | 190 | #changelist-filter li { 191 | list-style-type: none; 192 | margin-left: 0; 193 | padding-left: 0; 194 | } 195 | 196 | #changelist-filter a { 197 | display: block; 198 | color: var(--body-quiet-color); 199 | word-break: break-word; 200 | } 201 | 202 | #changelist-filter li.selected { 203 | border-left: 5px solid var(--hairline-color); 204 | padding-left: 10px; 205 | margin-left: -15px; 206 | } 207 | 208 | #changelist-filter li.selected a { 209 | color: var(--link-selected-fg); 210 | } 211 | 212 | #changelist-filter a:focus, #changelist-filter a:hover, 213 | #changelist-filter li.selected a:focus, 214 | #changelist-filter li.selected a:hover { 215 | color: var(--link-hover-color); 216 | } 217 | 218 | #changelist-filter #changelist-filter-clear a { 219 | font-size: 0.8125rem; 220 | padding-bottom: 10px; 221 | border-bottom: 1px solid var(--hairline-color); 222 | } 223 | 224 | /* DATE DRILLDOWN */ 225 | 226 | .change-list ul.toplinks { 227 | display: block; 228 | float: left; 229 | padding: 0; 230 | margin: 0; 231 | width: 100%; 232 | } 233 | 234 | .change-list ul.toplinks li { 235 | padding: 3px 6px; 236 | font-weight: bold; 237 | list-style-type: none; 238 | display: inline-block; 239 | } 240 | 241 | .change-list ul.toplinks .date-back a { 242 | color: var(--body-quiet-color); 243 | } 244 | 245 | .change-list ul.toplinks .date-back a:focus, 246 | .change-list ul.toplinks .date-back a:hover { 247 | color: var(--link-hover-color); 248 | } 249 | 250 | /* ACTIONS */ 251 | 252 | .filtered .actions { 253 | border-right: none; 254 | } 255 | 256 | #changelist table input { 257 | margin: 0; 258 | vertical-align: baseline; 259 | } 260 | 261 | #changelist table tbody tr.selected { 262 | background-color: var(--selected-row); 263 | } 264 | 265 | #changelist .actions { 266 | padding: 10px; 267 | background: var(--body-bg); 268 | border-top: none; 269 | border-bottom: none; 270 | line-height: 24px; 271 | color: var(--body-quiet-color); 272 | width: 100%; 273 | } 274 | 275 | #changelist .actions span.all, 276 | #changelist .actions span.action-counter, 277 | #changelist .actions span.clear, 278 | #changelist .actions span.question { 279 | font-size: 0.8125rem; 280 | margin: 0 0.5em; 281 | } 282 | 283 | #changelist .actions:last-child { 284 | border-bottom: none; 285 | } 286 | 287 | #changelist .actions select { 288 | vertical-align: top; 289 | height: 24px; 290 | color: var(--body-fg); 291 | border: 1px solid var(--border-color); 292 | border-radius: 4px; 293 | font-size: 0.875rem; 294 | padding: 0 0 0 4px; 295 | margin: 0; 296 | margin-left: 10px; 297 | } 298 | 299 | #changelist .actions select:focus { 300 | border-color: var(--body-quiet-color); 301 | } 302 | 303 | #changelist .actions label { 304 | display: inline-block; 305 | vertical-align: middle; 306 | font-size: 0.8125rem; 307 | } 308 | 309 | #changelist .actions .button { 310 | font-size: 0.8125rem; 311 | border: 1px solid var(--border-color); 312 | border-radius: 4px; 313 | background: var(--body-bg); 314 | box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset; 315 | cursor: pointer; 316 | height: 24px; 317 | line-height: 1; 318 | padding: 4px 8px; 319 | margin: 0; 320 | color: var(--body-fg); 321 | } 322 | 323 | #changelist .actions .button:focus, #changelist .actions .button:hover { 324 | border-color: var(--body-quiet-color); 325 | } 326 | -------------------------------------------------------------------------------- /django/django_project/static/admin/css/dark_mode.css: -------------------------------------------------------------------------------- 1 | @media (prefers-color-scheme: dark) { 2 | :root { 3 | --primary: #264b5d; 4 | --primary-fg: #f7f7f7; 5 | 6 | --body-fg: #eeeeee; 7 | --body-bg: #121212; 8 | --body-quiet-color: #e0e0e0; 9 | --body-loud-color: #ffffff; 10 | 11 | --breadcrumbs-link-fg: #e0e0e0; 12 | --breadcrumbs-bg: var(--primary); 13 | 14 | --link-fg: #81d4fa; 15 | --link-hover-color: #4ac1f7; 16 | --link-selected-fg: #6f94c6; 17 | 18 | --hairline-color: #272727; 19 | --border-color: #353535; 20 | 21 | --error-fg: #e35f5f; 22 | --message-success-bg: #006b1b; 23 | --message-warning-bg: #583305; 24 | --message-error-bg: #570808; 25 | 26 | --darkened-bg: #212121; 27 | --selected-bg: #1b1b1b; 28 | --selected-row: #00363a; 29 | 30 | --close-button-bg: #333333; 31 | --close-button-hover-bg: #666666; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /django/django_project/static/admin/css/dashboard.css: -------------------------------------------------------------------------------- 1 | /* DASHBOARD */ 2 | 3 | .dashboard .module table th { 4 | width: 100%; 5 | } 6 | 7 | .dashboard .module table td { 8 | white-space: nowrap; 9 | } 10 | 11 | .dashboard .module table td a { 12 | display: block; 13 | padding-right: .6em; 14 | } 15 | 16 | /* RECENT ACTIONS MODULE */ 17 | 18 | .module ul.actionlist { 19 | margin-left: 0; 20 | } 21 | 22 | ul.actionlist li { 23 | list-style-type: none; 24 | overflow: hidden; 25 | text-overflow: ellipsis; 26 | } 27 | -------------------------------------------------------------------------------- /django/django_project/static/admin/css/fonts.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Roboto'; 3 | src: url('../fonts/Roboto-Bold-webfont.woff'); 4 | font-weight: 700; 5 | font-style: normal; 6 | } 7 | 8 | @font-face { 9 | font-family: 'Roboto'; 10 | src: url('../fonts/Roboto-Regular-webfont.woff'); 11 | font-weight: 400; 12 | font-style: normal; 13 | } 14 | 15 | @font-face { 16 | font-family: 'Roboto'; 17 | src: url('../fonts/Roboto-Light-webfont.woff'); 18 | font-weight: 300; 19 | font-style: normal; 20 | } 21 | -------------------------------------------------------------------------------- /django/django_project/static/admin/css/login.css: -------------------------------------------------------------------------------- 1 | /* LOGIN FORM */ 2 | 3 | .login { 4 | background: var(--darkened-bg); 5 | height: auto; 6 | } 7 | 8 | .login #header { 9 | height: auto; 10 | padding: 15px 16px; 11 | justify-content: center; 12 | } 13 | 14 | .login #header h1 { 15 | font-size: 1.125rem; 16 | margin: 0; 17 | } 18 | 19 | .login #header h1 a { 20 | color: var(--header-link-color); 21 | } 22 | 23 | .login #content { 24 | padding: 20px 20px 0; 25 | } 26 | 27 | .login #container { 28 | background: var(--body-bg); 29 | border: 1px solid var(--hairline-color); 30 | border-radius: 4px; 31 | overflow: hidden; 32 | width: 28em; 33 | min-width: 300px; 34 | margin: 100px auto; 35 | height: auto; 36 | } 37 | 38 | .login .form-row { 39 | padding: 4px 0; 40 | } 41 | 42 | .login .form-row label { 43 | display: block; 44 | line-height: 2em; 45 | } 46 | 47 | .login .form-row #id_username, .login .form-row #id_password { 48 | padding: 8px; 49 | width: 100%; 50 | box-sizing: border-box; 51 | } 52 | 53 | .login .submit-row { 54 | padding: 1em 0 0 0; 55 | margin: 0; 56 | text-align: center; 57 | } 58 | 59 | .login .password-reset-link { 60 | text-align: center; 61 | } 62 | -------------------------------------------------------------------------------- /django/django_project/static/admin/css/nav_sidebar.css: -------------------------------------------------------------------------------- 1 | .sticky { 2 | position: sticky; 3 | top: 0; 4 | max-height: 100vh; 5 | } 6 | 7 | .toggle-nav-sidebar { 8 | z-index: 20; 9 | left: 0; 10 | display: flex; 11 | align-items: center; 12 | justify-content: center; 13 | flex: 0 0 23px; 14 | width: 23px; 15 | border: 0; 16 | border-right: 1px solid var(--hairline-color); 17 | background-color: var(--body-bg); 18 | cursor: pointer; 19 | font-size: 1.25rem; 20 | color: var(--link-fg); 21 | padding: 0; 22 | } 23 | 24 | [dir="rtl"] .toggle-nav-sidebar { 25 | border-left: 1px solid var(--hairline-color); 26 | border-right: 0; 27 | } 28 | 29 | .toggle-nav-sidebar:hover, 30 | .toggle-nav-sidebar:focus { 31 | background-color: var(--darkened-bg); 32 | } 33 | 34 | #nav-sidebar { 35 | z-index: 15; 36 | flex: 0 0 275px; 37 | left: -276px; 38 | margin-left: -276px; 39 | border-top: 1px solid transparent; 40 | border-right: 1px solid var(--hairline-color); 41 | background-color: var(--body-bg); 42 | overflow: auto; 43 | } 44 | 45 | [dir="rtl"] #nav-sidebar { 46 | border-left: 1px solid var(--hairline-color); 47 | border-right: 0; 48 | left: 0; 49 | margin-left: 0; 50 | right: -276px; 51 | margin-right: -276px; 52 | } 53 | 54 | .toggle-nav-sidebar::before { 55 | content: '\00BB'; 56 | } 57 | 58 | .main.shifted .toggle-nav-sidebar::before { 59 | content: '\00AB'; 60 | } 61 | 62 | .main.shifted > #nav-sidebar { 63 | margin-left: 0; 64 | } 65 | 66 | [dir="rtl"] .main.shifted > #nav-sidebar { 67 | margin-right: 0; 68 | } 69 | 70 | #nav-sidebar .module th { 71 | width: 100%; 72 | overflow-wrap: anywhere; 73 | } 74 | 75 | #nav-sidebar .module th, 76 | #nav-sidebar .module caption { 77 | padding-left: 16px; 78 | } 79 | 80 | #nav-sidebar .module td { 81 | white-space: nowrap; 82 | } 83 | 84 | [dir="rtl"] #nav-sidebar .module th, 85 | [dir="rtl"] #nav-sidebar .module caption { 86 | padding-left: 8px; 87 | padding-right: 16px; 88 | } 89 | 90 | #nav-sidebar .current-app .section:link, 91 | #nav-sidebar .current-app .section:visited { 92 | color: var(--header-color); 93 | font-weight: bold; 94 | } 95 | 96 | #nav-sidebar .current-model { 97 | background: var(--selected-row); 98 | } 99 | 100 | .main > #nav-sidebar + .content { 101 | max-width: calc(100% - 23px); 102 | } 103 | 104 | .main.shifted > #nav-sidebar + .content { 105 | max-width: calc(100% - 299px); 106 | } 107 | 108 | @media (max-width: 767px) { 109 | #nav-sidebar, #toggle-nav-sidebar { 110 | display: none; 111 | } 112 | 113 | .main > #nav-sidebar + .content, 114 | .main.shifted > #nav-sidebar + .content { 115 | max-width: 100%; 116 | } 117 | } 118 | 119 | #nav-filter { 120 | width: 100%; 121 | box-sizing: border-box; 122 | padding: 2px 5px; 123 | margin: 5px 0; 124 | border: 1px solid var(--border-color); 125 | background-color: var(--darkened-bg); 126 | color: var(--body-fg); 127 | } 128 | 129 | #nav-filter:focus { 130 | border-color: var(--body-quiet-color); 131 | } 132 | 133 | #nav-filter.no-results { 134 | background: var(--message-error-bg); 135 | } 136 | 137 | #nav-sidebar table { 138 | width: 100%; 139 | } 140 | -------------------------------------------------------------------------------- /django/django_project/static/admin/css/responsive_rtl.css: -------------------------------------------------------------------------------- 1 | /* TABLETS */ 2 | 3 | @media (max-width: 1024px) { 4 | [dir="rtl"] .colMS { 5 | margin-right: 0; 6 | } 7 | 8 | [dir="rtl"] #user-tools { 9 | text-align: right; 10 | } 11 | 12 | [dir="rtl"] #changelist .actions label { 13 | padding-left: 10px; 14 | padding-right: 0; 15 | } 16 | 17 | [dir="rtl"] #changelist .actions select { 18 | margin-left: 0; 19 | margin-right: 15px; 20 | } 21 | 22 | [dir="rtl"] .change-list .filtered .results, 23 | [dir="rtl"] .change-list .filtered .paginator, 24 | [dir="rtl"] .filtered #toolbar, 25 | [dir="rtl"] .filtered div.xfull, 26 | [dir="rtl"] .filtered .actions, 27 | [dir="rtl"] #changelist-filter { 28 | margin-left: 0; 29 | } 30 | 31 | [dir="rtl"] .inline-group ul.tools a.add, 32 | [dir="rtl"] .inline-group div.add-row a, 33 | [dir="rtl"] .inline-group .tabular tr.add-row td a { 34 | padding: 8px 26px 8px 10px; 35 | background-position: calc(100% - 8px) 9px; 36 | } 37 | 38 | [dir="rtl"] .related-widget-wrapper-link + .selector { 39 | margin-right: 0; 40 | margin-left: 15px; 41 | } 42 | 43 | [dir="rtl"] .selector .selector-filter label { 44 | margin-right: 0; 45 | margin-left: 8px; 46 | } 47 | 48 | [dir="rtl"] .object-tools li { 49 | float: right; 50 | } 51 | 52 | [dir="rtl"] .object-tools li + li { 53 | margin-left: 0; 54 | margin-right: 15px; 55 | } 56 | 57 | [dir="rtl"] .dashboard .module table td a { 58 | padding-left: 0; 59 | padding-right: 16px; 60 | } 61 | } 62 | 63 | /* MOBILE */ 64 | 65 | @media (max-width: 767px) { 66 | [dir="rtl"] .aligned .related-lookup, 67 | [dir="rtl"] .aligned .datetimeshortcuts { 68 | margin-left: 0; 69 | margin-right: 15px; 70 | } 71 | 72 | [dir="rtl"] .aligned ul { 73 | margin-right: 0; 74 | } 75 | 76 | [dir="rtl"] #changelist-filter { 77 | margin-left: 0; 78 | margin-right: 0; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /django/django_project/static/admin/css/rtl.css: -------------------------------------------------------------------------------- 1 | /* GLOBAL */ 2 | 3 | th { 4 | text-align: right; 5 | } 6 | 7 | .module h2, .module caption { 8 | text-align: right; 9 | } 10 | 11 | .module ul, .module ol { 12 | margin-left: 0; 13 | margin-right: 1.5em; 14 | } 15 | 16 | .viewlink, .addlink, .changelink { 17 | padding-left: 0; 18 | padding-right: 16px; 19 | background-position: 100% 1px; 20 | } 21 | 22 | .deletelink { 23 | padding-left: 0; 24 | padding-right: 16px; 25 | background-position: 100% 1px; 26 | } 27 | 28 | .object-tools { 29 | float: left; 30 | } 31 | 32 | thead th:first-child, 33 | tfoot td:first-child { 34 | border-left: none; 35 | } 36 | 37 | /* LAYOUT */ 38 | 39 | #user-tools { 40 | right: auto; 41 | left: 0; 42 | text-align: left; 43 | } 44 | 45 | div.breadcrumbs { 46 | text-align: right; 47 | } 48 | 49 | #content-main { 50 | float: right; 51 | } 52 | 53 | #content-related { 54 | float: left; 55 | margin-left: -300px; 56 | margin-right: auto; 57 | } 58 | 59 | .colMS { 60 | margin-left: 300px; 61 | margin-right: 0; 62 | } 63 | 64 | /* SORTABLE TABLES */ 65 | 66 | table thead th.sorted .sortoptions { 67 | float: left; 68 | } 69 | 70 | thead th.sorted .text { 71 | padding-right: 0; 72 | padding-left: 42px; 73 | } 74 | 75 | /* dashboard styles */ 76 | 77 | .dashboard .module table td a { 78 | padding-left: .6em; 79 | padding-right: 16px; 80 | } 81 | 82 | /* changelists styles */ 83 | 84 | .change-list .filtered table { 85 | border-left: none; 86 | border-right: 0px none; 87 | } 88 | 89 | #changelist-filter { 90 | border-left: none; 91 | border-right: none; 92 | margin-left: 0; 93 | margin-right: 30px; 94 | } 95 | 96 | #changelist-filter li.selected { 97 | border-left: none; 98 | padding-left: 10px; 99 | margin-left: 0; 100 | border-right: 5px solid var(--hairline-color); 101 | padding-right: 10px; 102 | margin-right: -15px; 103 | } 104 | 105 | #changelist table tbody td:first-child, #changelist table tbody th:first-child { 106 | border-right: none; 107 | border-left: none; 108 | } 109 | 110 | /* FORMS */ 111 | 112 | .aligned label { 113 | padding: 0 0 3px 1em; 114 | float: right; 115 | } 116 | 117 | .submit-row { 118 | text-align: left 119 | } 120 | 121 | .submit-row p.deletelink-box { 122 | float: right; 123 | } 124 | 125 | .submit-row input.default { 126 | margin-left: 0; 127 | } 128 | 129 | .vDateField, .vTimeField { 130 | margin-left: 2px; 131 | } 132 | 133 | .aligned .form-row input { 134 | margin-left: 5px; 135 | } 136 | 137 | form .aligned p.help, form .aligned div.help { 138 | clear: right; 139 | } 140 | 141 | form .aligned ul { 142 | margin-right: 163px; 143 | margin-left: 0; 144 | } 145 | 146 | form ul.inline li { 147 | float: right; 148 | padding-right: 0; 149 | padding-left: 7px; 150 | } 151 | 152 | input[type=submit].default, .submit-row input.default { 153 | float: left; 154 | } 155 | 156 | fieldset .fieldBox { 157 | float: right; 158 | margin-left: 20px; 159 | margin-right: 0; 160 | } 161 | 162 | .errorlist li { 163 | background-position: 100% 12px; 164 | padding: 0; 165 | } 166 | 167 | .errornote { 168 | background-position: 100% 12px; 169 | padding: 10px 12px; 170 | } 171 | 172 | /* WIDGETS */ 173 | 174 | .calendarnav-previous { 175 | top: 0; 176 | left: auto; 177 | right: 10px; 178 | background: url(../img/calendar-icons.svg) 0 -30px no-repeat; 179 | } 180 | 181 | .calendarbox .calendarnav-previous:focus, 182 | .calendarbox .calendarnav-previous:hover { 183 | background-position: 0 -45px; 184 | } 185 | 186 | .calendarnav-next { 187 | top: 0; 188 | right: auto; 189 | left: 10px; 190 | background: url(../img/calendar-icons.svg) 0 0 no-repeat; 191 | } 192 | 193 | .calendarbox .calendarnav-next:focus, 194 | .calendarbox .calendarnav-next:hover { 195 | background-position: 0 -15px; 196 | } 197 | 198 | .calendar caption, .calendarbox h2 { 199 | text-align: center; 200 | } 201 | 202 | .selector { 203 | float: right; 204 | } 205 | 206 | .selector .selector-filter { 207 | text-align: right; 208 | } 209 | 210 | .inline-deletelink { 211 | float: left; 212 | } 213 | 214 | form .form-row p.datetime { 215 | overflow: hidden; 216 | } 217 | 218 | .related-widget-wrapper { 219 | float: right; 220 | } 221 | 222 | /* MISC */ 223 | 224 | .inline-related h2, .inline-group h2 { 225 | text-align: right 226 | } 227 | 228 | .inline-related h3 span.delete { 229 | padding-right: 20px; 230 | padding-left: inherit; 231 | left: 10px; 232 | right: inherit; 233 | float:left; 234 | } 235 | 236 | .inline-related h3 span.delete label { 237 | margin-left: inherit; 238 | margin-right: 2px; 239 | } 240 | -------------------------------------------------------------------------------- /django/django_project/static/admin/css/vendor/select2/LICENSE-SELECT2.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012-2017 Kevin Brown, Igor Vaynberg, and Select2 contributors 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /django/django_project/static/admin/fonts/README.txt: -------------------------------------------------------------------------------- 1 | Roboto webfont source: https://www.google.com/fonts/specimen/Roboto 2 | WOFF files extracted using https://github.com/majodev/google-webfonts-helper 3 | Weights used in this project: Light (300), Regular (400), Bold (700) 4 | -------------------------------------------------------------------------------- /django/django_project/static/admin/fonts/Roboto-Bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/static/admin/fonts/Roboto-Bold-webfont.woff -------------------------------------------------------------------------------- /django/django_project/static/admin/fonts/Roboto-Light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/static/admin/fonts/Roboto-Light-webfont.woff -------------------------------------------------------------------------------- /django/django_project/static/admin/fonts/Roboto-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/static/admin/fonts/Roboto-Regular-webfont.woff -------------------------------------------------------------------------------- /django/django_project/static/admin/img/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Code Charm Ltd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/README.txt: -------------------------------------------------------------------------------- 1 | All icons are taken from Font Awesome (http://fontawesome.io/) project. 2 | The Font Awesome font is licensed under the SIL OFL 1.1: 3 | - https://scripts.sil.org/OFL 4 | 5 | SVG icons source: https://github.com/encharm/Font-Awesome-SVG-PNG 6 | Font-Awesome-SVG-PNG is licensed under the MIT license (see file license 7 | in current folder). 8 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/calendar-icons.svg: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/gis/move_vertex_off.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/gis/move_vertex_on.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/icon-addlink.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/icon-alert.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/icon-calendar.svg: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/icon-changelink.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/icon-clock.svg: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/icon-deletelink.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/icon-no.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/icon-unknown-alt.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/icon-unknown.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/icon-viewlink.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/icon-yes.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/inline-delete.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/search.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/selector-icons.svg: -------------------------------------------------------------------------------- 1 | 35 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/sorting-icons.svg: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/tooltag-add.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /django/django_project/static/admin/img/tooltag-arrowright.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /django/django_project/static/admin/js/SelectBox.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | { 3 | const SelectBox = { 4 | cache: {}, 5 | init: function(id) { 6 | const box = document.getElementById(id); 7 | SelectBox.cache[id] = []; 8 | const cache = SelectBox.cache[id]; 9 | for (const node of box.options) { 10 | cache.push({value: node.value, text: node.text, displayed: 1}); 11 | } 12 | }, 13 | redisplay: function(id) { 14 | // Repopulate HTML select box from cache 15 | const box = document.getElementById(id); 16 | const scroll_value_from_top = box.scrollTop; 17 | box.innerHTML = ''; 18 | for (const node of SelectBox.cache[id]) { 19 | if (node.displayed) { 20 | const new_option = new Option(node.text, node.value, false, false); 21 | // Shows a tooltip when hovering over the option 22 | new_option.title = node.text; 23 | box.appendChild(new_option); 24 | } 25 | } 26 | box.scrollTop = scroll_value_from_top; 27 | }, 28 | filter: function(id, text) { 29 | // Redisplay the HTML select box, displaying only the choices containing ALL 30 | // the words in text. (It's an AND search.) 31 | const tokens = text.toLowerCase().split(/\s+/); 32 | for (const node of SelectBox.cache[id]) { 33 | node.displayed = 1; 34 | const node_text = node.text.toLowerCase(); 35 | for (const token of tokens) { 36 | if (!node_text.includes(token)) { 37 | node.displayed = 0; 38 | break; // Once the first token isn't found we're done 39 | } 40 | } 41 | } 42 | SelectBox.redisplay(id); 43 | }, 44 | delete_from_cache: function(id, value) { 45 | let delete_index = null; 46 | const cache = SelectBox.cache[id]; 47 | for (const [i, node] of cache.entries()) { 48 | if (node.value === value) { 49 | delete_index = i; 50 | break; 51 | } 52 | } 53 | cache.splice(delete_index, 1); 54 | }, 55 | add_to_cache: function(id, option) { 56 | SelectBox.cache[id].push({value: option.value, text: option.text, displayed: 1}); 57 | }, 58 | cache_contains: function(id, value) { 59 | // Check if an item is contained in the cache 60 | for (const node of SelectBox.cache[id]) { 61 | if (node.value === value) { 62 | return true; 63 | } 64 | } 65 | return false; 66 | }, 67 | move: function(from, to) { 68 | const from_box = document.getElementById(from); 69 | for (const option of from_box.options) { 70 | const option_value = option.value; 71 | if (option.selected && SelectBox.cache_contains(from, option_value)) { 72 | SelectBox.add_to_cache(to, {value: option_value, text: option.text, displayed: 1}); 73 | SelectBox.delete_from_cache(from, option_value); 74 | } 75 | } 76 | SelectBox.redisplay(from); 77 | SelectBox.redisplay(to); 78 | }, 79 | move_all: function(from, to) { 80 | const from_box = document.getElementById(from); 81 | for (const option of from_box.options) { 82 | const option_value = option.value; 83 | if (SelectBox.cache_contains(from, option_value)) { 84 | SelectBox.add_to_cache(to, {value: option_value, text: option.text, displayed: 1}); 85 | SelectBox.delete_from_cache(from, option_value); 86 | } 87 | } 88 | SelectBox.redisplay(from); 89 | SelectBox.redisplay(to); 90 | }, 91 | sort: function(id) { 92 | SelectBox.cache[id].sort(function(a, b) { 93 | a = a.text.toLowerCase(); 94 | b = b.text.toLowerCase(); 95 | if (a > b) { 96 | return 1; 97 | } 98 | if (a < b) { 99 | return -1; 100 | } 101 | return 0; 102 | } ); 103 | }, 104 | select_all: function(id) { 105 | const box = document.getElementById(id); 106 | for (const option of box.options) { 107 | option.selected = true; 108 | } 109 | } 110 | }; 111 | window.SelectBox = SelectBox; 112 | } 113 | -------------------------------------------------------------------------------- /django/django_project/static/admin/js/autocomplete.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | { 3 | const $ = django.jQuery; 4 | 5 | $.fn.djangoAdminSelect2 = function() { 6 | $.each(this, function(i, element) { 7 | $(element).select2({ 8 | ajax: { 9 | data: (params) => { 10 | return { 11 | term: params.term, 12 | page: params.page, 13 | app_label: element.dataset.appLabel, 14 | model_name: element.dataset.modelName, 15 | field_name: element.dataset.fieldName 16 | }; 17 | } 18 | } 19 | }); 20 | }); 21 | return this; 22 | }; 23 | 24 | $(function() { 25 | // Initialize all autocomplete widgets except the one in the template 26 | // form used when a new formset is added. 27 | $('.admin-autocomplete').not('[name*=__prefix__]').djangoAdminSelect2(); 28 | }); 29 | 30 | document.addEventListener('formset:added', (event) => { 31 | $(event.target).find('.admin-autocomplete').djangoAdminSelect2(); 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /django/django_project/static/admin/js/cancel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | { 3 | // Call function fn when the DOM is loaded and ready. If it is already 4 | // loaded, call the function now. 5 | // http://youmightnotneedjquery.com/#ready 6 | function ready(fn) { 7 | if (document.readyState !== 'loading') { 8 | fn(); 9 | } else { 10 | document.addEventListener('DOMContentLoaded', fn); 11 | } 12 | } 13 | 14 | ready(function() { 15 | function handleClick(event) { 16 | event.preventDefault(); 17 | const params = new URLSearchParams(window.location.search); 18 | if (params.has('_popup')) { 19 | window.close(); // Close the popup. 20 | } else { 21 | window.history.back(); // Otherwise, go back. 22 | } 23 | } 24 | 25 | document.querySelectorAll('.cancel-link').forEach(function(el) { 26 | el.addEventListener('click', handleClick); 27 | }); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /django/django_project/static/admin/js/change_form.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | { 3 | const inputTags = ['BUTTON', 'INPUT', 'SELECT', 'TEXTAREA']; 4 | const modelName = document.getElementById('django-admin-form-add-constants').dataset.modelName; 5 | if (modelName) { 6 | const form = document.getElementById(modelName + '_form'); 7 | for (const element of form.elements) { 8 | // HTMLElement.offsetParent returns null when the element is not 9 | // rendered. 10 | if (inputTags.includes(element.tagName) && !element.disabled && element.offsetParent) { 11 | element.focus(); 12 | break; 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /django/django_project/static/admin/js/collapse.js: -------------------------------------------------------------------------------- 1 | /*global gettext*/ 2 | 'use strict'; 3 | { 4 | window.addEventListener('load', function() { 5 | // Add anchor tag for Show/Hide link 6 | const fieldsets = document.querySelectorAll('fieldset.collapse'); 7 | for (const [i, elem] of fieldsets.entries()) { 8 | // Don't hide if fields in this fieldset have errors 9 | if (elem.querySelectorAll('div.errors, ul.errorlist').length === 0) { 10 | elem.classList.add('collapsed'); 11 | const h2 = elem.querySelector('h2'); 12 | const link = document.createElement('a'); 13 | link.id = 'fieldsetcollapser' + i; 14 | link.className = 'collapse-toggle'; 15 | link.href = '#'; 16 | link.textContent = gettext('Show'); 17 | h2.appendChild(document.createTextNode(' (')); 18 | h2.appendChild(link); 19 | h2.appendChild(document.createTextNode(')')); 20 | } 21 | } 22 | // Add toggle to hide/show anchor tag 23 | const toggleFunc = function(ev) { 24 | if (ev.target.matches('.collapse-toggle')) { 25 | ev.preventDefault(); 26 | ev.stopPropagation(); 27 | const fieldset = ev.target.closest('fieldset'); 28 | if (fieldset.classList.contains('collapsed')) { 29 | // Show 30 | ev.target.textContent = gettext('Hide'); 31 | fieldset.classList.remove('collapsed'); 32 | } else { 33 | // Hide 34 | ev.target.textContent = gettext('Show'); 35 | fieldset.classList.add('collapsed'); 36 | } 37 | } 38 | }; 39 | document.querySelectorAll('fieldset.module').forEach(function(el) { 40 | el.addEventListener('click', toggleFunc); 41 | }); 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /django/django_project/static/admin/js/core.js: -------------------------------------------------------------------------------- 1 | // Core JavaScript helper functions 2 | 'use strict'; 3 | 4 | // quickElement(tagType, parentReference [, textInChildNode, attribute, attributeValue ...]); 5 | function quickElement() { 6 | const obj = document.createElement(arguments[0]); 7 | if (arguments[2]) { 8 | const textNode = document.createTextNode(arguments[2]); 9 | obj.appendChild(textNode); 10 | } 11 | const len = arguments.length; 12 | for (let i = 3; i < len; i += 2) { 13 | obj.setAttribute(arguments[i], arguments[i + 1]); 14 | } 15 | arguments[1].appendChild(obj); 16 | return obj; 17 | } 18 | 19 | // "a" is reference to an object 20 | function removeChildren(a) { 21 | while (a.hasChildNodes()) { 22 | a.removeChild(a.lastChild); 23 | } 24 | } 25 | 26 | // ---------------------------------------------------------------------------- 27 | // Find-position functions by PPK 28 | // See https://www.quirksmode.org/js/findpos.html 29 | // ---------------------------------------------------------------------------- 30 | function findPosX(obj) { 31 | let curleft = 0; 32 | if (obj.offsetParent) { 33 | while (obj.offsetParent) { 34 | curleft += obj.offsetLeft - obj.scrollLeft; 35 | obj = obj.offsetParent; 36 | } 37 | } else if (obj.x) { 38 | curleft += obj.x; 39 | } 40 | return curleft; 41 | } 42 | 43 | function findPosY(obj) { 44 | let curtop = 0; 45 | if (obj.offsetParent) { 46 | while (obj.offsetParent) { 47 | curtop += obj.offsetTop - obj.scrollTop; 48 | obj = obj.offsetParent; 49 | } 50 | } else if (obj.y) { 51 | curtop += obj.y; 52 | } 53 | return curtop; 54 | } 55 | 56 | //----------------------------------------------------------------------------- 57 | // Date object extensions 58 | // ---------------------------------------------------------------------------- 59 | { 60 | Date.prototype.getTwelveHours = function() { 61 | return this.getHours() % 12 || 12; 62 | }; 63 | 64 | Date.prototype.getTwoDigitMonth = function() { 65 | return (this.getMonth() < 9) ? '0' + (this.getMonth() + 1) : (this.getMonth() + 1); 66 | }; 67 | 68 | Date.prototype.getTwoDigitDate = function() { 69 | return (this.getDate() < 10) ? '0' + this.getDate() : this.getDate(); 70 | }; 71 | 72 | Date.prototype.getTwoDigitTwelveHour = function() { 73 | return (this.getTwelveHours() < 10) ? '0' + this.getTwelveHours() : this.getTwelveHours(); 74 | }; 75 | 76 | Date.prototype.getTwoDigitHour = function() { 77 | return (this.getHours() < 10) ? '0' + this.getHours() : this.getHours(); 78 | }; 79 | 80 | Date.prototype.getTwoDigitMinute = function() { 81 | return (this.getMinutes() < 10) ? '0' + this.getMinutes() : this.getMinutes(); 82 | }; 83 | 84 | Date.prototype.getTwoDigitSecond = function() { 85 | return (this.getSeconds() < 10) ? '0' + this.getSeconds() : this.getSeconds(); 86 | }; 87 | 88 | Date.prototype.getAbbrevMonthName = function() { 89 | return typeof window.CalendarNamespace === "undefined" 90 | ? this.getTwoDigitMonth() 91 | : window.CalendarNamespace.monthsOfYearAbbrev[this.getMonth()]; 92 | }; 93 | 94 | Date.prototype.getFullMonthName = function() { 95 | return typeof window.CalendarNamespace === "undefined" 96 | ? this.getTwoDigitMonth() 97 | : window.CalendarNamespace.monthsOfYear[this.getMonth()]; 98 | }; 99 | 100 | Date.prototype.strftime = function(format) { 101 | const fields = { 102 | b: this.getAbbrevMonthName(), 103 | B: this.getFullMonthName(), 104 | c: this.toString(), 105 | d: this.getTwoDigitDate(), 106 | H: this.getTwoDigitHour(), 107 | I: this.getTwoDigitTwelveHour(), 108 | m: this.getTwoDigitMonth(), 109 | M: this.getTwoDigitMinute(), 110 | p: (this.getHours() >= 12) ? 'PM' : 'AM', 111 | S: this.getTwoDigitSecond(), 112 | w: '0' + this.getDay(), 113 | x: this.toLocaleDateString(), 114 | X: this.toLocaleTimeString(), 115 | y: ('' + this.getFullYear()).substr(2, 4), 116 | Y: '' + this.getFullYear(), 117 | '%': '%' 118 | }; 119 | let result = '', i = 0; 120 | while (i < format.length) { 121 | if (format.charAt(i) === '%') { 122 | result = result + fields[format.charAt(i + 1)]; 123 | ++i; 124 | } 125 | else { 126 | result = result + format.charAt(i); 127 | } 128 | ++i; 129 | } 130 | return result; 131 | }; 132 | 133 | // ---------------------------------------------------------------------------- 134 | // String object extensions 135 | // ---------------------------------------------------------------------------- 136 | String.prototype.strptime = function(format) { 137 | const split_format = format.split(/[.\-/]/); 138 | const date = this.split(/[.\-/]/); 139 | let i = 0; 140 | let day, month, year; 141 | while (i < split_format.length) { 142 | switch (split_format[i]) { 143 | case "%d": 144 | day = date[i]; 145 | break; 146 | case "%m": 147 | month = date[i] - 1; 148 | break; 149 | case "%Y": 150 | year = date[i]; 151 | break; 152 | case "%y": 153 | // A %y value in the range of [00, 68] is in the current 154 | // century, while [69, 99] is in the previous century, 155 | // according to the Open Group Specification. 156 | if (parseInt(date[i], 10) >= 69) { 157 | year = date[i]; 158 | } else { 159 | year = (new Date(Date.UTC(date[i], 0))).getUTCFullYear() + 100; 160 | } 161 | break; 162 | } 163 | ++i; 164 | } 165 | // Create Date object from UTC since the parsed value is supposed to be 166 | // in UTC, not local time. Also, the calendar uses UTC functions for 167 | // date extraction. 168 | return new Date(Date.UTC(year, month, day)); 169 | }; 170 | } 171 | -------------------------------------------------------------------------------- /django/django_project/static/admin/js/filters.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Persist changelist filters state (collapsed/expanded). 3 | */ 4 | 'use strict'; 5 | { 6 | // Init filters. 7 | let filters = JSON.parse(sessionStorage.getItem('django.admin.filtersState')); 8 | 9 | if (!filters) { 10 | filters = {}; 11 | } 12 | 13 | Object.entries(filters).forEach(([key, value]) => { 14 | const detailElement = document.querySelector(`[data-filter-title='${key}']`); 15 | 16 | // Check if the filter is present, it could be from other view. 17 | if (detailElement) { 18 | value ? detailElement.setAttribute('open', '') : detailElement.removeAttribute('open'); 19 | } 20 | }); 21 | 22 | // Save filter state when clicks. 23 | const details = document.querySelectorAll('details'); 24 | details.forEach(detail => { 25 | detail.addEventListener('toggle', event => { 26 | filters[`${event.target.dataset.filterTitle}`] = detail.open; 27 | sessionStorage.setItem('django.admin.filtersState', JSON.stringify(filters)); 28 | }); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /django/django_project/static/admin/js/jquery.init.js: -------------------------------------------------------------------------------- 1 | /*global jQuery:false*/ 2 | 'use strict'; 3 | /* Puts the included jQuery into our own namespace using noConflict and passing 4 | * it 'true'. This ensures that the included jQuery doesn't pollute the global 5 | * namespace (i.e. this preserves pre-existing values for both window.$ and 6 | * window.jQuery). 7 | */ 8 | window.django = {jQuery: jQuery.noConflict(true)}; 9 | -------------------------------------------------------------------------------- /django/django_project/static/admin/js/nav_sidebar.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | { 3 | const toggleNavSidebar = document.getElementById('toggle-nav-sidebar'); 4 | if (toggleNavSidebar !== null) { 5 | const navLinks = document.querySelectorAll('#nav-sidebar a'); 6 | function disableNavLinkTabbing() { 7 | for (const navLink of navLinks) { 8 | navLink.tabIndex = -1; 9 | } 10 | } 11 | function enableNavLinkTabbing() { 12 | for (const navLink of navLinks) { 13 | navLink.tabIndex = 0; 14 | } 15 | } 16 | function disableNavFilterTabbing() { 17 | document.getElementById('nav-filter').tabIndex = -1; 18 | } 19 | function enableNavFilterTabbing() { 20 | document.getElementById('nav-filter').tabIndex = 0; 21 | } 22 | 23 | const main = document.getElementById('main'); 24 | let navSidebarIsOpen = localStorage.getItem('django.admin.navSidebarIsOpen'); 25 | if (navSidebarIsOpen === null) { 26 | navSidebarIsOpen = 'true'; 27 | } 28 | if (navSidebarIsOpen === 'false') { 29 | disableNavLinkTabbing(); 30 | disableNavFilterTabbing(); 31 | } 32 | main.classList.toggle('shifted', navSidebarIsOpen === 'true'); 33 | 34 | toggleNavSidebar.addEventListener('click', function() { 35 | if (navSidebarIsOpen === 'true') { 36 | navSidebarIsOpen = 'false'; 37 | disableNavLinkTabbing(); 38 | disableNavFilterTabbing(); 39 | } else { 40 | navSidebarIsOpen = 'true'; 41 | enableNavLinkTabbing(); 42 | enableNavFilterTabbing(); 43 | } 44 | localStorage.setItem('django.admin.navSidebarIsOpen', navSidebarIsOpen); 45 | main.classList.toggle('shifted'); 46 | }); 47 | } 48 | 49 | function initSidebarQuickFilter() { 50 | const options = []; 51 | const navSidebar = document.getElementById('nav-sidebar'); 52 | if (!navSidebar) { 53 | return; 54 | } 55 | navSidebar.querySelectorAll('th[scope=row] a').forEach((container) => { 56 | options.push({title: container.innerHTML, node: container}); 57 | }); 58 | 59 | function checkValue(event) { 60 | let filterValue = event.target.value; 61 | if (filterValue) { 62 | filterValue = filterValue.toLowerCase(); 63 | } 64 | if (event.key === 'Escape') { 65 | filterValue = ''; 66 | event.target.value = ''; // clear input 67 | } 68 | let matches = false; 69 | for (const o of options) { 70 | let displayValue = ''; 71 | if (filterValue) { 72 | if (o.title.toLowerCase().indexOf(filterValue) === -1) { 73 | displayValue = 'none'; 74 | } else { 75 | matches = true; 76 | } 77 | } 78 | // show/hide parent
to respect formatting */
19 | }
20 |
21 | .main-container {
22 | padding-left: 30px;
23 | padding-right: 30px;
24 | }
25 |
26 | .btn:focus,
27 | .btn:focus:active {
28 | outline: none;
29 | }
30 |
31 | .sidebar {
32 | overflow: auto;
33 | font-family: verdana, sans-serif;
34 | font-size: 12px;
35 | font-weight: 200;
36 | background-color: #2e353d;
37 | position: fixed;
38 | top: 0px;
39 | width: 225px;
40 | height: 100%;
41 | color: #FFF;
42 | }
43 |
44 | .sidebar .brand {
45 | background-color: #23282e;
46 | display: block;
47 | text-align: center;
48 | padding: 25px 0;
49 | margin-top: 0;
50 | margin-bottom: 0;
51 | }
52 |
53 | .sidebar .brand a {
54 | color: #FFF;
55 | }
56 |
57 | .sidebar .brand a:hover,
58 | .sidebar .brand a:active,
59 | .sidebar .brand a:focus {
60 | text-decoration: none;
61 | }
62 |
63 | .sidebar .toggle-btn {
64 | display: none;
65 | }
66 |
67 | .sidebar .menu-list {
68 | width: inherit;
69 | }
70 |
71 | .sidebar .menu-list ul,
72 | .sidebar .menu-list li {
73 | background: #2e353d;
74 | list-style: none;
75 | padding: 0px;
76 | margin: 0px;
77 | line-height: 35px;
78 | cursor: pointer;
79 | }
80 |
81 | .sidebar .menu-list ul :not(collapsed) .arrow:before,
82 | .sidebar .menu-list li :not(collapsed) .arrow:before {
83 | font-family: FontAwesome;
84 | content: "\f078";
85 | display: inline-block;
86 | padding-left: 10px;
87 | padding-right: 10px;
88 | vertical-align: middle;
89 | float: right;
90 | }
91 |
92 | .sidebar .menu-list ul .active,
93 | .sidebar .menu-list li .active {
94 | border-left: 3px solid #d19b3d;
95 | background-color: #4f5b69;
96 | }
97 |
98 | .sidebar .menu-list ul .sub-menu li.active,
99 | .sidebar .menu-list li .sub-menu li.active {
100 | color: #d19b3d;
101 | }
102 |
103 | .sidebar .menu-list ul .sub-menu li.active a,
104 | .sidebar .menu-list li .sub-menu li.active a {
105 | color: #d19b3d;
106 | }
107 |
108 | .sidebar .menu-list ul .sub-menu li,
109 | .sidebar .menu-list li .sub-menu li {
110 | background-color: #181c20;
111 | border: none;
112 | border-bottom: 1px solid #23282e;
113 | margin-left: 0px;
114 | line-height: 1.4;
115 | padding-top: 10px;
116 | padding-bottom: 10px;
117 | padding-right: 10px;
118 | padding-left: 25px;
119 | }
120 |
121 | .sidebar .menu-list ul .sub-menu li:hover,
122 | .sidebar .menu-list li .sub-menu li:hover {
123 | background-color: #020203;
124 | }
125 |
126 |
127 | .sidebar .menu-list ul .sub-menu li a,
128 | .sidebar .menu-list li .sub-menu li a {
129 | display: block;
130 | }
131 |
132 | .sidebar .menu-list ul .sub-menu li a:before,
133 | .sidebar .menu-list li .sub-menu li a:before {
134 | font-family: FontAwesome;
135 | font-size: 14px;
136 | font-weight: bold;
137 | content: "\f105";
138 | display: inline;
139 | vertical-align: middle;
140 | padding-left: 0;
141 | padding-right: 7px;
142 | margin-left: -12px;
143 | }
144 |
145 | .sidebar .menu-list li {
146 | padding-left: 0px;
147 | border-left: 3px solid #2e353d;
148 | border-bottom: 1px solid #23282e;
149 | }
150 |
151 | .sidebar .menu-list li a {
152 | text-decoration: none;
153 | color: white;
154 | }
155 |
156 | .sidebar .menu-list li a i {
157 | padding-left: 10px;
158 | width: 20px;
159 | padding-right: 20px;
160 | }
161 |
162 | .sidebar .menu-list li:hover {
163 | border-left: 3px solid #d19b3d;
164 | background-color: #4f5b69;
165 | -webkit-transition: all 1s ease;
166 | -moz-transition: all 1s ease;
167 | -o-transition: all 1s ease;
168 | -ms-transition: all 1s ease;
169 | transition: all 1s ease;
170 | }
171 |
172 | .sidebar #menu-content {
173 | padding-bottom: 70px;
174 | }
175 |
176 | body {
177 | margin: 0px;
178 | padding: 0px;
179 | }
180 |
181 | .coredocs-section-title {
182 | margin-top: 20px;
183 | padding-bottom: 10px;
184 | border-bottom: 1px solid lightgrey;
185 | }
186 |
187 | .coredocs-link-title a,
188 | .coredocs-section-title a {
189 | display: none;
190 | }
191 |
192 | .coredocs-link-title a,
193 | .coredocs-section-title a {
194 | text-decoration: none;
195 | }
196 |
197 | .coredocs-link-title:hover a,
198 | .coredocs-section-title:hover a {
199 | display: inline;
200 | font-size: 20px;
201 | }
202 |
203 | .coredocs-section-title:last-child {
204 | margin-top: 0;
205 | }
206 |
207 |
208 | /* @group Language Switcher */
209 |
210 | .sidebar .menu-list.menu-list-bottom {
211 | margin-bottom: 0;
212 | position: fixed;
213 | width: inherit;
214 | bottom: 0;
215 | left: 0;
216 | right: 0;
217 | border-top: 1px solid #23282e;
218 | }
219 |
220 | .sidebar .menu-list-bottom li span {
221 | float: right;
222 | margin-right: 20px;
223 | color: #d19b3d;
224 | }
225 |
226 | /* @end Language Switcher */
227 |
228 |
229 | /* @group Docs Content */
230 |
231 | .docs-content .meta .label {
232 | vertical-align: middle;
233 | font-size: 14px;
234 | font-weight: normal;
235 | }
236 |
237 | .docs-content .meta code {
238 | vertical-align: middle;
239 | padding: .2em .6em .3em;
240 | font-size: 14px;
241 | }
242 |
243 | .docs-content .btn {
244 | font-size: inherit;
245 | }
246 |
247 | .code-samples pre {
248 | margin-top: 20px;
249 | }
250 |
251 | /* @end Docs Content */
252 |
253 |
254 | @media (max-width: 767px) {
255 | .main-container {
256 | padding-left: 15px;
257 | padding-right: 15px;
258 | }
259 |
260 | .sidebar {
261 | position: relative;
262 | width: 100%;
263 | margin-bottom: 10px;
264 | overflow: visible;
265 | }
266 |
267 | .sidebar .toggle-btn {
268 | display: block;
269 | cursor: pointer;
270 | position: absolute;
271 | right: 10px;
272 | top: 10px;
273 | z-index: 10 !important;
274 | padding: 3px;
275 | width: 40px;
276 | text-align: center;
277 | }
278 |
279 | .sidebar .menu-list.menu-list-bottom {
280 | position: static;
281 | }
282 |
283 | .sidebar .brand {
284 | margin-top: 0;
285 | margin-bottom: 0;
286 |
287 | text-align: left !important;
288 | font-size: 22px;
289 | padding: 0;
290 | padding-left: 20px;
291 | line-height: 50px !important;
292 | }
293 | }
294 |
295 | @media (min-width: 767px) {
296 | .sidebar .menu-list .menu-content {
297 | display: block;
298 | }
299 | #main {
300 | width:calc(100% - 225px);
301 | float: right;
302 | }
303 | }
304 |
305 | @media (min-width: 992px) {
306 | .modal-lg {
307 | width: 980px;
308 | }
309 | }
310 |
311 | .api-modal .modal-title .fa {
312 | color: #93c54b;
313 | }
314 |
315 | .api-modal .modal-body .request-awaiting {
316 | padding: 35px 10px;
317 | color: #7F8177;
318 | text-align: center;
319 | }
320 |
321 | .api-modal .modal-body .meta {
322 | margin-bottom: 20px;
323 | }
324 |
325 | .api-modal .modal-body .meta .label {
326 | vertical-align: middle;
327 | font-size: 14px;
328 | font-weight: normal;
329 | }
330 |
331 | .api-modal .modal-body .meta code {
332 | vertical-align: middle;
333 | padding: .2em .6em .3em;
334 | font-size: 14px;
335 | }
336 |
337 | .api-modal .modal-content .toggle-view {
338 | text-align: right;
339 | float: right;
340 | }
341 |
342 | .api-modal .modal-content .response .well {
343 | margin: 0;
344 | max-height: 550px;
345 | }
346 |
347 | .highlight {
348 | background-color: #f7f7f9
349 | }
350 |
351 | .checkbox label.control-label {
352 | font-weight: bold
353 | }
354 |
355 | @media (min-width: 768px) {
356 | .navbar-nav.navbar-right:last-child {
357 | margin-right: 0 !important;
358 | }
359 | }
360 |
--------------------------------------------------------------------------------
/django/django_project/static/rest_framework/docs/css/highlight.css:
--------------------------------------------------------------------------------
1 | /*
2 | This is the GitHub theme for highlight.js
3 |
4 | github.com style (c) Vasily Polovnyov
5 |
6 | */
7 |
8 | .hljs {
9 | display: block;
10 | overflow-x: auto;
11 | padding: 0.5em;
12 | color: #333;
13 | -webkit-text-size-adjust: none;
14 | }
15 |
16 | .hljs-comment,
17 | .diff .hljs-header,
18 | .hljs-javadoc {
19 | color: #998;
20 | font-style: italic;
21 | }
22 |
23 | .hljs-keyword,
24 | .css .rule .hljs-keyword,
25 | .hljs-winutils,
26 | .nginx .hljs-title,
27 | .hljs-subst,
28 | .hljs-request,
29 | .hljs-status {
30 | color: #333;
31 | font-weight: bold;
32 | }
33 |
34 | .hljs-number,
35 | .hljs-hexcolor,
36 | .ruby .hljs-constant {
37 | color: #008080;
38 | }
39 |
40 | .hljs-string,
41 | .hljs-tag .hljs-value,
42 | .hljs-phpdoc,
43 | .hljs-dartdoc,
44 | .tex .hljs-formula {
45 | color: #d14;
46 | }
47 |
48 | .hljs-title,
49 | .hljs-id,
50 | .scss .hljs-preprocessor {
51 | color: #900;
52 | font-weight: bold;
53 | }
54 |
55 | .hljs-list .hljs-keyword,
56 | .hljs-subst {
57 | font-weight: normal;
58 | }
59 |
60 | .hljs-class .hljs-title,
61 | .hljs-type,
62 | .vhdl .hljs-literal,
63 | .tex .hljs-command {
64 | color: #458;
65 | font-weight: bold;
66 | }
67 |
68 | .hljs-tag,
69 | .hljs-tag .hljs-title,
70 | .hljs-rule .hljs-property,
71 | .django .hljs-tag .hljs-keyword {
72 | color: #000080;
73 | font-weight: normal;
74 | }
75 |
76 | .hljs-attribute,
77 | .hljs-variable,
78 | .lisp .hljs-body,
79 | .hljs-name {
80 | color: #008080;
81 | }
82 |
83 | .hljs-regexp {
84 | color: #009926;
85 | }
86 |
87 | .hljs-symbol,
88 | .ruby .hljs-symbol .hljs-string,
89 | .lisp .hljs-keyword,
90 | .clojure .hljs-keyword,
91 | .scheme .hljs-keyword,
92 | .tex .hljs-special,
93 | .hljs-prompt {
94 | color: #990073;
95 | }
96 |
97 | .hljs-built_in {
98 | color: #0086b3;
99 | }
100 |
101 | .hljs-preprocessor,
102 | .hljs-pragma,
103 | .hljs-pi,
104 | .hljs-doctype,
105 | .hljs-shebang,
106 | .hljs-cdata {
107 | color: #999;
108 | font-weight: bold;
109 | }
110 |
111 | .hljs-deletion {
112 | background: #fdd;
113 | }
114 |
115 | .hljs-addition {
116 | background: #dfd;
117 | }
118 |
119 | .diff .hljs-change {
120 | background: #0086b3;
121 | }
122 |
123 | .hljs-chunk {
124 | color: #aaa;
125 | }
126 |
--------------------------------------------------------------------------------
/django/django_project/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}
--------------------------------------------------------------------------------
/django/django_project/static/rest_framework/docs/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/static/rest_framework/docs/img/favicon.ico
--------------------------------------------------------------------------------
/django/django_project/static/rest_framework/docs/img/grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/static/rest_framework/docs/img/grid.png
--------------------------------------------------------------------------------
/django/django_project/static/rest_framework/docs/js/jquery.json-view.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * jquery.json-view - jQuery collapsible JSON plugin
3 | * @version v1.0.0
4 | * @link http://github.com/bazh/jquery.json-view
5 | * @license MIT
6 | */
7 | !function(e){"use strict";var n=function(n){var a=e("",{"class":"collapser",on:{click:function(){var n=e(this);n.toggleClass("collapsed");var a=n.parent().children(".block"),p=a.children("ul");n.hasClass("collapsed")?(p.hide(),a.children(".dots, .comments").show()):(p.show(),a.children(".dots, .comments").hide())}}});return n&&a.addClass("collapsed"),a},a=function(a,p){var t=e.extend({},{nl2br:!0},p),r=function(e){return e.toString()?e.toString().replace(/&/g,"&").replace(/"/g,""").replace(//g,">"):""},s=function(n,a){return e("",{"class":a,html:r(n)})},l=function(a,p){switch(e.type(a)){case"object":p||(p=0);var c=e("",{"class":"block"}),d=Object.keys(a).length;if(!d)return c.append(s("{","b")).append(" ").append(s("}","b"));c.append(s("{","b"));var i=e("
",{"class":"obj collapsible level"+p});return e.each(a,function(a,t){d--;var r=e("").append(s('"',"q")).append(a).append(s('"',"q")).append(": ").append(l(t,p+1));-1===["object","array"].indexOf(e.type(t))||e.isEmptyObject(t)||r.prepend(n()),d>0&&r.append(","),i.append(r)}),c.append(i),c.append(s("...","dots")),c.append(s("}","b")),c.append(1===Object.keys(a).length?s("// 1 item","comments"):s("// "+Object.keys(a).length+" items","comments")),c;case"array":p||(p=0);var d=a.length,c=e("",{"class":"block"});if(!d)return c.append(s("[","b")).append(" ").append(s("]","b"));c.append(s("[","b"));var i=e("
",{"class":"obj collapsible level"+p});return e.each(a,function(a,t){d--;var r=e("").append(l(t,p+1));-1===["object","array"].indexOf(e.type(t))||e.isEmptyObject(t)||r.prepend(n()),d>0&&r.append(","),i.append(r)}),c.append(i),c.append(s("...","dots")),c.append(s("]","b")),c.append(1===a.length?s("// 1 item","comments"):s("// "+a.length+" items","comments")),c;case"string":if(a=r(a),/^(http|https|file):\/\/[^\s]+$/i.test(a))return e("").append(s('"',"q")).append(e("",{href:a,text:a})).append(s('"',"q"));if(t.nl2br){var o=/\n/g;o.test(a)&&(a=(a+"").replace(o,"
"))}var u=e("",{"class":"str"}).html(a);return e("").append(s('"',"q")).append(u).append(s('"',"q"));case"number":return s(a.toString(),"num");case"undefined":return s("undefined","undef");case"null":return s("null","null");case"boolean":return s(a?"true":"false","bool")}};return l(a)};return e.fn.jsonView=function(n,p){var t=e(this);if(p=e.extend({},{nl2br:!0},p),"string"==typeof n)try{n=JSON.parse(n)}catch(r){}return t.append(e("",{"class":"json-view"}).append(a(n,p))),t}}(jQuery);
--------------------------------------------------------------------------------
/django/django_project/static/rest_framework/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/static/rest_framework/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/django/django_project/static/rest_framework/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/static/rest_framework/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/django/django_project/static/rest_framework/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/static/rest_framework/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/django/django_project/static/rest_framework/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/static/rest_framework/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/django/django_project/static/rest_framework/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/static/rest_framework/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/django/django_project/static/rest_framework/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/static/rest_framework/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/django/django_project/static/rest_framework/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/static/rest_framework/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/django/django_project/static/rest_framework/img/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/static/rest_framework/img/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/django/django_project/static/rest_framework/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/static/rest_framework/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/django/django_project/static/rest_framework/img/grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/static/rest_framework/img/grid.png
--------------------------------------------------------------------------------
/django/django_project/static/rest_framework/js/ajax-form.js:
--------------------------------------------------------------------------------
1 | function replaceDocument(docString) {
2 | var doc = document.open("text/html");
3 |
4 | doc.write(docString);
5 | doc.close();
6 | }
7 |
8 | function doAjaxSubmit(e) {
9 | var form = $(this);
10 | var btn = $(this.clk);
11 | var method = (
12 | btn.data('method') ||
13 | form.data('method') ||
14 | form.attr('method') || 'GET'
15 | ).toUpperCase();
16 |
17 | if (method === 'GET') {
18 | // GET requests can always use standard form submits.
19 | return;
20 | }
21 |
22 | var contentType =
23 | form.find('input[data-override="content-type"]').val() ||
24 | form.find('select[data-override="content-type"] option:selected').text();
25 |
26 | if (method === 'POST' && !contentType) {
27 | // POST requests can use standard form submits, unless we have
28 | // overridden the content type.
29 | return;
30 | }
31 |
32 | // At this point we need to make an AJAX form submission.
33 | e.preventDefault();
34 |
35 | var url = form.attr('action');
36 | var data;
37 |
38 | if (contentType) {
39 | data = form.find('[data-override="content"]').val() || ''
40 |
41 | if (contentType === 'multipart/form-data') {
42 | // We need to add a boundary parameter to the header
43 | // We assume the first valid-looking boundary line in the body is correct
44 | // regex is from RFC 2046 appendix A
45 | var boundaryCharNoSpace = "0-9A-Z'()+_,-./:=?";
46 | var boundaryChar = boundaryCharNoSpace + ' ';
47 | var re = new RegExp('^--([' + boundaryChar + ']{0,69}[' + boundaryCharNoSpace + '])[\\s]*?$', 'im');
48 | var boundary = data.match(re);
49 | if (boundary !== null) {
50 | contentType += '; boundary="' + boundary[1] + '"';
51 | }
52 | // Fix textarea.value EOL normalisation (multipart/form-data should use CR+NL, not NL)
53 | data = data.replace(/\n/g, '\r\n');
54 | }
55 | } else {
56 | contentType = form.attr('enctype') || form.attr('encoding')
57 |
58 | if (contentType === 'multipart/form-data') {
59 | if (!window.FormData) {
60 | alert('Your browser does not support AJAX multipart form submissions');
61 | return;
62 | }
63 |
64 | // Use the FormData API and allow the content type to be set automatically,
65 | // so it includes the boundary string.
66 | // See https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects
67 | contentType = false;
68 | data = new FormData(form[0]);
69 | } else {
70 | contentType = 'application/x-www-form-urlencoded; charset=UTF-8'
71 | data = form.serialize();
72 | }
73 | }
74 |
75 | var ret = $.ajax({
76 | url: url,
77 | method: method,
78 | data: data,
79 | contentType: contentType,
80 | processData: false,
81 | headers: {
82 | 'Accept': 'text/html; q=1.0, */*'
83 | },
84 | });
85 |
86 | ret.always(function(data, textStatus, jqXHR) {
87 | if (textStatus != 'success') {
88 | jqXHR = data;
89 | }
90 |
91 | var responseContentType = jqXHR.getResponseHeader("content-type") || "";
92 |
93 | if (responseContentType.toLowerCase().indexOf('text/html') === 0) {
94 | replaceDocument(jqXHR.responseText);
95 |
96 | try {
97 | // Modify the location and scroll to top, as if after page load.
98 | history.replaceState({}, '', url);
99 | scroll(0, 0);
100 | } catch (err) {
101 | // History API not supported, so redirect.
102 | window.location = url;
103 | }
104 | } else {
105 | // Not HTML content. We can't open this directly, so redirect.
106 | window.location = url;
107 | }
108 | });
109 |
110 | return ret;
111 | }
112 |
113 | function captureSubmittingElement(e) {
114 | var target = e.target;
115 | var form = this;
116 |
117 | form.clk = target;
118 | }
119 |
120 | $.fn.ajaxForm = function() {
121 | var options = {}
122 |
123 | return this
124 | .unbind('submit.form-plugin click.form-plugin')
125 | .bind('submit.form-plugin', options, doAjaxSubmit)
126 | .bind('click.form-plugin', options, captureSubmittingElement);
127 | };
128 |
--------------------------------------------------------------------------------
/django/django_project/static/rest_framework/js/csrf.js:
--------------------------------------------------------------------------------
1 | function getCookie(name) {
2 | var cookieValue = null;
3 |
4 | if (document.cookie && document.cookie != '') {
5 | var cookies = document.cookie.split(';');
6 |
7 | for (var i = 0; i < cookies.length; i++) {
8 | var cookie = jQuery.trim(cookies[i]);
9 |
10 | // Does this cookie string begin with the name we want?
11 | if (cookie.substring(0, name.length + 1) == (name + '=')) {
12 | cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
13 | break;
14 | }
15 | }
16 | }
17 |
18 | return cookieValue;
19 | }
20 |
21 | function csrfSafeMethod(method) {
22 | // these HTTP methods do not require CSRF protection
23 | return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
24 | }
25 |
26 | function sameOrigin(url) {
27 | // test that a given url is a same-origin URL
28 | // url could be relative or scheme relative or absolute
29 | var host = document.location.host; // host + port
30 | var protocol = document.location.protocol;
31 | var sr_origin = '//' + host;
32 | var origin = protocol + sr_origin;
33 |
34 | // Allow absolute or scheme relative URLs to same origin
35 | return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
36 | (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
37 | // or any other URL that isn't scheme relative or absolute i.e relative.
38 | !(/^(\/\/|http:|https:).*/.test(url));
39 | }
40 |
41 | var csrftoken = window.drf.csrfToken;
42 |
43 | $.ajaxSetup({
44 | beforeSend: function(xhr, settings) {
45 | if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
46 | // Send the token to same-origin, relative URLs only.
47 | // Send the token only if the method warrants CSRF protection
48 | // Using the CSRFToken value acquired earlier
49 | xhr.setRequestHeader(window.drf.csrfHeaderName, csrftoken);
50 | }
51 | }
52 | });
53 |
--------------------------------------------------------------------------------
/django/django_project/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 |
--------------------------------------------------------------------------------
/django/django_project/students/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/students/__init__.py
--------------------------------------------------------------------------------
/django/django_project/students/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/django/django_project/students/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class StudentsConfig(AppConfig):
5 | default_auto_field = 'django.db.models.BigAutoField'
6 | name = 'students'
7 |
--------------------------------------------------------------------------------
/django/django_project/students/management/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/students/management/__init__.py
--------------------------------------------------------------------------------
/django/django_project/students/management/commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/students/management/commands/__init__.py
--------------------------------------------------------------------------------
/django/django_project/students/management/commands/my_command.py:
--------------------------------------------------------------------------------
1 | from django.core.management.base import BaseCommand
2 | from PIL import Image
3 | import pika
4 | import time
5 |
6 | class Command(BaseCommand):
7 | def handle(self, *args, **options):
8 | count_try=0
9 | conn=False
10 | while not conn:
11 | count_try+=1
12 | print(f'I`m DJANGO WORKER. Попытка присоединится №{count_try}')
13 | conn, channel=self.initial()
14 | if not conn:
15 | time.sleep(2)
16 | print(' [*] I`m DJANGO WORKER and i`m Waiting for messages. To exit press CTRL+C')
17 | channel.start_consuming()
18 |
19 | def initial(self):
20 | try:
21 | hostname = 'rmq'
22 | port = 5672
23 | credentials = pika.PlainCredentials(username='admin', password='admin')
24 | parameters = pika.ConnectionParameters(host=hostname, port=port, credentials=credentials)
25 | connection = pika.BlockingConnection(parameters=parameters)
26 | # Создать канал
27 | channel = connection.channel()
28 | channel.queue_declare(queue='to_resize')
29 | channel.queue_declare(queue='from_resize')
30 | try:
31 | channel.basic_consume(queue='from_resize',
32 | auto_ack=True,
33 | on_message_callback=self.callback)
34 | except Exception as error:
35 | print(error)
36 | return True, channel
37 | except:
38 | return False, None
39 |
40 | @staticmethod
41 | def callback(ch, method, properties, body):
42 | try:
43 | data = body.split(b'separator')
44 | image = Image.frombytes("RGB", (int(data[1]), int(data[2])), data[0])
45 | file_name = data[3].decode('UTF-8')
46 | image.save('media/photo/' + file_name)
47 | except Exception as error:
48 | print(error)
49 |
--------------------------------------------------------------------------------
/django/django_project/students/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.1.5 on 2023-01-20 20:02
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | initial = True
9 |
10 | dependencies = [
11 | ]
12 |
13 | operations = [
14 | migrations.CreateModel(
15 | name='Student',
16 | fields=[
17 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18 | ('name', models.CharField(max_length=240, verbose_name='Name')),
19 | ('email', models.EmailField(max_length=254)),
20 | ('document', models.CharField(max_length=20, verbose_name='Document')),
21 | ('phone', models.CharField(max_length=20)),
22 | ('registrationDate', models.DateField(auto_now_add=True, verbose_name='Registration Date')),
23 | ('photo', models.CharField(max_length=512, verbose_name='URL')),
24 | ],
25 | ),
26 | ]
27 |
--------------------------------------------------------------------------------
/django/django_project/students/migrations/0002_students.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.1.5 on 2023-01-16 05:56
2 |
3 | from django.db import migrations
4 |
5 | def create_data(apps, schema_editor):
6 | Student = apps.get_model('students', 'Student')
7 | Student(name="Joe Silver", email="joe@email.com", document="22342342", phone="00000000", photo='media/photo/nophoto.png').save()
8 | Student(name="John Smith", email="john@email.com", document="11111111", phone="11111111", photo='media/photo/nophoto.png').save()
9 | Student(name="Alex Smth", email="alex@email.com", document="22222222", phone="22222222", photo='media/photo/nophoto.png').save()
10 | Student(name="Kira Night", email="kira@email.com", document="33333333", phone="33333333", photo='media/photo/nophoto.png').save()
11 | Student(name="Amanda Lex", email="amanda@email.com", document="44444444", phone="44444444", photo='media/photo/nophoto.png').save()
12 | Student(name="Oni Musha", email="oni@email.com", document="44444444", phone="44444444", photo='media/photo/nophoto.png').save()
13 |
14 | class Migration(migrations.Migration):
15 |
16 | dependencies = [
17 | ('students', '0001_initial'),
18 | ]
19 |
20 | operations = [
21 | migrations.RunPython(create_data),
22 | ]
23 |
--------------------------------------------------------------------------------
/django/django_project/students/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/django/django_project/students/migrations/__init__.py
--------------------------------------------------------------------------------
/django/django_project/students/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | class Student(models.Model):
4 | name = models.CharField("Name", max_length=240)
5 | email = models.EmailField()
6 | document = models.CharField("Document", max_length=20)
7 | phone = models.CharField(max_length=20)
8 | registrationDate = models.DateField("Registration Date", auto_now_add=True)
9 | photo = models.CharField("URL", max_length=512)
10 |
11 |
12 | def __str__(self):
13 | return self.name
--------------------------------------------------------------------------------
/django/django_project/students/serializers.py:
--------------------------------------------------------------------------------
1 | from rest_framework import serializers
2 | from .models import Student
3 |
4 | class StudentSerializer(serializers.ModelSerializer):
5 |
6 | class Meta:
7 | model = Student
8 | fields = ('pk', 'name', 'email', 'document', 'phone', 'registrationDate','photo')
--------------------------------------------------------------------------------
/django/django_project/students/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/django/django_project/students/views.py:
--------------------------------------------------------------------------------
1 | import uuid
2 | import pika
3 | from rest_framework.response import Response
4 | from rest_framework.decorators import api_view
5 | from rest_framework import status
6 | from .serializers import *
7 |
8 |
9 | # Create your views here.
10 |
11 | @api_view(['GET', 'POST'])
12 | def students_list(request):
13 | if request.method == 'GET':
14 | data = Student.objects.all()
15 |
16 | serializer = StudentSerializer(data, context={'request': request}, many=True)
17 |
18 | return Response(serializer.data)
19 |
20 | elif request.method == 'POST':
21 | print('post')
22 | serializer = StudentSerializer(data=request.data)
23 | if serializer.is_valid():
24 | serializer.save()
25 | if 'file' in request.FILES:
26 | save_image_to_media(serializer, request)
27 | return Response(status=status.HTTP_201_CREATED)
28 | return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
29 |
30 |
31 | @api_view(['PUT', 'DELETE'])
32 | def students_detail(request, pk):
33 | try:
34 | student = Student.objects.get(pk=pk)
35 | except Student.DoesNotExist:
36 | return Response(status=status.HTTP_404_NOT_FOUND)
37 |
38 | if request.method == 'PUT':
39 | serializer = StudentSerializer(student, data=request.data, context={'request': request})
40 | if serializer.is_valid():
41 | serializer.save()
42 | if 'file' in request.FILES:
43 | save_image_to_media(serializer, request)
44 | return Response(status=status.HTTP_204_NO_CONTENT)
45 | return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
46 |
47 | elif request.method == 'DELETE':
48 | student.delete()
49 | return Response(status=status.HTTP_204_NO_CONTENT)
50 |
51 | def send_to_rabbit(data):
52 | file = data.FILES['file']
53 | file_name = bytes('separator' + str(uuid.uuid4()) + '.' + file.name[file.name.rfind(".") + 1:], 'utf-8')
54 | img = file.read() + file_name
55 | hostname = '192.168.56.101'
56 | port = 5672
57 | credentials = pika.PlainCredentials(username='admin', password='admin')
58 | parameters = pika.ConnectionParameters(host=hostname, port=port, credentials=credentials)
59 | connection = pika.BlockingConnection(parameters=parameters)
60 | channel = connection.channel()
61 | channel.queue_declare(queue='to_resize')
62 | channel.basic_publish(exchange='',
63 | routing_key='to_resize',
64 | body=img)
65 | connection.close()
66 | return file_name.decode('utf-8')
67 |
68 | def save_image_to_media(serializer, request):
69 | print('save_image_to_media')
70 | file_name = send_to_rabbit(request).split('separator')[1]
71 | file_path = 'media\\photo\\' + file_name
72 | serializer.validated_data['photo'] = file_path
73 | if serializer.is_valid():
74 | serializer.save()
--------------------------------------------------------------------------------
/django/django_project/worker.py:
--------------------------------------------------------------------------------
1 | import pika
2 | from PIL import Image
3 | import io
4 | import time
5 |
6 |
7 | def initial():
8 | try:
9 | hostname = 'rmq'
10 | port = 5672
11 | credentials = pika.PlainCredentials(username='admin', password='admin')
12 | parameters = pika.ConnectionParameters(host=hostname, port=port, credentials=credentials)
13 | connection = pika.BlockingConnection(parameters=parameters)
14 | # Создать канал
15 | channel = connection.channel()
16 | # На всякий случай создаём очереди
17 | channel.queue_declare(queue='to_resize')
18 | channel.queue_declare(queue='from_resize')
19 | channel.basic_consume(queue='to_resize',
20 | auto_ack=True,
21 | on_message_callback=callback)
22 |
23 | return True, channel
24 | except:
25 | return False, None
26 |
27 | def callback(ch, method, properties, body):
28 | try:
29 | data = body.split(b'separator')
30 | file_name = b'separator' + data[1]
31 | fixed_height = 300
32 | image = Image.open(io.BytesIO(data[0]))
33 | height_percent = (fixed_height / float(image.size[1]))
34 | width_size = int((float(image.size[0]) * float(height_percent)))
35 | new = image.resize((width_size, fixed_height))
36 | img_width = bytes(f'separator{new.width}', 'utf-8')
37 | img_height = bytes(f'separator{new.height}', 'utf-8')
38 | tobyte = new.tobytes() + img_width + img_height + file_name
39 | return_resize_image(tobyte)
40 | except Exception as error:
41 | print(error)
42 |
43 |
44 | def return_resize_image(data):
45 | channel.basic_publish(exchange='',
46 | routing_key='from_resize',
47 | body=data)
48 |
49 |
50 | if __name__ == '__main__':
51 | count_try=0
52 | conn=False
53 | while not conn:
54 | count_try+=1
55 | print(f'I`m WORKER. Попытка присоединится №{count_try}')
56 | conn, channel=initial()
57 | if not conn:
58 | time.sleep(2)
59 | print(' [*] I`m WORKER and i`m Waiting for messages. To exit press CTRL+C')
60 | channel.start_consuming()
61 |
62 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.8'
2 | # Поднимаем пять сервисов: django, node, rabbitMQ, postgres, nginx
3 | services:
4 | django:
5 | #говорим что build будет из dockerfile который располагается ./django/django_project/
6 | build: ./django/django_project/
7 | # имя контейнера
8 | container_name: djangoapp
9 | # перезапускать контейнер при завершении выполнения работы или при аварийном завершении
10 | restart: always
11 | # проброс портов внутрь контейнера, 8000 порт на хост машине будет проброшен внутрь контейнера на такой же 8000 порт
12 | expose:
13 | - 8000
14 | # команда при старте контейнера
15 | command: >
16 | sh -c "nohup python worker.py & nohup python manage.py my_command & python manage.py runserver 0.0.0.0:8000"
17 | depends_on:
18 | - postgres
19 | - rmq
20 | # Для статики мы подключаем два volume (чтобы при перезапуске наши данные не пропадали)), создадим их ниже.
21 | volumes:
22 | - ./django/django_project:/usr/src/app/
23 | - django_static_volume:/usr/src/app/static
24 | - django_media_volume:/usr/src/app/media
25 | # подключаем к сети myNetwork (в целом не обязательно, но до кучи чтоб было)
26 | networks:
27 | - myNetwork
28 | env_file:
29 | - ./.env
30 |
31 | node:
32 | # Аналогично, build из ./reactapp/dockerfile
33 | build: ./reactapp
34 | # имя контейнера
35 | container_name: reactapp
36 | # рестарт
37 | restart: always
38 | # порты
39 | ports:
40 | - 3000:3000
41 | # команда при запуске
42 | volumes:
43 | - ./reactapp/public/:/usr/src/app/public/
44 | - ./reactapp/src/:/usr/src/app/src/
45 | command: npm start
46 | # Зависимость. нет смысла ноде, если некому отдать ей данные. поэтому сначала стартуем сервис django, а за ней node
47 | depends_on:
48 | - django
49 | # Сеть та же, все контейнеры должны крутиться в однйо сети чтобы видеть друг друга.
50 | networks:
51 | - myNetwork
52 |
53 | rmq:
54 | # на этот раз мы не билдим контейнер а используем полностью готовый из репозитория
55 | image: rabbitmq:3.10-management
56 | restart: always
57 | container_name: rmq
58 | networks:
59 | - myNetwork
60 | # Переменные окружения для настройки.
61 | environment:
62 | - RABBITMQ_DEFAULT_USER=admin
63 | - RABBITMQ_DEFAULT_PASS=admin
64 | # volume для хранения данных rmq, можно и без него, но тогда при перезапуске каждый раз будет создаваться новый и они будут потихоньку накапливаться
65 | volumes:
66 | - rabbitmq_data_volume:/var/lib/rabbitmq/
67 | # проброс портов, 15672 для менеджмента, 5671-5672 для работы
68 | ports:
69 | - 1234:15672
70 | - 5671-5672:5671-5672
71 |
72 | # Сервис нашей БД
73 | postgres:
74 | # Так же разворачиваем с готового контейнера
75 | image: postgres:15-alpine
76 | container_name: postgresdb
77 | # Чтобы наши данные не пропадали при перезапуске подключсим volume
78 | volumes:
79 | - postgres_volume:/var/lib/postgresql/data/
80 | # Переменные окружения. их надо будет передавать в django.
81 | environment:
82 | - POSTGRES_USER=admin
83 | - POSTGRES_PASSWORD=strong_password
84 | - POSTGRES_DB=django_db
85 | # Сеть
86 | networks:
87 | - myNetwork
88 |
89 | nginx:
90 | build: ./nginx
91 | container_name: nginx
92 | networks:
93 | - myNetwork
94 | ports:
95 | - 1337:80
96 | depends_on:
97 | - django
98 | volumes:
99 | - django_static_volume:/home/src/app/static
100 | - django_media_volume:/home/src/app/media
101 |
102 | # создаём два volume для статики
103 | volumes:
104 | postgres_volume:
105 | django_static_volume:
106 | django_media_volume:
107 | rabbitmq_data_volume:
108 |
109 | # создаём сеть.
110 | networks:
111 | myNetwork:
112 | driver: bridge
--------------------------------------------------------------------------------
/nginx/Dockerfile:
--------------------------------------------------------------------------------
1 | # Собираемся из готового образа nginx:1.23-alpine
2 | FROM nginx:1.23-alpine
3 | # Удаляем дефолтный конфиг
4 | RUN rm /etc/nginx/conf.d/default.conf
5 | # Подкидываем наш
6 | COPY ./nginx.conf /etc/nginx/conf.d/
--------------------------------------------------------------------------------
/nginx/nginx.conf:
--------------------------------------------------------------------------------
1 | upstream django_app {
2 | # Список бэкэнд серверов для проксирования
3 | server django:8000;
4 | }
5 | server {
6 | listen 80;
7 | # Параметры проксирования
8 | location / {
9 | # Если будет открыта корневая страница
10 | # все запросу пойдут к одному из серверов
11 | # в upstream django_proj
12 | proxy_pass http://django_app;
13 | # Устанавливаем заголовки
14 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
15 | proxy_set_header Host $host;
16 | # Отключаем перенаправление
17 | proxy_redirect off;
18 | }
19 | # Статика и медиа
20 | location /static/ {
21 | alias /home/src/app/static/;
22 | }
23 | location /media/ {
24 | alias /home/src/app/media/;
25 | }
26 | }
--------------------------------------------------------------------------------
/reactapp/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
13 |
14 | The page will reload when you make changes.\
15 | You may also see any lint errors in the console.
16 |
17 | ### `npm test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `npm run eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!**
35 |
36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
39 |
40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
48 | ### Code Splitting
49 |
50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
51 |
52 | ### Analyzing the Bundle Size
53 |
54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
55 |
56 | ### Making a Progressive Web App
57 |
58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
59 |
60 | ### Advanced Configuration
61 |
62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
63 |
64 | ### Deployment
65 |
66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
67 |
68 | ### `npm run build` fails to minify
69 |
70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
71 |
--------------------------------------------------------------------------------
/reactapp/dockerfile:
--------------------------------------------------------------------------------
1 | # так же берём готовый контейнер с node на основе alpine
2 | FROM node:18-alpine as build
3 | # Задаем рабочий каталог
4 | WORKDIR /usr/src/app
5 | # Копируем туда наши json файлы
6 | ADD *.json ./
7 | # Устанавливаем все пакеты и зависимости указанные в json
8 | RUN npm install
9 |
10 | # Копируем каталоги public и src.
11 | # можно воспользоваться командой COPY . . но если вы синхронизировали node_modules, то будете ждать пока залётся
12 | # этот каталог целиком. да и потом могут возникнуть проблемы.
13 | ADD ./public ./public
14 | ADD ./src ./src
--------------------------------------------------------------------------------
/reactapp/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reactapp",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.5",
7 | "@testing-library/react": "^13.4.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "axios": "^1.2.3",
10 | "bootstrap": "^5.2.3",
11 | "react": "^18.2.0",
12 | "react-dom": "^18.2.0",
13 | "react-scripts": "5.0.1",
14 | "reactstrap": "^9.1.5",
15 | "web-vitals": "^2.1.4"
16 | },
17 | "scripts": {
18 | "start": "react-scripts start",
19 | "build": "react-scripts build",
20 | "test": "react-scripts test",
21 | "eject": "react-scripts eject"
22 | },
23 | "eslintConfig": {
24 | "extends": [
25 | "react-app",
26 | "react-app/jest"
27 | ]
28 | },
29 | "browserslist": {
30 | "production": [
31 | ">0.2%",
32 | "not dead",
33 | "not op_mini all"
34 | ],
35 | "development": [
36 | "last 1 chrome version",
37 | "last 1 firefox version",
38 | "last 1 safari version"
39 | ]
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/reactapp/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/reactapp/public/favicon.ico
--------------------------------------------------------------------------------
/reactapp/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/reactapp/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/reactapp/public/logo192.png
--------------------------------------------------------------------------------
/reactapp/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eldalex/ProjectStudent/34855f85c15e4f032195793c1620c8e76f09d193/reactapp/public/logo512.png
--------------------------------------------------------------------------------
/reactapp/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/reactapp/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/reactapp/src/components/app/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/reactapp/src/components/app/App.js:
--------------------------------------------------------------------------------
1 | import './App.css';
2 | import {Fragment} from "react";
3 | import Header from "../appHeader/Header";
4 | import Home from "../appHome/Home";
5 |
6 | function App() {
7 | return (
8 |
9 |
10 |
11 |
12 | );
13 | }
14 |
15 | export default App;
16 |
--------------------------------------------------------------------------------
/reactapp/src/components/app/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render( );
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/reactapp/src/components/appHeader/Header.js:
--------------------------------------------------------------------------------
1 | const Header = () => {
2 | return (
3 |
4 |
11 |
12 | App for project on React + Django
13 | )
14 | }
15 |
16 | export default Header;
--------------------------------------------------------------------------------
/reactapp/src/components/appHome/Home.js:
--------------------------------------------------------------------------------
1 | import {Container, Row, Col} from "reactstrap";
2 | import ListStudents from "../appListStudents/ListStudents";
3 | import axios from "axios";
4 | import {useEffect, useState} from "react";
5 | import ModalStudent from "../appModalStudent/ModalStudent";
6 |
7 | import {API_URL} from "../../index";
8 |
9 |
10 | const Home = () => {
11 | const [students, setStudents] = useState([])
12 |
13 | useEffect(()=>{
14 | getStudents()
15 | },[])
16 |
17 | const getStudents = (data)=>{
18 | axios.get(API_URL).then(data => setStudents(data.data))
19 | }
20 |
21 | const resetState = () => {
22 | getStudents();
23 | };
24 |
25 | return (
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
38 |
39 |
40 |
41 | )
42 | }
43 |
44 | export default Home;
--------------------------------------------------------------------------------
/reactapp/src/components/appListStudents/ListStudents.js:
--------------------------------------------------------------------------------
1 | import {Table} from "reactstrap";
2 | import ModalStudent from "../appModalStudent/ModalStudent";
3 | import AppRemoveStudent from "../appRemoveStudent/appRemoveStudent";
4 | import ModalPhoto from "../appPhotoModal/ModalPhoto";
5 |
6 | const ListStudents = (props) => {
7 | // debugger
8 | const {students} = props
9 | return (
10 |
11 |
12 |
13 | Name
14 | Email
15 | Document
16 | Phone
17 | Registration
18 | Photo
19 |
20 |
21 |
22 |
23 | {!students || students.length <= 0 ? (
24 |
25 |
26 | Пока ничего нет
27 |
28 |
29 | ) : students.map(student => (
30 |
31 | {student.name}
32 | {student.email}
33 | {student.document}
34 | {student.phone}
35 | {student.registrationDate}
36 |
39 |
40 |
46 |
47 |
51 |
52 |
53 | )
54 | )}
55 |
56 |
57 | )
58 | }
59 | export default ListStudents
--------------------------------------------------------------------------------
/reactapp/src/components/appModalStudent/ModalStudent.js:
--------------------------------------------------------------------------------
1 | import {Fragment, useState} from "react";
2 | import {Button, Modal, ModalHeader, ModalBody} from "reactstrap";
3 | import StudentForm from "../appStudentForm/StudentForm";
4 |
5 | const ModalStudent = (props) => {
6 | const [visible, setVisible] = useState(false)
7 | var button = ;
8 |
9 | const toggle = () => {
10 | setVisible(!visible)
11 | }
12 |
13 | if (props.create) {
14 | button = (
15 |
22 | )
23 | }
24 | return (
25 |
26 | {button}
27 |
28 | {props.create ? "Добавить студента" : "Редактировать студента"}
30 |
31 |
37 |
38 |
39 |
40 | )
41 | }
42 | export default ModalStudent;
--------------------------------------------------------------------------------
/reactapp/src/components/appPhotoModal/ModalPhoto.js:
--------------------------------------------------------------------------------
1 | import {Fragment, useState} from "react";
2 | import {API_STATIC_MEDIA} from "../../index";
3 | import {Button, Modal, ModalBody, ModalFooter, ModalHeader} from "reactstrap";
4 |
5 | const ModalPhoto = (props) => {
6 | const [visible, setVisible] = useState(false)
7 | const toggle = () => {
8 | setVisible(!visible)
9 | }
10 | return (
11 | <>
12 |
13 |
14 | Фото
15 | 
16 |
17 |
18 | >
19 | )
20 | }
21 |
22 | export default ModalPhoto;
--------------------------------------------------------------------------------
/reactapp/src/components/appRemoveStudent/appRemoveStudent.js:
--------------------------------------------------------------------------------
1 | import {Fragment, useState} from "react";
2 | import {Button, Modal, ModalHeader, ModalFooter} from "reactstrap";
3 | import axios from "axios";
4 | import {API_URL} from "../../index";
5 |
6 | const AppRemoveStudent = (props) => {
7 | const [visible, setVisible] = useState(false)
8 | const toggle = () => {
9 | setVisible(!visible)
10 | }
11 | const deleteStudent = () => {
12 | axios.delete(API_URL + props.pk).then(() => {
13 | props.resetState()
14 | toggle();
15 | });
16 | }
17 | return (
18 |
19 |
22 |
23 | Вы уверены?
24 |
25 |
30 |
31 |
32 |
33 |
34 | )
35 | }
36 | export default AppRemoveStudent;
--------------------------------------------------------------------------------
/reactapp/src/components/appStudentForm/StudentForm.js:
--------------------------------------------------------------------------------
1 | import {useEffect, useState} from "react";
2 | import {Button, Form, FormGroup, Input, Label} from "reactstrap";
3 | import axios from "axios";
4 | import {API_URL} from "../../index";
5 |
6 | const StudentForm = (props) => {
7 | const [student, setStudent] = useState({})
8 |
9 | const onChange = (e) => {
10 | const newState = student
11 | if (e.target.name === "file") {
12 | newState[e.target.name] = e.target.files[0]
13 | } else newState[e.target.name] = e.target.value
14 | setStudent(newState)
15 | }
16 |
17 | useEffect(() => {
18 | if (!props.newStudent) {
19 | setStudent(student => props.student)
20 | }
21 | // eslint-disable-next-line
22 | }, [props.student])
23 |
24 |
25 | const defaultIfEmpty = value => {
26 | return value === "" ? "" : value;
27 | }
28 |
29 | const submitDataEdit = async (e) => {
30 | e.preventDefault();
31 | // eslint-disable-next-line
32 | const result = await axios.put(API_URL + student.pk, student, {headers: {'Content-Type': 'multipart/form-data'}})
33 | .then(() => {
34 | props.resetState()
35 | props.toggle()
36 | })
37 | }
38 | const submitDataAdd = async (e) => {
39 | e.preventDefault();
40 | const data = {
41 | name: student['name'],
42 | email: student['email'],
43 | document: student['document'],
44 | phone: student['phone'],
45 | photo: "/",
46 | file: student['file']
47 | }
48 | // eslint-disable-next-line
49 | const result = await axios.post(API_URL, data, {headers: {'Content-Type': 'multipart/form-data'}})
50 | .then(() => {
51 | props.resetState()
52 | props.toggle()
53 | })
54 | }
55 | return (
56 |
106 | )
107 | }
108 |
109 | export default StudentForm;
--------------------------------------------------------------------------------
/reactapp/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/reactapp/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import './index.css';
4 | import App from './components/app/App';
5 | import reportWebVitals from './reportWebVitals';
6 | import 'bootstrap/dist/css/bootstrap.min.css'
7 |
8 | // export const API_URL = "http://127.0.0.1:8000/api/students/"
9 | export const API_URL = "http://192.168.56.101:1337/api/students/"
10 | export const API_STATIC_MEDIA = "http://192.168.56.101:1337/"
11 |
12 | const root = ReactDOM.createRoot(document.getElementById('root'));
13 | root.render(
14 |
15 |
16 |
17 | );
18 |
19 | reportWebVitals();
20 |
--------------------------------------------------------------------------------
/reactapp/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/reactapp/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/reactapp/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------