diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1db56b0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+.env
+.venv
+__pycache__
+venv
+idea
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..19bf78b
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..ec5e7af
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/task-python.iml b/.idea/task-python.iml
new file mode 100644
index 0000000..ea85759
--- /dev/null
+++ b/.idea/task-python.iml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Repositories/ServiceRepo.py b/Repositories/ServiceRepo.py
new file mode 100644
index 0000000..ed66c16
--- /dev/null
+++ b/Repositories/ServiceRepo.py
@@ -0,0 +1,137 @@
+import datetime
+import uuid
+import pymongo
+
+from db.db_connect import db
+from model.service import Service
+
+
+STATE_WORK = 'work'
+STATE_NOT_WORK = 'notwork'
+
+
+class ServiceRepo():
+
+ @staticmethod
+ async def get_all():
+ """ Выводит список сервисов """
+ items = db.get_collection('service').find()
+ list_items = []
+ async for item in items:
+ list_items.append(item)
+ return list_items
+
+ @staticmethod
+ async def get_actual_state_service():
+ """ Выводит список сервисов с актуальным состоянием """
+
+ services = await db.service.distinct('name')
+ list_services = []
+ for service in services:
+ actual_state = (
+ db.get_collection('service')
+ .find({'name': service})
+ .sort('state_dt', pymongo.DESCENDING)
+ .limit(1)
+ )
+ list_services.append(await actual_state.to_list(1))
+ return list_services
+
+ @staticmethod
+ async def get_state_data(state: str):
+ """ Dсе данные по каждому состоянию """
+
+ states = (
+ db.get_collection('service')
+ .find({'state': state})
+ .sort('state_dt', pymongo.DESCENDING)
+ )
+ list_state = []
+ async for state in states:
+ list_state.append(state)
+ return list_state
+
+ @staticmethod
+ async def get_service_by_name(name: str):
+ """ По имени сервиса выдает историю изменения состояния """
+
+ services = (
+ db.get_collection('service')
+ .find({'name': name})
+ .sort('state_dt', pymongo.DESCENDING)
+ )
+ list_service = []
+ async for service in services:
+ list_service.append(service)
+ return list_service
+
+ @staticmethod
+ async def get_sla(name: str, date_start: datetime.datetime, date_end: datetime.datetime):
+ """ По указанному интервалу выдается информация о том сколько не работал сервис и считает SLA """
+
+ services = (
+ db.get_collection('service')
+ .find({'name': name, 'state_dt': {'$gt': date_start, '$lt': date_end}})
+ .sort('state_dt', pymongo.DESCENDING)
+ )
+ previous_service = (
+ db.get_collection('service')
+ .find({'name': name, 'state_dt': {'$lt': date_start}})
+ .sort('state_dt', pymongo.DESCENDING)
+ .limit(1)
+ )
+ list_state = []
+ downtime = datetime.timedelta()
+ downtime_end = None
+ downtime_start = None
+
+ async for service in previous_service:
+ if service['state'] == STATE_NOT_WORK:
+ list_state.append(service)
+
+ async for service in services:
+ if service['state'] == STATE_NOT_WORK:
+ downtime_start = service['state_dt']
+ if downtime_end:
+ downtime += downtime_end - downtime_start
+ downtime_end = None
+ continue
+ downtime += date_end - downtime_start
+ elif service['state'] == STATE_WORK:
+ downtime_end = service['state_dt']
+
+ if list_state and downtime_end:
+ downtime += downtime_end - date_start
+ list_state.pop()
+ elif list_state and downtime_start:
+ downtime += downtime_start - date_start
+ list_state.pop()
+
+ sla = get_formatted_sla(date_start, date_end, downtime)
+ return sla
+
+
+ @staticmethod
+ async def insert(service: Service):
+ """ Получает и сохраняет данные: имя, состояние, описание """
+
+ id = str(uuid.uuid4())
+ service.id = id
+ service = service.dict(exclude={})
+ service['_id'] = id
+ service.pop('id')
+ await db.get_collection('service').insert_one(service)
+ return service
+
+ @staticmethod
+ async def delete_one(id: str):
+ """ Удаление сервиса """
+
+ return await db.get_collection('service').delete_one({'_id': id})
+
+
+def get_formatted_sla(date_start: datetime.datetime, date_end: datetime.datetime, downtime: datetime.timedelta):
+ """ Форматирование строки SLA """
+
+ sla = ((date_end - date_start) - downtime) / (date_end - date_start) * 100
+ return '{0:.0f} hours'.format(downtime.total_seconds() // 3600), '{0:.3f}%'.format(sla)
diff --git a/config.py b/config.py
new file mode 100644
index 0000000..5bdcccb
--- /dev/null
+++ b/config.py
@@ -0,0 +1 @@
+DB_URL = "mongodb://localhost:27017"
diff --git a/db/db_connect.py b/db/db_connect.py
new file mode 100644
index 0000000..c6f9876
--- /dev/null
+++ b/db/db_connect.py
@@ -0,0 +1,8 @@
+import motor.motor_asyncio
+from fastapi import FastAPI
+
+from config import DB_URL
+
+
+app = FastAPI()
+db = motor.motor_asyncio.AsyncIOMotorClient(DB_URL).service
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..7391904
--- /dev/null
+++ b/main.py
@@ -0,0 +1,6 @@
+from fastapi import FastAPI
+from routers import service
+
+
+app = FastAPI()
+app.include_router(service.router)
diff --git a/model/service.py b/model/service.py
new file mode 100644
index 0000000..f7f2d55
--- /dev/null
+++ b/model/service.py
@@ -0,0 +1,10 @@
+import datetime
+from pydantic import BaseModel, Field
+
+
+class Service(BaseModel):
+ id: str = ''
+ name: str = Field(include=True)
+ state: str = Field(include=True)
+ description: str = Field(include=True)
+ state_dt: datetime.datetime = Field(default=datetime.datetime.now())
diff --git a/routers/service.py b/routers/service.py
new file mode 100644
index 0000000..e3c90a8
--- /dev/null
+++ b/routers/service.py
@@ -0,0 +1,44 @@
+from datetime import datetime
+from fastapi import APIRouter
+
+from Repositories.ServiceRepo import ServiceRepo
+from model.service import Service
+
+
+router = APIRouter(prefix="/service", tags=['/service'])
+
+
+@router.get("/")
+async def get_all():
+ return await ServiceRepo.get_all()
+
+
+@router.get("/actual_state_service")
+async def actual_state_service():
+ return await ServiceRepo.get_actual_state_service()
+
+
+@router.get("/get_state_data")
+async def get_state_data(state: str):
+ return await ServiceRepo.get_state_data(state)
+
+
+@router.get("/get_sla")
+async def get_state_data(name: str, date_start: datetime, date_end: datetime):
+ return await ServiceRepo.get_sla(name, date_start, date_end)
+
+
+@router.get("/get_service_by_name")
+async def get_service_by_name(name: str):
+ return await ServiceRepo.get_service_by_name(name)
+
+
+@router.post("/add_service", status_code=201)
+async def add_tabel(service: Service):
+ _service = await ServiceRepo.insert(service)
+ return _service
+
+
+@router.delete("/delete", status_code=200)
+async def delete_user(id: str):
+ await ServiceRepo.delete_one(id)