Compare commits

...

10 Commits

Author SHA1 Message Date
e-serg
1a52626440 feedback на решение задания 2024-04-24 18:09:49 +03:00
e-serg
d8b1af4cda Note: CRLF in uWSGI.ini 2024-04-18 11:09:10 +03:00
e-serg
aacbe7e013 пример заполнения секретного файла 2024-04-15 19:34:17 +03:00
e-serg
9d8a1bdf6b config nginx (w ssl) 2024-04-15 19:27:18 +03:00
e-serg
a8c156e25d . corrected for prod 2024-04-15 19:17:58 +03:00
e-serg
5975923cbb config uwsgi (tested) 2024-04-15 19:16:50 +03:00
e-serg
3de98e01b3 config nginx (w/o ssl) 2024-04-15 19:16:22 +03:00
e-serg
e8c8cb393f .cp1251->utf8 2024-04-15 17:41:27 +03:00
e-serg
65042dcebc .красота 2024-04-15 14:09:08 +03:00
e-serg
04c204fc57 ERR FIX: наложение фильтров 2 2024-04-15 13:34:00 +03:00
11 changed files with 183 additions and 121 deletions

View File

@ -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-разработчик` -- загадка. И, похоже, дополнительно нужен навык чтения мыслей на расстоянии.

View File

@ -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
}

View File

@ -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
View File

@ -0,0 +1,3 @@
Мы ознакомились с результатом выполнения тестового задания и отметили творческий подход,
однако выбранные технологии и качество программной реализации не соответствуют нашим
стандартам.

13
my_anwer.txt Normal file
View 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

View 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'

View File

@ -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,

View File

@ -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 *

View File

@ -24,8 +24,16 @@
{% for PET in PETS %}<tr>
<td>{{ PET.szPetSerNum }}</td>
<td>{% if PET.szPetName %}{{ PET.szPetName }}{% else %}&mdash;{% 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 %}&mdash;{% endif %}</td>

View File

@ -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 %}&mdash;{% 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 %}&mdash;{% 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 %}

View File

@ -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+) на выбор тестируемого. Техническая реализация фильтра на усмотрение тестируемого (обработка на клиенте, запрос к серверу с параметрами, или иное).
При нажатии на подпункт «О приложении» будет отображено всплывающее окно с содержимым на выбор тестируемого.