-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #70 from xmartlabs/Occupancy-SendNotifications
Occupancy Notifications
- Loading branch information
Showing
13 changed files
with
240 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import os | ||
import time | ||
import logging | ||
import csv | ||
from datetime import date, datetime | ||
from collections import deque | ||
from .utils.mailing import MailService | ||
from .notifications.slack_notifications import SlackService | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class AreaReporting: | ||
|
||
def __init__(self, config, area): | ||
self.processing_alerts = False | ||
self.config = config | ||
self.area = area | ||
|
||
self.occupancy_sleep_time_interval = float(self.config.get_section_dict("App")["OccupancyAlertsMinInterval"]) | ||
self.log_dir = self.config.get_section_dict("Logger")["LogDirectory"] | ||
self.idle_time = float(self.config.get_section_dict('Logger')['TimeInterval']) | ||
self.area_id = self.area['id'] | ||
self.area_name = self.area['name'] | ||
self.occupancy_threshold = self.area['occupancy_threshold'] | ||
self.should_send_email_notifications = self.area['should_send_email_notifications'] | ||
self.should_send_slack_notifications = self.area['should_send_slack_notifications'] | ||
self.cameras = [camera for camera in self.config.get_video_sources() if camera['id'] in self.area['cameras']] | ||
for camera in self.cameras: | ||
camera['file_path'] = os.path.join(self.log_dir, camera['id'], "objects_log") | ||
camera['last_processed_time'] = time.time() | ||
|
||
self.mail_service = MailService(config) | ||
self.slack_service = SlackService(config) | ||
|
||
def process_area(self): | ||
# Sleep for a while so cameras start processing | ||
time.sleep(30) | ||
|
||
self.processing_alerts = True | ||
logger.info(f'Enabled processing alerts for - {self.area_id}: {self.area_name} with {len(self.cameras)} cameras') | ||
while self.processing_alerts: | ||
camera_file_paths = [os.path.join(camera['file_path'], str(date.today()) + ".csv") for camera in self.cameras] | ||
if not all(list(map(os.path.isfile, camera_file_paths))): | ||
# Wait before csv for this day are created | ||
logger.info(f'Area reporting on - {self.area_id}: {self.area_name} is waiting for reports to be created') | ||
time.sleep(5) | ||
|
||
occupancy = 0 | ||
for camera in self.cameras: | ||
with open(os.path.join(camera['file_path'], str(date.today()) + ".csv"), 'r') as log: | ||
last_log = deque(csv.DictReader(log), 1)[0] | ||
log_time = datetime.strptime(last_log['Timestamp'], "%Y-%m-%d %H:%M:%S") | ||
# TODO: If the TimeInterval of the Logger is more than 30 seconds this would have to be revised. | ||
if (datetime.now() - log_time).total_seconds() < 30: | ||
occupancy += int(last_log['DetectedObjects']) | ||
else: | ||
logger.warn(f"Logs aren't being updated for camera {camera['id']} - {camera['name']}") | ||
|
||
if occupancy > self.occupancy_threshold: | ||
# Trigger alerts | ||
if self.should_send_email_notifications: | ||
self.mail_service.send_occupancy_notification(self.area, occupancy) | ||
if self.should_send_slack_notifications: | ||
self.slack_service.occupancy_alert(self.area, occupancy) | ||
# Sleep until the cooldown of the alert | ||
time.sleep(self.occupancy_sleep_time_interval) | ||
else: | ||
# Sleep until new data is logged | ||
time.sleep(self.idle_time) | ||
|
||
self.stop_process_area() | ||
|
||
def stop_process_area(self): | ||
logger.info(f'Disabled processing alerts for area - {self.area_id}: {self.area_name}') | ||
self.processing_alerts = False |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import os | ||
from threading import Thread | ||
from libs.area_reporting import AreaReporting as AreaEngine | ||
import logging | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def run_area_processing(config, pipe, areas): | ||
pid = os.getpid() | ||
logger.info(f"[{pid}] taking on notifications for {len(areas)} areas") | ||
threads = [] | ||
for area in areas: | ||
engine = AreaThread(config, area) | ||
engine.start() | ||
threads.append(engine) | ||
|
||
# Wait for a signal to die | ||
pipe.recv() | ||
logger.info(f"[{pid}] will stop area alerts and die") | ||
for t in threads: | ||
t.stop() | ||
|
||
logger.info(f"[{pid}] Goodbye!") | ||
|
||
|
||
class AreaThread(Thread): | ||
def __init__(self, config, area): | ||
Thread.__init__(self) | ||
self.engine = None | ||
self.config = config | ||
self.area = area | ||
|
||
def run(self): | ||
self.engine = AreaEngine(self.config, self.area) | ||
self.engine.process_area() | ||
|
||
def stop(self): | ||
self.engine.stop_process_area() | ||
self.join() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
<!DOCTYPE html> | ||
|
||
<head> | ||
|
||
</head> | ||
|
||
<body> | ||
<table border="0" cellpadding="0" cellspacing="0" width="100%" | ||
style="min-width:100%;border-collapse:collapse;font-family: WorkSans-Regular_;"> | ||
<tbody> | ||
<tr> | ||
<td valign="top" style="padding:9px"> | ||
<table align="left" width="100%" border="0" cellpadding="0" cellspacing="0" | ||
style="min-width:100%;border-collapse:collapse"> | ||
<tbody> | ||
<tr> | ||
<td valign="top" | ||
style="padding-right:9px;padding-left:9px;padding-top:0;padding-bottom:0"> | ||
<img align="left" alt="" | ||
src="https://uploads-ssl.webflow.com/5f242545e7c52d78d56665aa/5f513656ab6ea7fb684aba6d_logo-horizontal%402x.png" | ||
width="208.76000000000002" | ||
style="max-width:614px;padding-bottom:0;display:inline!important;vertical-align:bottom;border:0;height:auto;outline:none;text-decoration:none" | ||
class="m_5283153553260840980mcnRetinaImage CToWUd"> | ||
</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
<table border="0" cellpadding="0" cellspacing="0" width="100%" | ||
style="min-width:100%;border-collapse:collapse"> | ||
<tbody> | ||
<tr> | ||
<td valign="top" style="padding-top:9px"> | ||
<table align="left" border="0" cellpadding="0" cellspacing="0" | ||
style="max-width:100%;min-width:100%;border-collapse:collapse" width="100%" | ||
class="m_5283153553260840980mcnTextContentContainer"> | ||
<tbody> | ||
<tr> | ||
<td valign="top" class="m_5283153553260840980mcnTextContent" | ||
style="padding: 0 18px 9px;word-break:break-word;color:#080e2a;font-family:Helvetica;font-size:16px;line-height:150%;text-align:left"> | ||
<h1 style="display:block;margin:0;padding:0;color:#080e2a;font-size:26px;line-height:125%;letter-spacing:normal;text-align:left"> | ||
Occupancy threshold was exceeded in <strong>{entity_type} {entity_id}: {entity_name}</strong>. | ||
</h1> | ||
<h3 style="display:block;margin:0;padding:0;color:#080e2a;font-size:26px;line-height:125%;letter-spacing:normal;text-align:left"> | ||
We found <strong>{num_occupancy}</strong> people out of a capacity of <strong>{entity_threshold}</strong>. | ||
</h3> | ||
</td> | ||
</tr> | ||
<tr> | ||
<td valign="top" class="m_5283153553260840980mcnTextContent" | ||
style="padding: 0 18px 9px;font-family:Helvetica;color:#080e2a;font-size: 20px;font-weight: normal"> | ||
<p>You can see more information or adjust this settings on:</p> | ||
<a href="{url}" style="text-decoration: #080e2a underline;cursor: pointer">{url}</a> | ||
</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</body> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters