From d886f0a0bea4a75735ad205827ef1ac3a370f69f Mon Sep 17 00:00:00 2001 From: "d.volchkov" Date: Fri, 17 Nov 2023 21:31:30 +0500 Subject: [PATCH 1/3] completed_task --- .gitignore | 5 ++ .idea/.gitignore | 8 ++ .../inspectionProfiles/profiles_settings.xml | 6 ++ .idea/misc.xml | 4 + .idea/modules.xml | 8 ++ .idea/task-python.iml | 10 +++ .idea/vcs.xml | 6 ++ Repositories/ServiceRepo.py | 81 +++++++++++++++++++ config.py | 1 + db/db_connect.py | 7 ++ main.py | 6 ++ model/service.py | 10 +++ routers/service.py | 44 ++++++++++ 13 files changed, 196 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/task-python.iml create mode 100644 .idea/vcs.xml create mode 100644 Repositories/ServiceRepo.py create mode 100644 config.py create mode 100644 db/db_connect.py create mode 100644 main.py create mode 100644 model/service.py create mode 100644 routers/service.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..47cccf8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.env +.venv +__pycache__ +venv +idea \ No newline at end of file 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..817c1a6 --- /dev/null +++ b/Repositories/ServiceRepo.py @@ -0,0 +1,81 @@ +import datetime +import uuid + +import pymongo + +from db.db_connect import db +from model.service import Service + + +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): + _state = db.get_collection('service').find({"state": state}).sort('state_dt', pymongo.DESCENDING) + _list_state = [] + async for state in _state: + _list_state.append(state) + return _list_state + + @staticmethod + async def get_service_by_name(name): + _service = db.get_collection('service').find({"name": name}).sort('state_dt', pymongo.DESCENDING) + _list_service = [] + async for service in _service: + _list_service.append(service) + return _list_service + + @staticmethod + async def get_sla(name, date_start, date_end): + _service = (db.get_collection('service').find({"name": name, 'state_dt': {'$gt': date_start, '$lt': date_end}}) + .sort('state_dt', pymongo.DESCENDING)) + downtime = datetime.timedelta() + downtime_start = '' + downtime_end = '' + _list_state = [] + async for service in _service: + _list_state.append(service) + if service["state"] == "work": + downtime_start = service["state_dt"] + if not downtime_end == '': + downtime += downtime_end - downtime_start + downtime_start = '' + if service["state"] == "nowork": + downtime_end = service["state_dt"] + if not downtime_start == '': + downtime += downtime_end - downtime_start + downtime_end = '' + sla = ((date_end - date_start) - downtime) / (date_end - date_start) * 100 + return "{0:.0f} hours".format(downtime.total_seconds() // 3600), "{0:.3f}%".format(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}) diff --git a/config.py b/config.py new file mode 100644 index 0000000..76752c5 --- /dev/null +++ b/config.py @@ -0,0 +1 @@ +DB_URL = "mongodb://localhost:27017" \ No newline at end of file diff --git a/db/db_connect.py b/db/db_connect.py new file mode 100644 index 0000000..f165ac9 --- /dev/null +++ b/db/db_connect.py @@ -0,0 +1,7 @@ +import motor.motor_asyncio +from config import DB_URL +from fastapi import FastAPI + +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..d7aa993 --- /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) \ No newline at end of file From c590bd94fb1a489b2418deb8d1f80e52282aef47 Mon Sep 17 00:00:00 2001 From: "d.volchkov" Date: Sat, 18 Nov 2023 00:24:53 +0500 Subject: [PATCH 2/3] completed_task --- .gitignore | 2 +- Repositories/ServiceRepo.py | 146 ++++++++++++++++++++++++------------ config.py | 2 +- db/db_connect.py | 5 +- routers/service.py | 4 +- 5 files changed, 107 insertions(+), 52 deletions(-) diff --git a/.gitignore b/.gitignore index 47cccf8..1db56b0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ .venv __pycache__ venv -idea \ No newline at end of file +idea diff --git a/Repositories/ServiceRepo.py b/Repositories/ServiceRepo.py index 817c1a6..89bc2db 100644 --- a/Repositories/ServiceRepo.py +++ b/Repositories/ServiceRepo.py @@ -1,6 +1,5 @@ import datetime import uuid - import pymongo from db.db_connect import db @@ -11,71 +10,126 @@ 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 + """ Выводит список сервисов """ + 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 + """ Выводит список сервисов с актуальным состоянием """ + + 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): - _state = db.get_collection('service').find({"state": state}).sort('state_dt', pymongo.DESCENDING) - _list_state = [] - async for state in _state: - _list_state.append(state) - return _list_state + 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): - _service = db.get_collection('service').find({"name": name}).sort('state_dt', pymongo.DESCENDING) - _list_service = [] - async for service in _service: - _list_service.append(service) - return _list_service + 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, date_start, date_end): - _service = (db.get_collection('service').find({"name": name, 'state_dt': {'$gt': date_start, '$lt': date_end}}) - .sort('state_dt', pymongo.DESCENDING)) - downtime = datetime.timedelta() - downtime_start = '' - downtime_end = '' + 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 = [] - async for service in _service: - _list_state.append(service) - if service["state"] == "work": + _list_states = [] + downtime = datetime.timedelta() + downtime_end = None + downtime_start = None + + async for service in previous_service: + if service["state"] == "nowork": + _list_state.append(service) + + async for service in services: + _list_states.append(service) + if service["state"] == "nowork": downtime_start = service["state_dt"] - if not downtime_end == '': + if downtime_end: downtime += downtime_end - downtime_start - downtime_start = '' - if service["state"] == "nowork": + downtime_end = None + else: + downtime += date_end - downtime_start + elif service["state"] == "work": downtime_end = service["state_dt"] - if not downtime_start == '': - downtime += downtime_end - downtime_start - downtime_end = '' - sla = ((date_end - date_start) - downtime) / (date_end - date_start) * 100 - return "{0:.0f} hours".format(downtime.total_seconds() // 3600), "{0:.3f}%".format(sla) + + 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) + 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 index 76752c5..5bdcccb 100644 --- a/config.py +++ b/config.py @@ -1 +1 @@ -DB_URL = "mongodb://localhost:27017" \ No newline at end of file +DB_URL = "mongodb://localhost:27017" diff --git a/db/db_connect.py b/db/db_connect.py index f165ac9..c6f9876 100644 --- a/db/db_connect.py +++ b/db/db_connect.py @@ -1,7 +1,8 @@ import motor.motor_asyncio -from config import DB_URL from fastapi import FastAPI +from config import DB_URL + + app = FastAPI() db = motor.motor_asyncio.AsyncIOMotorClient(DB_URL).service - diff --git a/routers/service.py b/routers/service.py index d7aa993..e3c90a8 100644 --- a/routers/service.py +++ b/routers/service.py @@ -1,6 +1,6 @@ from datetime import datetime - from fastapi import APIRouter + from Repositories.ServiceRepo import ServiceRepo from model.service import Service @@ -41,4 +41,4 @@ async def add_tabel(service: Service): @router.delete("/delete", status_code=200) async def delete_user(id: str): - await ServiceRepo.delete_one(id) \ No newline at end of file + await ServiceRepo.delete_one(id) From 7990625e28a06cd1712cf2d3df1e5b36ab1326d8 Mon Sep 17 00:00:00 2001 From: "d.volchkov" Date: Sat, 18 Nov 2023 00:34:07 +0500 Subject: [PATCH 3/3] completed_task --- Repositories/ServiceRepo.py | 46 +++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/Repositories/ServiceRepo.py b/Repositories/ServiceRepo.py index 89bc2db..ed66c16 100644 --- a/Repositories/ServiceRepo.py +++ b/Repositories/ServiceRepo.py @@ -6,6 +6,10 @@ from model.service import Service +STATE_WORK = 'work' +STATE_NOT_WORK = 'notwork' + + class ServiceRepo(): @staticmethod @@ -21,12 +25,12 @@ async def get_all(): async def get_actual_state_service(): """ Выводит список сервисов с актуальным состоянием """ - services = await db.service.distinct("name") + services = await db.service.distinct('name') list_services = [] for service in services: actual_state = ( db.get_collection('service') - .find({"name": service}) + .find({'name': service}) .sort('state_dt', pymongo.DESCENDING) .limit(1) ) @@ -39,7 +43,7 @@ async def get_state_data(state: str): states = ( db.get_collection('service') - .find({"state": state}) + .find({'state': state}) .sort('state_dt', pymongo.DESCENDING) ) list_state = [] @@ -53,7 +57,7 @@ async def get_service_by_name(name: str): services = ( db.get_collection('service') - .find({"name": name}) + .find({'name': name}) .sort('state_dt', pymongo.DESCENDING) ) list_service = [] @@ -67,43 +71,41 @@ async def get_sla(name: str, date_start: datetime.datetime, date_end: datetime.d services = ( db.get_collection('service') - .find({"name": name, 'state_dt': {'$gt': date_start, '$lt': date_end}}) + .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}}) + .find({'name': name, 'state_dt': {'$lt': date_start}}) .sort('state_dt', pymongo.DESCENDING) .limit(1) ) - _list_state = [] - _list_states = [] + list_state = [] downtime = datetime.timedelta() downtime_end = None downtime_start = None async for service in previous_service: - if service["state"] == "nowork": - _list_state.append(service) + if service['state'] == STATE_NOT_WORK: + list_state.append(service) async for service in services: - _list_states.append(service) - if service["state"] == "nowork": - downtime_start = service["state_dt"] + if service['state'] == STATE_NOT_WORK: + downtime_start = service['state_dt'] if downtime_end: downtime += downtime_end - downtime_start downtime_end = None - else: - downtime += date_end - downtime_start - elif service["state"] == "work": - downtime_end = service["state_dt"] + continue + downtime += date_end - downtime_start + elif service['state'] == STATE_WORK: + downtime_end = service['state_dt'] - if _list_state and downtime_end: + if list_state and downtime_end: downtime += downtime_end - date_start - _list_state.pop() - elif _list_state and downtime_start: + list_state.pop() + elif list_state and downtime_start: downtime += downtime_start - date_start - _list_state.pop() + list_state.pop() sla = get_formatted_sla(date_start, date_end, downtime) return sla @@ -132,4 +134,4 @@ def get_formatted_sla(date_start: datetime.datetime, date_end: datetime.datetime """ Форматирование строки 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) + return '{0:.0f} hours'.format(downtime.total_seconds() // 3600), '{0:.3f}%'.format(sla)