Compare commits
10 Commits
8cbbd15829
...
1a52626440
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1a52626440 | ||
![]() |
d8b1af4cda | ||
![]() |
aacbe7e013 | ||
![]() |
9d8a1bdf6b | ||
![]() |
a8c156e25d | ||
![]() |
5975923cbb | ||
![]() |
3de98e01b3 | ||
![]() |
e8c8cb393f | ||
![]() |
65042dcebc | ||
![]() |
04c204fc57 |
13
README.md
13
README.md
@ -1,5 +1,14 @@
|
||||
## Тестовое задание rosmorport
|
||||
|
||||
* [Задание](target.md) (таким, как оно было выдано, авторская орфография и пунктуация).
|
||||
* Пакеты virtualenv: [dev (home win)](requare_dev_w_home.txt) / [prod (ubuntu)](requare_dev_prod.txt).
|
||||
Тестовое задание на соискание вакансии `Frontend-разработчик` в ФГУП РосМорФлот.
|
||||
|
||||
* [Тестовое задание](target.md) (таким, как оно было выдано, авторская орфография и пунктуация).
|
||||
* [Мой ответ](my_anwer.txt) (спустя два дня)
|
||||
* [Ответ РосМорФлот](feedback.txt) (спустя четыре дня, в ответ на просьбу обратной связи... судя по логам
|
||||
заходил один раз сразу по получения решения).
|
||||
|
||||
Мое мнение о тестовом задании: в задании нет никаких рекомендаций, ограничений и запретов на
|
||||
какие-либо _выбранные технологии_... Зачем задании столько требований по Backend, если вакансия
|
||||
`Frontend-разработчик` -- загадка. И, похоже, дополнительно нужен навык чтения мыслей на расстоянии.
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
# ТЕСТОВОЕ ЗАДАНИЕ РОСМОРПОРТ
|
||||
# == Конфикурационный файл pets-clone--nginx.conf ==
|
||||
# == Конфикурационный файл pet-clones--nginx.conf ==
|
||||
|
||||
# Описываем апстрим-потоки которые должен подключить Nginx
|
||||
# Для каждого сайта надо настроить свой поток, со своим уникальным именем.
|
||||
@ -7,79 +7,64 @@
|
||||
|
||||
upstream pet-clone {
|
||||
# расположение файла Unix-сокет для взаимодействие с uwsgi
|
||||
server unix:///home/web/clone.cocorico.ru/socket/clone_pets.sock;
|
||||
# также можно использовать веб-сокет (порт) для взаимодействие с uwsgi. Но это медленнее
|
||||
# server 127.0.0.1:8021; # для взаимодействия с uwsgi через веб-порт 8021
|
||||
server unix:///home/web/pet-clones.cocorico.ru/socket/clone_pets.sock;
|
||||
}
|
||||
|
||||
# конфигурируем сервер
|
||||
server {
|
||||
server_name clone.cocorico.ru; # доменное имя сайта
|
||||
server_name pet-clones.cocorico.ru; # доменное имя сайта
|
||||
listen 443 ssl; # managed by Certbot
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/clone.cocorico.ru/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/clone.cocorico.ru/privkey.pem; # managed by Certbot
|
||||
ssl_certificate /etc/letsencrypt/live/pet-clones.cocorico.ru/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/pet-clones.cocorico.ru/privkey.pem; # managed by Certbot
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||
|
||||
ssl_protocols TLSv1 TLSv1.1;
|
||||
# ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
|
||||
resolver 217.16.16.15 217.16.20.15 217.16.22.15 217.16.16.31 77.88.8.8 valid=10s;
|
||||
ssl_session_cache builtin:1000 shared:SSL:25m;
|
||||
keepalive_requests 200;
|
||||
keepalive_timeout 75s;
|
||||
|
||||
charset utf-8; # кодировка по умолчанию
|
||||
|
||||
access_log /home/web/clone.cocorico.ru/logs/clone-pets-access.log; # логи с доступом
|
||||
error_log /home/web/clone.cocorico.ru/logs/clone-pets-error.log; # логи с ошибками
|
||||
access_log /home/web/pet-clones.cocorico.ru/logs/clone-pets-access.log; # логи с доступом
|
||||
error_log /home/web/pet-clones.cocorico.ru/logs/clone-pets-error.log; # логи с ошибками
|
||||
|
||||
client_max_body_size 100M; # максимальный объем файла для загрузки на сайт (max upload size)
|
||||
|
||||
error_page 404 /404.html;
|
||||
error_page 500 /500.html;
|
||||
|
||||
location /media { alias /home/web/clone.cocorico.ru/public/media; } # Расположение media-файлов Django
|
||||
location /static { alias /home/web/clone.cocorico.ru/public/static; } # Расположение static-файлов Django
|
||||
location /media { alias /home/web/pet-clones.cocorico.ru/public/media; } # Расположение media-файлов Django
|
||||
location /static { alias /home/web/pet-clones.cocorico.ru/public/static; } # Расположение static-файлов Django
|
||||
|
||||
location /robots.txt { root /home/web/clone.cocorico.ru/public; } # Расположение robots.txt
|
||||
location /favicon.ico { root /home/web/clone.cocorico.ru/public/static/img; } # Расположение favicon.ico
|
||||
location /favicon.png { root /home/web/clone.cocorico.ru/public/static/img; } # Расположение favicon
|
||||
location /robots.txt { root /home/web/pet-clones.cocorico.ru/public; } # Расположение robots.txt
|
||||
location /favicon.ico { root /home/web/pet-clones.cocorico.ru/public/static/img; } # Расположение favicon.ico
|
||||
location /favicon.png { root /home/web/pet-clones.cocorico.ru/public/static/img; } # Расположение favicon
|
||||
location = /404.html {
|
||||
root /home/web/clone.cocorico.ru/rosmorport_tsts/templates-django/404.html;
|
||||
root /home/web/pet-clones.cocorico.ru/rosmorport_tsts/templates-django/404.html;
|
||||
internal;
|
||||
}
|
||||
location = /500.html {
|
||||
root /home/web/clone.cocorico.ru/rosmorport_tsts/templates-django/500.html;
|
||||
root /home/web/pet-clones.cocorico.ru/rosmorport_tsts/templates-django/500.html;
|
||||
internal;
|
||||
}
|
||||
location ~ \.(xml|html|htm|txt|svg)$ {
|
||||
root /home/web/clone.cocorico.ru/public; # Расположение статичных *.xml, *.html и *.txt
|
||||
root /home/web/pet-clones.cocorico.ru/public; # Расположение статичных *.xml, *.html и *.txt
|
||||
}
|
||||
|
||||
location / {
|
||||
# uwsgi_pass pet-clone; # upstream обрабатывающий обращений
|
||||
# # uwsgi_pass 127.0.0.1:8001; # upstream обрабатывающий обращений
|
||||
# include uwsgi_params; # конфигурационный файл uwsgi;
|
||||
# uwsgi_read_timeout 1800; # вдруг некоторые запросы очень долго обрабатываются?
|
||||
# uwsgi_send_timeout 200; # на всякий случай время записи в сокет тоже побольше...
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
uwsgi_pass pet-clone; # upstream обрабатывающий обращений
|
||||
include uwsgi_params; # конфигурационный файл uwsgi;
|
||||
uwsgi_read_timeout 1800; # вдруг некоторые запросы очень долго обрабатываются?
|
||||
uwsgi_send_timeout 200; # на всякий случай время записи в сокет тоже побольше...
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
if ($host = clone.cocorico.ru) {
|
||||
if ($host = pet-clones.cocorico.ru) {
|
||||
return 301 https://$host$request_uri;
|
||||
} # managed by Certbot
|
||||
|
||||
|
||||
server_name pet-clones.cocorico.ru;
|
||||
listen 80;
|
||||
server_name clone.cocorico.ru;
|
||||
return 404; # managed by Certbot
|
||||
|
||||
|
||||
}
|
@ -1,64 +1,66 @@
|
||||
# === Конфикурационный файл uwsgi pets-clone--uwsgi.ini ===
|
||||
# === Конфигурационный файл uwsgi pets-clone--uwsgi.ini ===
|
||||
# ░▒▓████▓▒░▒▓█████▓▒░▒▓█▓▒░ ▒▓██████▓▒░ ВНИМАНИЕ ДЛЯ ТЕХ КТО ДЕПЛОИТ ИЗ ПОД MICROSOFT WINDOWS!!
|
||||
# ░▒▓█▓▒▒▓█▓▒▒▓█▓▒▒▓█▓▒▒▓█▓▒░ ▒▓█▓▒░ ОКОНЧАНИЯ СТРОК ДОЛЖНЫ БЫТЬ В ФОРМАТЕ UNIX (LF) а не CR+LR (как в Win)
|
||||
#-░▒▓█▓▒░----▒▓█████▓▒-▒▓█▓▒░---▒▓████▓▒░----------------------------------------------------------------
|
||||
# ░▒▓█▓▒▒▓█▓▒▒▓█▓▒▒▓█▓▒▒▓█▓▒░ ▒▓█▓▒░ Заметка: CR - carriage return (\r, возврат каретки, ВК)
|
||||
# ░▒▓████▓▒░▒▓█▓▒▒▓█▓▒▒▓█████▓▒▒▓█▓▒░ LF - line feed (\n, перевод строки, ПС)
|
||||
|
||||
[uwsgi]
|
||||
|
||||
# НАСТРОЙКИ ДЛЯ DJANGO
|
||||
# Корневая папка проекта (полный путь)
|
||||
chdir: /home/web/clone.cocorico.ru/rosmorport_tsts
|
||||
# Django wsgi файл rosmorport_tsts/wsgi.py записываем так:
|
||||
module: rosmorport_tsts.wsgi
|
||||
chdir = /home/web/pet-clones.cocorico.ru/rosmorport_tsts
|
||||
module = rosmorport_tsts.wsgi
|
||||
# полный путь к виртуальному окружению
|
||||
home: /home/web/clone.cocorico.ru/env
|
||||
home = /home/web/pet-clones.cocorico.ru/env
|
||||
# полный путь к файлу сокета
|
||||
# route: ^/websocket 127.0.0.1:8021
|
||||
socket: unix:///home/web/clone.cocorico.ru/socket/clone_pets.sock
|
||||
# Исходящие сообщения в лог
|
||||
daemonize: /home/web/clone.cocorico.ru/logs/clone-pets-uwsgi.log
|
||||
socket = /home/web/pet-clones.cocorico.ru/socket/clone_pets.sock
|
||||
|
||||
# ЗАГАДОЧНЫЕ НАСТРОЙКИ, ПО ИДЕЕ ОНИ НУЖНЫ, НО И БЕЗ НИХ ВСЁ РАБОТАЕТ
|
||||
# расположение wsgi.py
|
||||
# wsgi-file: /home/web/clone.cocorico.ru/rosmorport_tsts/rosmorport_tsts/wsgi.py
|
||||
wsgi-file = /home/web/pet-clones.cocorico.ru/rosmorport_tsts/rosmorport_tsts/wsgi.py
|
||||
# расположение виртуального окружения (как оно работает если этот параметр не указан, не ясно)
|
||||
virtualenv: /home/web/clone.cocorico.ru/env
|
||||
virtualenv = /home/web/pet-clones.cocorico.ru/env
|
||||
# имя файла при изменении которого происходит авторестарт приложения
|
||||
# (когда этого параметра нет, то гичего не авторестартится, но с ним все рестартится.
|
||||
# Cтоит изменить любой Python-исходник проекта, как изменения сразу вступают в силу.
|
||||
touch-reload: /home/web/clone.cocorico.ru/logs/touchreload.txt
|
||||
py-autoreload: 5
|
||||
touch-reload = /home/web/pet-clones.cocorico.ru/logs/touchreload.txt
|
||||
py-autoreload = 5
|
||||
|
||||
#
|
||||
# НАСТРОЙКИ ОБЩИЕ
|
||||
# быть master-процессом
|
||||
master: true
|
||||
master = true
|
||||
# максимальное количество процессов
|
||||
processes: 1
|
||||
processes = 2
|
||||
# если uWSGI устнаовлен как сервис через apt-get то нужно установить еще плугин:
|
||||
# sudo apt-get install uwsgi-plugin-python3
|
||||
# и добавить в этот конфиг: plugin: python3
|
||||
plugin: python3
|
||||
# sudo apt-get install uwsgi-plugin-python
|
||||
# и добавить в этот конфиг: plugin = python
|
||||
plugin = python3
|
||||
# права доступа к файлу сокета. По умолчанию должно хватать 664. Но каких-то прав не хватает, поэтому 666.
|
||||
chmod-socket: 777
|
||||
chmod-socket = 666
|
||||
# очищать окружение от служебных файлов uwsgi по завершению
|
||||
vacuum: true
|
||||
vacuum = true
|
||||
# количество секунд после которых подвисший процес будет перезапущен
|
||||
# Так как некоторе скрипты требуют изрядно времени (особенно полная переиндексация) то ставим значение побольще
|
||||
harakiri: 2600
|
||||
harakiri = 2600
|
||||
# В общем случае, при некотых значениях harakiri логах uWSGI может вываливаться предупреждение:
|
||||
# WARNING: you have enabled harakiri without post buffering. Slow upload could be rejected on post-unbuffered webservers
|
||||
# можно оставить harakiri закоментированным, но нам нужно 900 и на него не ругается. Ругается на 30.
|
||||
|
||||
# разрешаем многопоточность
|
||||
enable-threads: true
|
||||
vacuum: true
|
||||
thunder-lock: true
|
||||
max-requests: 500
|
||||
enable-threads = true
|
||||
vacuum = true
|
||||
thunder-lock = true
|
||||
max-requests = 500
|
||||
|
||||
# пользователь и группа пользователей от имени которых запускать uWSGI
|
||||
# указываем www-data: к этой группе относится nginх, и ранее мы включили в эту группу нашего [user]
|
||||
# uid : nginx
|
||||
# gid : nginx
|
||||
# uid : www-data
|
||||
# gid : www-data
|
||||
uid: web
|
||||
gid: web
|
||||
# указываем www-data: к этой группе относится nginz, и ранее мы включили в эту группу нашего [user]
|
||||
# uid = nginx
|
||||
# gid = nginx
|
||||
# uid = root
|
||||
# gid = root
|
||||
uid = web
|
||||
gid = web
|
||||
|
||||
|
||||
print: ---------------- Запущен uWSGI ----------------
|
||||
print = ---------------- Запущен uWSGI для pet-clones.cocorico.ru ---------------
|
3
feedback.txt
Normal file
3
feedback.txt
Normal file
@ -0,0 +1,3 @@
|
||||
Мы ознакомились с результатом выполнения тестового задания и отметили творческий подход,
|
||||
однако выбранные технологии и качество программной реализации не соответствуют нашим
|
||||
стандартам.
|
13
my_anwer.txt
Normal file
13
my_anwer.txt
Normal file
@ -0,0 +1,13 @@
|
||||
Добрый день ....
|
||||
|
||||
Не успел сделать прокрутку в tbody ... закопался в деплоем (попался на RFLF гWSGI-конфига) :(
|
||||
|
||||
В целом, в задании, мне кажется, иногда путают высоту и ширину (в блоке шириной 75px даже слово
|
||||
из 8 букв не уместить, тем более меню которые надо выравнивать странно). Так что делал как посчитал
|
||||
разумным. Прошу извинить.
|
||||
|
||||
Проект: https://pet-clones.cocorico.ru/
|
||||
Гит: https://git.cube2.ru/erjemin/2024-test-rosmorport
|
||||
|
||||
login: admin
|
||||
pwd: 1234
|
49
rosmorport_tsts/rosmorport_tsts/_my_secret__sample.py
Normal file
49
rosmorport_tsts/rosmorport_tsts/_my_secret__sample.py
Normal file
@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ВСЕ СЕКРЕТНЫЕ НАСТРОЙКИ ПРОЕКТА ДЛЯ РАЗВЕРТЫВАНИЯ НА ПРОДАКШЕНЕ (турция)
|
||||
"""
|
||||
В этот файл вынесены все секретные настройки, чтобы не светить их в settings.py
|
||||
Например, при размещении в публичный репозиториях.
|
||||
"""
|
||||
|
||||
MY_DEBUG = False
|
||||
|
||||
# Хосты на которых может работать приложение
|
||||
MY_ALLOWED_HOSTS = [
|
||||
'127.0.0.1',
|
||||
'localhost',
|
||||
'pet-clones.cocorico.ru', # prod
|
||||
]
|
||||
|
||||
|
||||
# Ключ Django
|
||||
MY_SECRET_KEY = 'django-insecure-sample---secrets-must-be-kept-secret-SSxk75QmR6w37zu5okbthaNDCMQtPQ7kn7wYQcGPKENq'
|
||||
|
||||
# Настройки для сообщений об ошибках когда все упало и т.п.
|
||||
MY_ADMINS = (
|
||||
('S.Erjemin', 'erjemin@gmail.com'),
|
||||
)
|
||||
|
||||
#########################################
|
||||
# настройки для почтового сервера
|
||||
MY_EMAIL = 'admin@you.site'
|
||||
MY_EMAIL_FROM = 'admin@you.site'
|
||||
MY_EMAIL_HOST = 'smtp.mail.ru' # host
|
||||
MY_EMAIL_HOST_USER = 'admin@you.site' # login
|
||||
MY_EMAIL_HOST_PASSWORD = 'secrets-must-be-kept-secret' # password
|
||||
MY_EMAIL_PORT = 2525 # port
|
||||
MY_EMAIL_USE_TLS = True
|
||||
|
||||
# Настройки подключения к БД MySQL
|
||||
MY_DATABASE_HOST = 'localhost' # db-host
|
||||
MY_DATABASE_NAME = 'db-name' # db-name
|
||||
MY_DATABASE_PORT = '3306'
|
||||
MY_DATABASE_USER = 'db-user'
|
||||
MY_DATABASE_PASSWORD = 'secrets-must-be-kept-secret'
|
||||
|
||||
MY_TOUCH_RELOAD = '/home/web/you.site/logs/touchreload.txt'
|
||||
|
||||
MY_MEDIA_ROOT = '/home/web/you.site/public/media'
|
||||
|
||||
MY_STATIC_ROOT = '/home/web/you.site/public/static/'
|
||||
|
||||
MY_SITEMAP_ROOT = '/home/web/you.site/public'
|
@ -11,19 +11,19 @@ class TbPetsClones(models.Model):
|
||||
"""Модель для хранения клонов питомцев"""
|
||||
class PetType(models.IntegerChoices):
|
||||
"""Тип питомца"""
|
||||
DOG = 1, "Собачка"
|
||||
CAT = 2, "Кошечка"
|
||||
BIR = 3, "Птичка"
|
||||
FIS = 6, "Рыбка"
|
||||
ROD = 4, "Грызун"
|
||||
REP = 5, "Рептилия"
|
||||
OTH = 0, "Неизвестная зверушка"
|
||||
DOG = 1, "🐕 Собачка"
|
||||
CAT = 2, "🐈 Кошечка"
|
||||
BIR = 3, "🦜 Птичка"
|
||||
ROD = 4, "🐟 Рыбка"
|
||||
REP = 5, "🦎 Рептилия"
|
||||
FIS = 6, "🐜 Насекомое"
|
||||
OTH = 0, "👾 Неизвестная зверушка"
|
||||
|
||||
class PetSex(models.IntegerChoices):
|
||||
"""Пол питомца"""
|
||||
MA = 1, "Мужской"
|
||||
FE = 2, "Женский"
|
||||
UN = 0, "Неизвестно"
|
||||
MA = 1, "♂ Мужской"
|
||||
FE = 2, "♀ Женский"
|
||||
UN = 0, "⸰ Неизвестно"
|
||||
|
||||
iPetType = models.SmallIntegerField(
|
||||
default=PetType.OTH,
|
||||
|
@ -23,7 +23,7 @@ if socket.gethostname() == 'erjemin-home':
|
||||
elif socket.gethostname() in ['m1.N1', 'm1.local', ]:
|
||||
# домашний комп (MacOS)
|
||||
from rosmorport_tsts.my_secret_dev_home_mac import *
|
||||
elif socket.gethostname() in ['orangepi5', 'srv05962228.ultasrv.net']:
|
||||
else:
|
||||
# продакшн (боевой) сервер
|
||||
from rosmorport_tsts.my_secret_prod import *
|
||||
|
||||
|
@ -24,8 +24,16 @@
|
||||
{% for PET in PETS %}<tr>
|
||||
<td>{{ PET.szPetSerNum }}</td>
|
||||
<td>{% if PET.szPetName %}{{ PET.szPetName }}{% else %}—{% endif %}</td>
|
||||
<td>{{ PET.iPetType }}</td>
|
||||
<td>{{ PET.iPetSex }}</td>
|
||||
<td>{% if PET.iPetType == 0 %}<i class="fa-solid fa-ghost"></i>{% elif PET.iPetType == 1 %}
|
||||
<i class="fa-solid fa-dog"></i>{% elif PET.iPetType == 2 %}
|
||||
<i class="fa-solid fa-cat"></i>{% elif PET.iPetType == 3 %}
|
||||
<i class="fa-solid fa-dove"></i>{% elif PET.iPetType == 4 %}
|
||||
<i class="fa-solid fa-fish"></i>{% elif PET.iPetType == 5 %}
|
||||
<i class="fa-solid fa-frog"></i>{% elif PET.iPetType == 6 %}
|
||||
<i class="fa-solid fa-locust"></i>{% endif %}</td>
|
||||
<td>{% if PET.iPetSex == 0 %}<i class="fa-solid fa-genderless"></i>{% elif PET.iPetSex == 1 %}
|
||||
<i class="fa-solid fa-mars"></i>{% elif PET.iPetSex == 2 %}
|
||||
<i class="fa-solid fa-venus"></i>{% endif %}</td>
|
||||
<td>{% if PET.bPetIsReg %}<i class="fa-solid fa-circle-check"></i>{% endif %}</td>
|
||||
<td>{% if not PET.bPetIsAlive %}<i class="fa-solid fa-skull-crossbones"></i>{% endif %}</td>
|
||||
<td>{% if PET.szPetOwner %}{{ PET.szPetOwner }}{% else %}—{% endif %}</td>
|
||||
|
@ -39,8 +39,16 @@
|
||||
{% for PET in PETS %}<tr class="t{{ PET.iPetType }} s{{ PET.iPetSex }}">
|
||||
<td>{{ PET.szPetSerNum }}</td>
|
||||
<td>{% if PET.szPetName %}{{ PET.szPetName }}{% else %}—{% endif %}</td>
|
||||
<td>{{ PET.iPetType }}</td>
|
||||
<td>{{ PET.iPetSex }}</td>
|
||||
<td>{% if PET.iPetType == 0 %}<i class="fa-solid fa-ghost"></i>{% elif PET.iPetType == 1 %}
|
||||
<i class="fa-solid fa-dog"></i>{% elif PET.iPetType == 2 %}
|
||||
<i class="fa-solid fa-cat"></i>{% elif PET.iPetType == 3 %}
|
||||
<i class="fa-solid fa-dove"></i>{% elif PET.iPetType == 4 %}
|
||||
<i class="fa-solid fa-fish"></i>{% elif PET.iPetType == 5 %}
|
||||
<i class="fa-solid fa-frog"></i>{% elif PET.iPetType == 6 %}
|
||||
<i class="fa-solid fa-locust"></i>{% endif %}</td>
|
||||
<td>{% if PET.iPetSex == 0 %}<i class="fa-solid fa-genderless"></i>{% elif PET.iPetSex == 1 %}
|
||||
<i class="fa-solid fa-mars"></i>{% elif PET.iPetSex == 2 %}
|
||||
<i class="fa-solid fa-venus"></i>{% endif %}</td>
|
||||
<td>{% if PET.bPetIsReg %}<i class="fa-solid fa-circle-check"></i>{% endif %}</td>
|
||||
<td>{% if not PET.bPetIsAlive %}<i class="fa-solid fa-skull-crossbones"></i>{% endif %}</td>
|
||||
<td>{% if PET.szPetOwner %}{{ PET.szPetOwner }}{% else %}—{% endif %}</td>
|
||||
@ -82,27 +90,12 @@
|
||||
|
||||
{% block JS_1 %}<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('#type').change(function(){
|
||||
let t = $(this).find("option:selected").attr('value');
|
||||
let s = $('#sex').find("option:selected").attr('value');
|
||||
if (t === 'tA')
|
||||
$('tbody > tr').show();
|
||||
else {
|
||||
$('tbody > tr').hide();
|
||||
$('tbody > tr.' + t).show();
|
||||
}
|
||||
if(s !== 'sA') $('tbody > tr:not(.' + s +')').hide();
|
||||
});
|
||||
$('#sex').change(function(){
|
||||
let s = $(this).find("option:selected").attr('value');
|
||||
$('#type,#sex').change(function(){
|
||||
let t = $('#type').find("option:selected").attr('value');
|
||||
if (s === 'sA')
|
||||
let s = $('#sex').find("option:selected").attr('value');
|
||||
$('tbody > tr').show();
|
||||
else {
|
||||
$('tbody > tr').hide();
|
||||
$('tbody > tr.' + s).show();
|
||||
}
|
||||
if(s !== 'tA') $('tbody > tr:not(.' + t + ')').hide();
|
||||
if(t !== 'tA') $('tbody > tr:not(.'+t+')').hide();
|
||||
if(s !== 'sA') $('tbody > tr:not(.'+s+')').hide();
|
||||
});
|
||||
});
|
||||
</script>{% endblock %}
|
@ -1,14 +1,14 @@
|
||||
Необходимо реализовать интерфейс веб-приложения (сайта), который состоит из 3 областей:
|
||||
- Информационная область.
|
||||
Представлена в виде полосы в верхней части экрана, ширина должна быть фиксированная 75 пикс. В этой области слева располагается название веб-приложения («Тестовое задание Frontend»), размер шрифта 11pt, вертикальное выравнивание по центру, горизонтальное по левому краю. Слева кнопка «Вход», выравнивание аналогичное названию но по правому краю. При нажатии на неё появляется всплывающее окно с формой ввода логина и пароля (обязательные поля) и кнопкой «Войти». При попытке войти должен производиться запрос к серверу, на сервере происходит авторизация средствами Django (или иного фреймворка, пользователь должен существовать в БД фреймворка). В случае успешной авторизации страница приложения перезагружается, иначе появляется сообщение что авторизоваться не удалось с пояснением причины (пользователя не существует, пароль не верный, ошибка подключения, иная ошибка).
|
||||
В случае если пользователь авторизован кнопка «Войти» заменяется на «Выйти», рядом отображается имя пользователя (слева от кнопки), при её нажатии будет отправлен запрос на сервер завершающий сессию пользователя, страница должна быть перезагружена.
|
||||
- Область меню.
|
||||
Представлена в виде полосы шириной 75 пикс., цвет отличается от информационной области, размер шрифта 11pt. Выравнивание объектов внутри горизонтально по левому краю, вертикально по центру, внутри расположены пункты меню: «Ввод данных», «Отчёты», «Информация». При наведении курсора на пункт меню вертикально вниз выпадают соответствующие подпункты: «Ввод данных» - «Форма ввода»; «Отчёты» - «Отчёт 1», «Отчёт 2»; «Информация» - «О приложении». Если пользователь не авторизован, то в первых двух пунктах меню вместо подпунктов будет отображено «Необходима авторизация».
|
||||
- Рабочая область.
|
||||
Остальное свободное место на странице. Информационная область и область меню должны оставаться видимыми на странице даже если содержимое рабочей области больше отведённого под неё места.
|
||||
Необходимо реализовать интерфейс веб-приложения (сайта), который состоит из 3 областей:
|
||||
- Информационная область.
|
||||
Представлена в виде полосы в верхней части экрана, ширина должна быть фиксированная 75 пикс. В этой области слева располагается название веб-приложения («Тестовое задание Frontend»), размер шрифта 11pt, вертикальное выравнивание по центру, горизонтальное по левому краю. Слева кнопка «Вход», выравнивание аналогичное названию но по правому краю. При нажатии на неё появляется всплывающее окно с формой ввода логина и пароля (обязательные поля) и кнопкой «Войти». При попытке войти должен производиться запрос к серверу, на сервере происходит авторизация средствами Django (или иного фреймворка, пользователь должен существовать в БД фреймворка). В случае успешной авторизации страница приложения перезагружается, иначе появляется сообщение что авторизоваться не удалось с пояснением причины (пользователя не существует, пароль не верный, ошибка подключения, иная ошибка).
|
||||
В случае если пользователь авторизован кнопка «Войти» заменяется на «Выйти», рядом отображается имя пользователя (слева от кнопки), при её нажатии будет отправлен запрос на сервер завершающий сессию пользователя, страница должна быть перезагружена.
|
||||
- Область меню.
|
||||
Представлена в виде полосы шириной 75 пикс., цвет отличается от информационной области, размер шрифта 11pt. Выравнивание объектов внутри горизонтально по левому краю, вертикально по центру, внутри расположены пункты меню: «Ввод данных», «Отчёты», «Информация». При наведении курсора на пункт меню вертикально вниз выпадают соответствующие подпункты: «Ввод данных» - «Форма ввода»; «Отчёты» - «Отчёт 1», «Отчёт 2»; «Информация» - «О приложении». Если пользователь не авторизован, то в первых двух пунктах меню вместо подпунктов будет отображено «Необходима авторизация».
|
||||
- Рабочая область.
|
||||
Остальное свободное место на странице. Информационная область и область меню должны оставаться видимыми на странице даже если содержимое рабочей области больше отведённого под неё места.
|
||||
|
||||
При нажатии на подпункты меню должен отправляться запрос на сервер, а сервер возвращать содержимое (вёрстку) в зависимости от выбранного пункта, этим содержимым будет заменено содержимое рабочей области.
|
||||
При нажатии на подпункт «Форма ввода» в рабочей области должна появиться форма ввода документа на усмотрение тестируемого, но которая должна содержать как минимум 7 полей, среди них поля с «галочками» (checkbox) и выпадающие списки. Должна существовать возможность сохранить все введённые данные в базу данных. Должны присутствовать обязательные и необязательные поля, в интерфейсе их необходимо пометить соответствующим образом.
|
||||
При нажатии на подпункт «Отчёт 1» необходимо вывести таблицу, в которой будет представлены все внесённые в базу записи через интерфейс «Форма ввода». У таблицы должна присутствовать «шапка», которая должна отображаться всегда вне зависимости от объёма данных в отчёте (оставаться на виду при прокрутке отчёта вниз). Под таблицей необходимо предусмотреть строку справочной информации, в которой будет отображено количество записей в отчёте и время выполнения запроса к базе данных и обработки данных (не от клиента к серверу и обратно).
|
||||
При нажатии на подпункт «Отчёт 2» будет отображён аналогичный «Отчёту 1» отчёт, но с возможностью фильтрации по полям (1+) на выбор тестируемого. Техническая реализация фильтра – на усмотрение тестируемого (обработка на клиенте, запрос к серверу с параметрами, или иное).
|
||||
При нажатии на подпункт «О приложении» будет отображено всплывающее окно с содержимым на выбор тестируемого.
|
||||
При нажатии на подпункты меню должен отправляться запрос на сервер, а сервер возвращать содержимое (вёрстку) в зависимости от выбранного пункта, этим содержимым будет заменено содержимое рабочей области.
|
||||
При нажатии на подпункт «Форма ввода» в рабочей области должна появиться форма ввода документа на усмотрение тестируемого, но которая должна содержать как минимум 7 полей, среди них поля с «галочками» (checkbox) и выпадающие списки. Должна существовать возможность сохранить все введённые данные в базу данных. Должны присутствовать обязательные и необязательные поля, в интерфейсе их необходимо пометить соответствующим образом.
|
||||
При нажатии на подпункт «Отчёт 1» необходимо вывести таблицу, в которой будет представлены все внесённые в базу записи через интерфейс «Форма ввода». У таблицы должна присутствовать «шапка», которая должна отображаться всегда вне зависимости от объёма данных в отчёте (оставаться на виду при прокрутке отчёта вниз). Под таблицей необходимо предусмотреть строку справочной информации, в которой будет отображено количество записей в отчёте и время выполнения запроса к базе данных и обработки данных (не от клиента к серверу и обратно).
|
||||
При нажатии на подпункт «Отчёт 2» будет отображён аналогичный «Отчёту 1» отчёт, но с возможностью фильтрации по полям (1+) на выбор тестируемого. Техническая реализация фильтра – на усмотрение тестируемого (обработка на клиенте, запрос к серверу с параметрами, или иное).
|
||||
При нажатии на подпункт «О приложении» будет отображено всплывающее окно с содержимым на выбор тестируемого.
|
Loading…
Reference in New Issue
Block a user