rogashova_ekaterina_lab_3 #106
2
rogashova_ekaterina_lab_3/.gitignore
vendored
Normal file
2
rogashova_ekaterina_lab_3/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/.venv
|
||||||
|
/.idea
|
27
rogashova_ekaterina_lab_3/docker-compose.yml
Normal file
27
rogashova_ekaterina_lab_3/docker-compose.yml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
services:
|
||||||
|
|
||||||
|
task_service:
|
||||||
|
container_name: task_service
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: ./task_service/Dockerfile
|
||||||
|
expose:
|
||||||
|
- 8081
|
||||||
|
|
||||||
|
teacher_service:
|
||||||
|
container_name: teacher_service
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: ./teacher_service/Dockerfile
|
||||||
|
expose:
|
||||||
|
- 8082
|
||||||
|
|
||||||
|
nginx:
|
||||||
|
image: nginx:latest
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
volumes:
|
||||||
|
- ./nginx.conf:/etc/nginx/nginx.conf
|
||||||
|
depends_on:
|
||||||
|
- task_service
|
||||||
|
- teacher_service
|
25
rogashova_ekaterina_lab_3/nginx.conf
Normal file
25
rogashova_ekaterina_lab_3/nginx.conf
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
events { worker_connections 1024; }
|
||||||
|
|
||||||
|
http {
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
location /task_service/ {
|
||||||
|
proxy_pass http://task_service:8081/;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /teacher_service/ {
|
||||||
|
proxy_pass http://teacher_service:8082/;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
rogashova_ekaterina_lab_3/readme.md
Normal file
37
rogashova_ekaterina_lab_3/readme.md
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# Лабораторная работа №3
|
||||||
|
|
||||||
|
## Описание
|
||||||
|
Были использованы следующие сущности:
|
||||||
|
1. Учитель. Поля: email и ФИО
|
||||||
|
2. Задание. Поля: название, предмет и id учителя
|
||||||
|
|
||||||
|
Сущности связаны связью один ко многим. У одного учителя может быть несколько заданий, у задания только один учитель.
|
||||||
|
|
||||||
|
Для каждой сущности был написан отдельный сервис.
|
||||||
|
|
||||||
|
Код каждого сервиса представляет собой полноценное API для работы с задачами и учителями.
|
||||||
|
Они включают основные операции CRUD (создание, чтение, обновление, удаление). Дополнительно можно вывести задачу с полной информацией об учителе.
|
||||||
|
|
||||||
|
В качестве хранилища данных использовалась оперативная память приложения.
|
||||||
|
|
||||||
|
Запросы к сервисам проксирует шлюз на основе Nginx. Для этого перед запуском nginx был описан конфигурационный файл nginx.conf,
|
||||||
|
в котором описан прослушиваемый порт и название сервера (в блоке server), маршруты до микросервисов и параметры проксирования (в блоке location).
|
||||||
|
|
||||||
|
Конфигурация Nginx направлена на организацию и упрощение работы с микросервисами.
|
||||||
|
|
||||||
|
Тестирование производится через расширение RestMan.
|
||||||
|
|
||||||
|
## Как запустить это?
|
||||||
|
|
||||||
|
Для запуска данной конфигурации Docker Compose выполните следующие шаги:
|
||||||
|
|
||||||
|
1. Открыть терминал и перейти в директорию, где находится docker-compose.yml.
|
||||||
|
2. Выполнить команду
|
||||||
|
```
|
||||||
|
docker compose up --build
|
||||||
|
```
|
||||||
|
3. Дождаться, пока Docker Compose запустит все контейнеры.
|
||||||
|
|
||||||
|
|
||||||
|
## Видео
|
||||||
|
Работоспособность представлена на [видео](https://vk.com/video204968285_456240927).
|
2
rogashova_ekaterina_lab_3/requirements.txt
Normal file
2
rogashova_ekaterina_lab_3/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Flask==3.0.3
|
||||||
|
requests==2.32.3
|
17
rogashova_ekaterina_lab_3/task_service/Dockerfile
Normal file
17
rogashova_ekaterina_lab_3/task_service/Dockerfile
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Используем базовый образ Python
|
||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
# Устанавливаем рабочую директорию
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Копируем файл requirements.txt в контейнер
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
# Устанавливаем зависимости
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Копируем все файлы в контейнер
|
||||||
|
COPY task_service/task_service.py .
|
||||||
|
|
||||||
|
# Команда для запуска Python-скрипта
|
||||||
|
CMD ["python", "task_service.py"]
|
119
rogashova_ekaterina_lab_3/task_service/task_service.py
Normal file
119
rogashova_ekaterina_lab_3/task_service/task_service.py
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
from flask import Flask, jsonify, request, abort
|
||||||
|
import uuid
|
||||||
|
import requests
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
tasks = {}
|
||||||
|
teachers = {}
|
||||||
|
|
||||||
|
@app.route('/tasks', methods=['GET'])
|
||||||
|
def get_tasks():
|
||||||
|
# Получаем все задачи, но только с teacher_id.
|
||||||
|
return jsonify(list(tasks.values()))
|
||||||
|
|
||||||
|
@app.route('/tasks_with_teachers', methods=['GET'])
|
||||||
|
def get_tasks_with_teachers():
|
||||||
|
tasks_with_teachers = []
|
||||||
|
|
||||||
|
for task in tasks.values():
|
||||||
|
teacher_id = task.get('teacher_id')
|
||||||
|
teacher_info = None
|
||||||
|
|
||||||
|
if teacher_id:
|
||||||
|
try:
|
||||||
|
# Запрос информации об учителе
|
||||||
|
teacher_response = requests.get(f'http://teacher_service:8082/teachers/{teacher_id}')
|
||||||
|
if teacher_response.status_code == 200:
|
||||||
|
teacher_details = teacher_response.json()
|
||||||
|
# Формируем объект с нужной информацией об учителе
|
||||||
|
teacher_info = {
|
||||||
|
"fio": teacher_details.get("fio"),
|
||||||
|
"email": teacher_details.get("email")
|
||||||
|
}
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"Error fetching teacher: {e}")
|
||||||
|
|
||||||
|
# Формируем ответ с нужной структурой
|
||||||
|
tasks_with_teachers.append({
|
||||||
|
'id': task.task_id,
|
||||||
|
'title': task.get('title'),
|
||||||
|
'subject': task.get('subject'),
|
||||||
|
'teacher_id': task.get('teacher_id'),
|
||||||
|
'teacherInfo': teacher_info # Упрощенная информация об учителе
|
||||||
|
})
|
||||||
|
|
||||||
|
return jsonify(tasks_with_teachers)
|
||||||
|
|
||||||
|
@app.route('/tasks/<string:task_id>', methods=['GET'])
|
||||||
|
def get_task(task_id):
|
||||||
|
task = tasks.get(task_id)
|
||||||
|
if task is None:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
return jsonify(task)
|
||||||
|
|
||||||
|
@app.route('/task_with_teacher/<string:task_id>', methods=['GET'])
|
||||||
|
def get_task_with_teacher(task_id):
|
||||||
|
task = tasks.get(task_id)
|
||||||
|
if task is None:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
# Получаем информацию о назначенном учителе из teacher_service
|
||||||
|
teacher_info = None
|
||||||
|
teacher_id = task.get('teacher_id')
|
||||||
|
if teacher_id:
|
||||||
|
try:
|
||||||
|
teacher_response = requests.get(f'http://teacher_service:8082/teachers/{teacher_id}')
|
||||||
|
if teacher_response.status_code == 200:
|
||||||
|
teacher_details = teacher_response.json()
|
||||||
|
# Формируем объект с нужной информацией об учителе
|
||||||
|
teacher_info = {
|
||||||
|
"fio": teacher_details.get("fio"),
|
||||||
|
"email": teacher_details.get("email")
|
||||||
|
}
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"Error fetching teacher: {e}")
|
||||||
|
|
||||||
|
# Формируем ответ с нужной структурой
|
||||||
|
response = {
|
||||||
|
"id": task_id, # id задания
|
||||||
|
"title": task.get('title'),
|
||||||
|
"subject": task.get('subject'),
|
||||||
|
"teacher_id": teacher_id,
|
||||||
|
"teacherInfo": teacher_info # Упрощенная информация об учителе
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonify(response)
|
||||||
|
|
||||||
|
@app.route('/tasks', methods=['POST'])
|
||||||
|
def create_task():
|
||||||
|
data = request.get_json()
|
||||||
|
task_id = str(uuid.uuid4())
|
||||||
|
tasks[task_id] = {
|
||||||
|
'id': task_id,
|
||||||
|
'title': data['title'],
|
||||||
|
'subject': data['subject'],
|
||||||
|
'teacher_id': data['teacher_id'] # Принимаем teacher_id из запроса
|
||||||
|
}
|
||||||
|
return jsonify(tasks[task_id]), 201
|
||||||
|
|
||||||
|
@app.route('/tasks/<string:task_id>', methods=['PUT'])
|
||||||
|
def update_task(task_id):
|
||||||
|
data = request.get_json()
|
||||||
|
task = tasks.get(task_id)
|
||||||
|
if task is None:
|
||||||
|
abort(404)
|
||||||
|
task['title'] = data.get('title', task['title'])
|
||||||
|
task['subject'] = data.get('subject', task['subject'])
|
||||||
|
return jsonify(task)
|
||||||
|
|
||||||
|
@app.route('/tasks/<string:task_id>', methods=['DELETE'])
|
||||||
|
def delete_task(task_id):
|
||||||
|
task = tasks.pop(task_id, None)
|
||||||
|
if task is None:
|
||||||
|
abort(404)
|
||||||
|
return '', 200
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host='0.0.0.0', use_reloader=False, port=8081)
|
17
rogashova_ekaterina_lab_3/teacher_service/Dockerfile
Normal file
17
rogashova_ekaterina_lab_3/teacher_service/Dockerfile
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Используем базовый образ Python
|
||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
# Устанавливаем рабочую директорию
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Копируем файл requirements.txt в контейнер
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
# Устанавливаем зависимости
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Копируем все файлы в контейнер
|
||||||
|
COPY teacher_service/teacher_service.py .
|
||||||
|
|
||||||
|
# Команда для запуска Python-скрипта
|
||||||
|
CMD ["python", "teacher_service.py"]
|
48
rogashova_ekaterina_lab_3/teacher_service/teacher_service.py
Normal file
48
rogashova_ekaterina_lab_3/teacher_service/teacher_service.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
from flask import Flask, jsonify, request, abort
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
teachers = {}
|
||||||
|
|
||||||
|
@app.route('/teachers', methods=['GET'])
|
||||||
|
def get_teachers():
|
||||||
|
return jsonify(list(teachers.values()))
|
||||||
|
|
||||||
|
@app.route('/teachers/<string:teacher_id>', methods=['GET'])
|
||||||
|
def get_teacher(teacher_id):
|
||||||
|
teacher = teachers.get(teacher_id)
|
||||||
|
if teacher is None:
|
||||||
|
abort(404)
|
||||||
|
return jsonify(teacher)
|
||||||
|
|
||||||
|
@app.route('/teachers', methods=['POST'])
|
||||||
|
def create_teacher():
|
||||||
|
data = request.get_json()
|
||||||
|
teacher_id = str(uuid.uuid4())
|
||||||
|
teachers[teacher_id] = {
|
||||||
|
'id': teacher_id,
|
||||||
|
'fio': data['fio'],
|
||||||
|
'email': data['email']
|
||||||
|
}
|
||||||
|
return jsonify(teachers[teacher_id]), 201
|
||||||
|
|
||||||
|
@app.route('/teachers/<string:teacher_id>', methods=['PUT'])
|
||||||
|
def update_teacher(teacher_id):
|
||||||
|
data = request.get_json()
|
||||||
|
teacher = teachers.get(teacher_id)
|
||||||
|
if teacher is None:
|
||||||
|
abort(404)
|
||||||
|
teacher['fio'] = data.get('fio', teacher['fio'])
|
||||||
|
teacher['email'] = data.get('email', teacher['email'])
|
||||||
|
return jsonify(teacher)
|
||||||
|
|
||||||
|
@app.route('/teachers/<string:teacher_id>', methods=['DELETE'])
|
||||||
|
def delete_teacher(teacher_id):
|
||||||
|
teacher = teachers.pop(teacher_id, None)
|
||||||
|
if teacher is None:
|
||||||
|
abort(404)
|
||||||
|
return '', 200
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host='0.0.0.0', use_reloader=False, port=8082)
|
Loading…
Reference in New Issue
Block a user