Skip to content

Commit

Permalink
Processor Basic Info Endpoint. HasBeenConfigured Config (#84)
Browse files Browse the repository at this point in the history
* Processor Basic Info Endpoint, Added HasBeenConfigured Config
Co-authored-by: Mathias Claassen <mathias@xmartlabs.com>
  • Loading branch information
renzodgc committed Nov 20, 2020
1 parent bf82aad commit e96a3fd
Show file tree
Hide file tree
Showing 14 changed files with 92 additions and 43 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NOTIFICATION_EMAIL_FROM=noreply@yourdomain.com
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ oauth2_cred.json
.aws
venv/
slack_token.txt
.env
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,12 +212,16 @@ You can read and modify the configurations in `config-*.ini` files, accordingly:

`config-x86-openvino.ini`: for x86 systems accelerated with Openvino

Please note that if you modify these values you should also set `[App]` `HasBeenConfigured` to `"True"`.
This allows for a client to recognize if this processor was previously configured.

You can also modify some of them using the [UI](https://beta.lanthorn.ai).
If you choose this option, make sure to mount the config file as a volume to keep the changes after any restart of the container.

All the configurations are grouped in *sections* and some of them can vary depending on the chosen device.

- `[App]`
- `HasBeenConfigured`: A boolean parameter that states whether the *config.ini* was set up or not.
- `Resolution`: Specifies the image resolution that the whole processor will use. If you are using a single camera we recommend using that resolution.
- `Encoder`: Specifies the video encoder used by the processing pipeline.
- `MaxProcesses`: Defines the number of processes executed in the processor. If you are using multiple cameras per processor we recommend increasing this number.
Expand Down
37 changes: 37 additions & 0 deletions api/config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging

from fastapi import APIRouter
from pydantic import BaseModel
from typing import Optional

from .areas import map_area, map_to_area_file_format
Expand All @@ -9,12 +10,28 @@
from .utils import (
extract_config, handle_response, update_and_restart_config
)
from constants import PROCESSOR_VERSION

logger = logging.getLogger(__name__)

config_router = APIRouter()


class ConfigInfo(BaseModel):
version: str
device: str
has_been_configured: bool

class Config:
schema_extra = {
"example": {
"version": PROCESSOR_VERSION,
"device": "device",
"has_been_configured": True
}
}


def map_to_config_file_format(config_dto: ConfigDTO):
config_dict = dict()
for count, camera in enumerate(config_dto.cameras):
Expand All @@ -33,6 +50,18 @@ def map_config(config, options):
}


def processor_info(config):
has_been_configured = bool(config["App"]["HasBeenConfigured"])
device = config["Detector"]["Device"]
if config["Detector"]["Name"] == "openvino":
device += "-openvino"
return {
"version": PROCESSOR_VERSION,
"device": device,
"has_been_configured": has_been_configured
}


@config_router.get("", response_model=ConfigDTO)
async def get_config(options: Optional[str] = ""):
"""
Expand All @@ -50,3 +79,11 @@ async def update_config(config: ConfigDTO):
config_dict = map_to_config_file_format(config)
success = update_and_restart_config(config_dict)
return handle_response(config_dict, success)


@config_router.get("/info", response_model=ConfigInfo)
async def get_processor_info():
"""
Returns basic info regarding this processor
"""
return processor_info(extract_config())
3 changes: 2 additions & 1 deletion api/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ fastapi==0.61.1
pandas==1.1.2
pyhumps==1.6.1
pytest==6.0.1
python-dotenv==0.15.0
requests==2.24.0
schedule==0.6.0
slackclient==2.8.2
uvicorn==0.11.8
yagmail==0.11.224
slackclient==2.8.2
1 change: 0 additions & 1 deletion api/settings.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from pydantic import BaseSettings
from libs.config_engine import ConfigEngine


class Settings:
instance = None

Expand Down
1 change: 1 addition & 0 deletions config-coral.ini
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[App]
HasBeenConfigured = False
Resolution = 640,480
Encoder = videoconvert ! video/x-raw,format=I420 ! x264enc speed-preset=ultrafast
MaxProcesses = 1
Expand Down
1 change: 1 addition & 0 deletions config-jetson.ini
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[App]
HasBeenConfigured = False
Resolution = 640,480
Encoder: videoconvert ! video/x-raw,format=I420 ! x264enc speed-preset=ultrafast
MaxProcesses = 1
Expand Down
1 change: 1 addition & 0 deletions config-x86-gpu.ini
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ QueuePort = 8010
QueueAuthKey = shibalba

[App]
HasBeenConfigured = False
Resolution = 640,480
Encoder = videoconvert ! video/x-raw,format=I420 ! x264enc speed-preset=ultrafast
MaxProcesses = 1
Expand Down
1 change: 1 addition & 0 deletions config-x86-openvino.ini
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ QueuePort = 8010
QueueAuthKey = shibalba

[App]
HasBeenConfigured = False
Resolution = 640,480
Encoder = videoconvert ! video/x-raw,format=I420 ! x264enc speed-preset=ultrafast
MaxProcesses = 2
Expand Down
1 change: 1 addition & 0 deletions config-x86.ini
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ QueuePort = 8010
QueueAuthKey = shibalba

[App]
HasBeenConfigured = False
Resolution = 640,480
Encoder = videoconvert ! video/x-raw,format=I420 ! x264enc speed-preset=ultrafast
MaxProcesses = 1
Expand Down
1 change: 1 addition & 0 deletions constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PROCESSOR_VERSION = "0.1.0"
73 changes: 37 additions & 36 deletions libs/config_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class ConfigEngine:
:param config_path: the path of config file
"""

def __init__(self, config_path='./config-coral.ini'):
def __init__(self, config_path="./config-coral.ini"):
self.config = configparser.ConfigParser()
self.config.optionxform = str
self.config_file_path = config_path
Expand Down Expand Up @@ -120,47 +120,48 @@ def set_option_in_section(self, section, option, value):
def update_config(self, config, save_file=True):
current_sections = []
for section, options in config.items():
if section.startswith('Source') or section.startswith('Area'):
if section.startswith("Source") or section.startswith("Area"):
current_sections.append(section)
for option, value in options.items():
self.set_option_in_section(section, option, value)
for section in self.config.sections():
if len(current_sections) and (section.startswith('Source') or section.startswith('Area')) and section not in current_sections:
if len(current_sections) and (section.startswith("Source") or section.startswith("Area")) and section not in current_sections:
self.config.remove_section(section)
self.set_option_in_section("App", "HasBeenConfigured", "True")
if save_file:
self.save(self.config_file_path)

def get_entity_with_notifications(self, title, section):
ent = {'section': title, 'id': section['Id'], 'name': section['Name']}
if 'Tags' in section and section['Tags'].strip() != "":
ent['tags'] = section['Tags'].split(',')
if 'Emails' in section and section['Emails'].strip() != "":
ent['emails'] = section['Emails'].split(',')
ent['notify_every_minutes'] = int(section['NotifyEveryMinutes'])
ent['violation_threshold'] = int(section['ViolationThreshold'])
ent['daily_report'] = self.config.getboolean(title, 'DailyReport')
ent['daily_report_time'] = section.get('DailyReportTime') or '06:00'
ent = {"section": title, "id": section["Id"], "name": section["Name"]}
if "Tags" in section and section["Tags"].strip() != "":
ent["tags"] = section["Tags"].split(",")
if "Emails" in section and section["Emails"].strip() != "":
ent["emails"] = section["Emails"].split(",")
ent["notify_every_minutes"] = int(section["NotifyEveryMinutes"])
ent["violation_threshold"] = int(section["ViolationThreshold"])
ent["daily_report"] = self.config.getboolean(title, "DailyReport")
ent["daily_report_time"] = section.get("DailyReportTime") or "06:00"

return ent

def get_video_sources(self):
try:
sources = []
for title, section in self.config.items():
if title.startswith('Source_'):
if title.startswith("Source_"):
src = self.get_entity_with_notifications(title, section)
src['type'] = 'Camera'
src['url'] = section['VideoPath']
src['dist_method'] = section['DistMethod']
if 'Tags' in section and section['Tags'].strip() != "":
src['tags'] = section['Tags'].split(',')
if src['notify_every_minutes'] > 0 and src['violation_threshold'] > 0:
src['should_send_email_notifications'] = 'emails' in src
src['should_send_slack_notifications'] = bool(self.config['App']['SlackChannel'] and
self.config.getboolean('App', 'EnableSlackNotifications'))
src["type"] = "Camera"
src["url"] = section["VideoPath"]
src["dist_method"] = section["DistMethod"]
if "Tags" in section and section["Tags"].strip() != "":
src["tags"] = section["Tags"].split(",")
if src["notify_every_minutes"] > 0 and src["violation_threshold"] > 0:
src["should_send_email_notifications"] = "emails" in src
src["should_send_slack_notifications"] = bool(self.config["App"]["SlackChannel"] and
self.config.getboolean("App", "EnableSlackNotifications"))
else:
src['should_send_email_notifications'] = False
src['should_send_slack_notifications'] = False
src["should_send_email_notifications"] = False
src["should_send_slack_notifications"] = False
sources.append(src)
return sources
except:
Expand All @@ -171,20 +172,20 @@ def get_areas(self):
try:
areas = []
for title, section in self.config.items():
if title.startswith('Area_'):
if title.startswith("Area_"):
area = self.get_entity_with_notifications(title, section)
area['type'] = 'Area'
area['occupancy_threshold'] = int(section['OccupancyThreshold'])
if 'Cameras' in section and section['Cameras'].strip() != "":
area['cameras'] = section['Cameras'].split(',')

if (area['notify_every_minutes'] > 0 and area['violation_threshold'] > 0) or area['occupancy_threshold'] > 0:
area['should_send_email_notifications'] = 'emails' in area
area['should_send_slack_notifications'] = bool(self.config['App']['SlackChannel'] and
self.config.getboolean('App', 'EnableSlackNotifications'))
area["type"] = "Area"
area["occupancy_threshold"] = int(section["OccupancyThreshold"])
if "Cameras" in section and section["Cameras"].strip() != "":
area["cameras"] = section["Cameras"].split(",")

if (area["notify_every_minutes"] > 0 and area["violation_threshold"] > 0) or area["occupancy_threshold"] > 0:
area["should_send_email_notifications"] = "emails" in area
area["should_send_slack_notifications"] = bool(self.config["App"]["SlackChannel"] and
self.config.getboolean("App", "EnableSlackNotifications"))
else:
area['should_send_email_notifications'] = False
area['should_send_slack_notifications'] = False
area["should_send_email_notifications"] = False
area["should_send_slack_notifications"] = False
areas.append(area)
return areas
except:
Expand Down
9 changes: 4 additions & 5 deletions libs/utils/mailing.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import os
import yagmail
import logging
import codecs

# TODO: Move to a constants file
NOTIFICATION_EMAIL_FROM = "noreply@yourdomain.com"

from dotenv import load_dotenv
load_dotenv()

def send_email(from_email, receiver, subject, contents, attachments=None):
with yagmail.SMTP(from_email, oauth2_file="oauth2_cred.json") as yag:
Expand All @@ -21,7 +20,7 @@ class MailService:

def __init__(self, config):
self.config = config
self.email_from = NOTIFICATION_EMAIL_FROM
self.email_from = os.getenv("NOTIFICATION_EMAIL_FROM")
self.logger = logging.getLogger(__name__)

def send_email_notification(self, entity_info, subject, content):
Expand Down

0 comments on commit e96a3fd

Please sign in to comment.