Skip to content

Commit 44bad00

Browse files
authored
Add Sphinx usage documentation (#2)
Adds documentation describing every public method and data structure that the Python API makes available. The documentation uses Sphinx's autodoc extension to pull information from the docstrings. I've also added any missing docstrings and clarified some older ones.
1 parent 164ef42 commit 44bad00

38 files changed

Lines changed: 1050 additions & 240 deletions

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
*.egg-info
22
.idea/
33
*.pyc
4+
5+
_build/
6+
_static/

README.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
BrainFrame Python API
33
=====================
44

5+
.. image:: https://readthedocs.org/projects/brainframe-python-api/badge/?version=latest
6+
:target: https://brainframe-python-api.readthedocs.io/en/latest/?badge=latest
7+
:alt: Documentation Status
8+
9+
.. image:: https://github.com/aotuai/brainframe_python/workflows/Publish%20package/badge.svg
10+
:target: https://github.com/aotuai/brainframe_python/actions
11+
:alt: Publish Packages CI Status
12+
513
This library is a Python wrapper around the BrainFrame REST API. It allows for
614
easy interaction with a BrainFrame server.
715

@@ -34,3 +42,12 @@ you are using.
3442
.. code-block:: bash
3543
3644
pip3 install brainframe-api==0.26.0
45+
46+
Documentation
47+
-------------
48+
49+
Documentation for this library is available on `ReadTheDocs`_.
50+
51+
.. _`ReadTheDocs`:
52+
https://brainframe-python-api.readthedocs.io/en/latest/
53+

brainframe/api/bf_codecs/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
DirectionType,
1111
)
1212
from .config_codecs import StreamConfiguration, ConnType
13-
from .detection_codecs import Attribute, Detection, Identity
13+
from .identity_codecs import Identity, Encoding
14+
from .detection_codecs import Attribute, Detection
1415
from .zone_codecs import Zone, ZoneStatus
1516
from .plugin_codecs import (
1617
PluginOption,
@@ -19,9 +20,8 @@
1920
OptionType,
2021
SizeType,
2122
)
22-
from .encoding_codecs import Encoding
23-
from .premises_codec import Premises
24-
from .user_codec import User, RoleType
23+
from .premises_codecs import Premises
24+
from .user_codecs import User, RoleType
2525
from .license_codecs import (
2626
LicenseTerms,
2727
LicenseInfo,

brainframe/api/bf_codecs/alarm_codecs.py

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import List
1+
from typing import List, Optional
22

33
from .base_codecs import Codec
44
from .condition_codecs import ZoneAlarmCountCondition, ZoneAlarmRateCondition
@@ -7,18 +7,40 @@
77
class ZoneAlarm(Codec):
88
"""This is the configuration for an alarm."""
99

10-
def __init__(self, *, name, count_conditions, rate_conditions,
11-
use_active_time, active_start_time, active_end_time,
12-
id_=None, zone_id=None, stream_id=None):
13-
self.name = name
14-
self.id = id_
15-
self.zone_id = zone_id
16-
self.stream_id = stream_id
10+
def __init__(self, *,
11+
name: str,
12+
count_conditions: List[ZoneAlarmCountCondition],
13+
rate_conditions: List[ZoneAlarmRateCondition],
14+
use_active_time: bool,
15+
active_start_time: str,
16+
active_end_time: str,
17+
id_: int = None,
18+
zone_id: int = None,
19+
stream_id: int = None):
20+
self.name: str = name
21+
"""A friendly name for the zone alarm"""
22+
self.id: int = id_
23+
"""A unique identifier"""
24+
self.zone_id: int = zone_id
25+
"""The ID of the zone this alarm is associated with"""
26+
self.stream_id: int = stream_id
27+
"""The ID of the stream the associated zone is in"""
1728
self.count_conditions: List[ZoneAlarmCountCondition] = count_conditions
29+
"""All count conditions for this alarm"""
1830
self.rate_conditions: List[ZoneAlarmRateCondition] = rate_conditions
19-
self.use_active_time = use_active_time
20-
self.active_start_time = active_start_time
21-
self.active_end_time = active_end_time
31+
"""All rate conditions for this alarm"""
32+
self.use_active_time: bool = use_active_time
33+
"""If True, the alarm will only be triggered when the current time is
34+
between the active_start_time and active_end_time.
35+
"""
36+
self.active_start_time: str = active_start_time
37+
"""The time of day where this alarm starts being active, in the format
38+
"hh:mm:ss"
39+
"""
40+
self.active_end_time: str = active_end_time
41+
"""The time of day where this alarm starts being active, in the format
42+
"hh:mm:ss"
43+
"""
2244

2345
def to_dict(self):
2446
d = dict(self.__dict__)
@@ -48,24 +70,36 @@ def from_dict(d):
4870

4971

5072
class Alert(Codec):
51-
"""This is sent when an Alarm has been triggered.
52-
An alert can be sent WITHOUT an end_time, but never without a start_time.
73+
"""This is sent when an Alarm has been triggered."""
5374

54-
self.verified_as can be True, False or None.
55-
If True, then this alert was labeled by a person as being legitimate
56-
If False,then this alert was labeled by a person as being a false alarm
57-
If None, then this alert has not been labeled yet.
58-
"""
59-
60-
def __init__(self, *, alarm_id, zone_id, stream_id, start_time, end_time,
61-
verified_as, id_=None):
62-
self.id = id_
63-
self.alarm_id = alarm_id
64-
self.zone_id = zone_id
65-
self.stream_id = stream_id
66-
self.start_time = start_time
67-
self.end_time = end_time
68-
self.verified_as = verified_as
75+
def __init__(self, *,
76+
alarm_id: int,
77+
zone_id: int,
78+
stream_id: int,
79+
start_time: float,
80+
end_time: float,
81+
verified_as: Optional[bool],
82+
id_: int = None):
83+
self.id: int = id_
84+
"""A unique identifier"""
85+
self.alarm_id: int = alarm_id
86+
"""The ID of the alarm that this alert came from"""
87+
self.zone_id: int = zone_id
88+
"""The ID of the zone this alert pertains to"""
89+
self.stream_id: int = stream_id
90+
"""The ID of the stream this alert pertains to"""
91+
self.start_time: float = start_time
92+
"""When the event started happening, in Unix Time (seconds)"""
93+
self.end_time: Optional[float] = end_time
94+
"""When the event stopped happening, in Unix Time (seconds), or None
95+
if the alert is ongoing.
96+
"""
97+
self.verified_as: Optional[bool] = verified_as
98+
"""
99+
- If True, then this alert was labeled by a person as legitimate
100+
- If False, then this alert was labeled by a person as a false alarm
101+
- If None, then this alert has not been reviewed by a person
102+
"""
69103

70104
def to_dict(self):
71105
d = dict(self.__dict__)

brainframe/api/bf_codecs/condition_codecs.py

Lines changed: 80 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from enum import Enum
2+
from typing import Optional
23

34
from .base_codecs import Codec
45
from .detection_codecs import Attribute
@@ -13,6 +14,10 @@
1314

1415

1516
class CountConditionTestType(Enum):
17+
"""Defines the way a count condition compares the actual value to the
18+
alarm's test value.
19+
"""
20+
1621
GREATER_THAN = ">"
1722
LESS_THAN = "<"
1823
EQUAL_TO = "="
@@ -24,6 +29,12 @@ def values(cls):
2429

2530

2631
class IntersectionPointType(Enum):
32+
"""The point on a detection that must be inside a zone for the detection to
33+
count as being inside the zone. The most commonly used intersection point
34+
is BOTTOM, which counts a detection as being inside a zone if the bottom
35+
center point of the detection in the zone.
36+
"""
37+
2738
BOTTOM = "bottom"
2839
TOP = "top"
2940
LEFT = "left"
@@ -40,20 +51,38 @@ class ZoneAlarmCountCondition(Codec):
4051
of objects in a zone to some number.
4152
"""
4253

43-
TestType = CountConditionTestType
44-
IntersectionPointType = IntersectionPointType
45-
46-
def __init__(self, *, test, check_value, with_class_name, with_attribute,
47-
window_duration, window_threshold, intersection_point,
48-
id_=None):
49-
self.test = test
50-
self.check_value = check_value
51-
self.with_class_name = with_class_name
52-
self.with_attribute = with_attribute
53-
self.window_duration = window_duration
54-
self.window_threshold = window_threshold
55-
self.intersection_point = intersection_point
56-
self.id = id_
54+
def __init__(self, *,
55+
test: CountConditionTestType,
56+
check_value: int,
57+
with_class_name: str,
58+
with_attribute: Optional[Attribute],
59+
window_duration: float,
60+
window_threshold: float,
61+
intersection_point: IntersectionPointType,
62+
id_: int = None):
63+
self.test: CountConditionTestType = test
64+
"""The way that the check value will be compared to the actual count
65+
"""
66+
self.check_value: int = check_value
67+
"""The value to test the actual count against"""
68+
self.with_class_name: str = with_class_name
69+
"""The class name of the objects to count"""
70+
self.with_attribute: Optional[Attribute] = with_attribute
71+
"""If provided, only objects that have this attribute value will be
72+
counted.
73+
"""
74+
self.window_duration: float = window_duration
75+
"""The sliding window duration for this condition"""
76+
self.window_threshold: float = window_threshold
77+
"""The portion of time during the sliding window duration that this
78+
condition must be true in order for the associated alarm to trigger
79+
"""
80+
self.intersection_point: IntersectionPointType = intersection_point
81+
"""The point in each detection that must be within the zone in order
82+
for that detection to be counted as in that zone
83+
"""
84+
self.id: int = id_
85+
"""A unique identifier"""
5786

5887
def __repr__(self):
5988
condition_str = condition_test_map[self.test.value]
@@ -93,6 +122,10 @@ def from_dict(d: dict):
93122

94123

95124
class RateConditionTestType(Enum):
125+
"""Defines the way a rate condition compares the actual rate value to the
126+
alarm's test value.
127+
"""
128+
96129
GREATER_THAN_OR_EQUAL_TO = ">="
97130
LESS_THAN_OR_EQUAL_TO = "<="
98131

@@ -102,6 +135,8 @@ def values(cls):
102135

103136

104137
class DirectionType(Enum):
138+
"""Defines the direction of flow that a rate condition pertains to."""
139+
105140
ENTERING = "entering"
106141
EXITING = "exiting"
107142
ENTERING_OR_EXITING = "entering_or_exiting"
@@ -112,27 +147,43 @@ def values(cls):
112147

113148

114149
class ZoneAlarmRateCondition(Codec):
115-
"""A condition that must be met for an alarm to go off. Compares the rate of
116-
change in the count of some object against a test value.
150+
"""A condition that must be met for an alarm to go off. Compares the rate
151+
of change in the count of some object against a test value.
117152
"""
118153

119-
TestType = RateConditionTestType
120-
DirectionType = DirectionType
121-
122154
direction_map = {DirectionType.ENTERING: "entered",
123155
DirectionType.EXITING: "exited",
124156
DirectionType.ENTERING_OR_EXITING: "entered or exited"}
125157

126-
def __init__(self, *, test, duration, change, direction, with_class_name,
127-
with_attribute, intersection_point, id_=None):
128-
self.test = test
129-
self.duration = duration
130-
self.change = change
131-
self.direction = direction
132-
self.with_class_name = with_class_name
133-
self.with_attribute = with_attribute
134-
self.intersection_point = intersection_point
135-
self.id = id_
158+
def __init__(self, *,
159+
test: RateConditionTestType,
160+
duration: float,
161+
change: float,
162+
direction: DirectionType,
163+
with_class_name: str,
164+
with_attribute: Optional[Attribute],
165+
intersection_point: IntersectionPointType,
166+
id_: int = None):
167+
self.test: RateConditionTestType = test
168+
"""The way that the change value will be compared to the actual rate"""
169+
self.duration: float = duration
170+
"""The time in seconds for this rate change to occur"""
171+
self.change: float = change
172+
"""The rate change value to compare the actual rate value against"""
173+
self.direction: DirectionType = direction
174+
"""The direction of flow this condition tests for"""
175+
self.with_class_name: str = with_class_name
176+
"""The class name of the objects to measure rate of change for"""
177+
self.with_attribute: Optional[Attribute] = with_attribute
178+
"""If provided, only objects that have this attribute will be counted
179+
in the rate calculation
180+
"""
181+
self.intersection_point: IntersectionPointType = intersection_point
182+
"""The point in each detection that must be within the zone in order
183+
for that detection to be counted as in that zone
184+
"""
185+
self.id: int = id_
186+
"""A unique identifier"""
136187

137188
def __repr__(self):
138189
condition_str = condition_test_map[self.test.value]

0 commit comments

Comments
 (0)