Skip to content

Commit

Permalink
Merge pull request #23 from cern-vc/develop
Browse files Browse the repository at this point in the history
Add support for Zoom Rooms
  • Loading branch information
renefs committed Jul 12, 2023
2 parents e9971f1 + a8399a5 commit 13d8847
Show file tree
Hide file tree
Showing 12 changed files with 344 additions and 13 deletions.
4 changes: 2 additions & 2 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Contributing to Buho Stocks
# Contributing to Zoom Python Client

When contributing to this repository, please first discuss the change you wish to
make via issue with the owners of this repository before making a change.
Expand Down Expand Up @@ -50,4 +50,4 @@ We also want to maximize the chances that your contribution will already be done
it harder to follow and to go look at the related issue or PR.
7. Please also refer to the guidelines for [commit messages](git-basics.md#commit-messages).

Once you've submitted a pull request, one or more of the maintainers will review it if all checks (tests and coverage pass).
Once you've submitted a pull request, one or more of the maintainers will review it if all checks (tests and coverage pass).
27 changes: 21 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,23 @@ Define the following variables in your `env` or your `.env` file:
### Initialize the ZoomApiClient from environment variables

```python
from src.zoom_python_client.zoom_api_client import ZoomApiClient
from zoom_python_client.zoom_api_client import ZoomApiClient

zoom_client = ZoomApiClient.init_from_env()
```

### Initialize the ZoomApiClient from .env

```python
from src.zoom_python_client.zoom_api_client import ZoomApiClient
from zoom_python_client.zoom_api_client import ZoomApiClient

zoom_client = ZoomApiClient.init_from_dotenv()
```

### Initialize the ZoomApiClient manually

```python
from src.zoom_python_client.zoom_api_client import ZoomApiClient
from zoom_python_client.zoom_api_client import ZoomApiClient

zoom_client = ZoomApiClient(
account_id="<YOUR ACCOUNT ID>",
Expand All @@ -59,7 +60,7 @@ There are some cases where you might want to store the access token in the file
You can define the path where the token will be stored, passing the `use_path` variable to the constructor:

```python
from src.zoom_python_client.zoom_api_client import ZoomApiClient
from zoom_python_client.zoom_api_client import ZoomApiClient

zoom_client = ZoomApiClient(
account_id="<YOUR ACCOUNT ID>",
Expand All @@ -68,7 +69,6 @@ zoom_client = ZoomApiClient(
use_path="/path/to/token/folder")
```


## How to make API calls

```python
Expand All @@ -93,18 +93,33 @@ setup_logs(log_level=logging.DEBUG)
## Available endpoints

### **users**:

1. get user details
2. get user meetings

### **meetings**:

1. get meeting details
2. get meeting token

### **meeting live streams**:

1. get meeting live stream
2. update live stream
3. update livestream status

### **webinars**:

1. get webinar details

### **webinar live streams**:

1. get webinar live stream
2. update webinar live stream
3. update webinar livestream status
3. update webinar livestream status

### **zoom rooms**:

1. get all zoom rooms
2. get zoom room details
3. get zoom room sensor data
23 changes: 23 additions & 0 deletions example/get_rooms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import logging

from zoom_python_client.client_components.rooms.rooms_component import (
RoomsListDict,
RoomType,
)
from zoom_python_client.utils.logger import setup_logs
from zoom_python_client.zoom_api_client import ZoomApiClient

logger = setup_logs(log_level=logging.DEBUG)

zoom_client = ZoomApiClient.init_from_dotenv(use_path=".")

parameters = RoomsListDict(
type=RoomType.ZOOM_ROOM,
)

result = zoom_client.rooms.get_rooms(parameters)

logger.info("Found %d rooms:", len(result["rooms"]))

for room in result["rooms"]:
print("\t- %s - %s - %s", room["name"], room["room_id"], room["status"])
50 changes: 50 additions & 0 deletions example/get_rooms_sensor_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import datetime
import logging
import os
import sys

import pytz

from zoom_python_client.client_components.rooms.rooms_component import (
RoomsSensorDataDict,
)
from zoom_python_client.utils.logger import setup_logs
from zoom_python_client.zoom_api_client import ZoomApiClient

logger = setup_logs(log_level=logging.DEBUG)

zoom_client = ZoomApiClient.init_from_dotenv(use_path=".")

ROOM_ID = os.getenv("ROOM_ID")

if ROOM_ID is None:
logger.error("ROOM_ID not found in environment variables")
sys.exit(1)


all_sensor_data = []

from_date = (datetime.datetime.now() - datetime.timedelta(hours=12)).isoformat(
timespec="seconds"
) + "Z"
to_date = datetime.datetime.now().isoformat(timespec="seconds") + "Z"

room_params = RoomsSensorDataDict(
from_date=from_date,
to_date=to_date,
)

while True:
result = zoom_client.rooms.get_room_sensor_data(ROOM_ID, data=room_params)
all_sensor_data.extend(result["sensor_data"])
if not result["next_page_token"]:
break
room_params["next_page_token"] = result["next_page_token"]

logger.info("Found %d sensor data points:", len(all_sensor_data))

for data in all_sensor_data:
date = datetime.datetime.strptime(
data["date_time"], "%Y-%m-%dT%H:%M:%SZ"
).astimezone(pytz.timezone("Europe/Zurich"))
logger.info("\t-%s - %d", date, data["sensor_value"])
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import pytest
import responses

from tests.zoom_python_client.base_test_case import TestCaseWithAuth
from zoom_python_client.client_components.rooms.rooms_component import (
RoomsComponent,
RoomsSensorDataDict,
)
from zoom_python_client.zoom_api_client import ZoomApiClient


class TestRoomsComponent(TestCaseWithAuth):
def setUp(self):
super().setUp()
zoom_client = ZoomApiClient("aaa", "bbb", "ccc", "http://localhost")
self.rooms_component = RoomsComponent(zoom_client)

@responses.activate
def test_get_rooms(self):
responses.add(
responses.GET,
"http://localhost/rooms",
json={"response": "ok"},
status=200,
)
rooms = self.rooms_component.get_rooms({})
assert rooms == {"response": "ok"}

@responses.activate
def test_get_room(self):
responses.add(
responses.GET,
"http://localhost/rooms/12345",
json={"response": "ok"},
status=200,
)
room = self.rooms_component.get_room("12345")
assert room == {"response": "ok"}

@responses.activate
def test_get_room_sensor_data_0(self):
responses.add(
responses.GET,
"http://localhost/rooms/12345/sensor_data",
json={"response": "ok"},
status=200,
)
parameters = RoomsSensorDataDict(
from_date="2020-01-01",
to_date="2023-07-10T08:27:46Z",
)
with pytest.raises(ValueError):
self.rooms_component.get_room_sensor_data("12345", parameters)

@responses.activate
def test_get_room_sensor_data_1(self):
responses.add(
responses.GET,
"http://localhost/rooms/12345/sensor_data",
json={"response": "ok"},
status=200,
)
parameters = RoomsSensorDataDict(
from_date="2023-07-10T08:27:46Z",
to_date="2020-01-01",
)
with pytest.raises(ValueError):
self.rooms_component.get_room_sensor_data("12345", parameters)

@responses.activate
def test_get_room_sensor_data_2(self):
responses.add(
responses.GET,
"http://localhost/rooms/12345/sensor_data",
json={"response": "ok"},
status=200,
)
parameters = RoomsSensorDataDict(
from_date="2023-07-10T08:27:46Z",
to_date="2023-07-10T10:27:46Z",
)
result = self.rooms_component.get_room_sensor_data("12345", parameters)
assert result == {"response": "ok"}
30 changes: 30 additions & 0 deletions tests/zoom_python_client/utils/test_typed_dict_parameters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from typing_extensions import NotRequired, TypedDict

from zoom_python_client.utils.typed_dict_parameters import generate_parameters_dict


class _TestListDict(TypedDict):
type: NotRequired[str]
page_size: NotRequired[int]
next_page_token: NotRequired[str]
page_number: NotRequired[int]
from_date: NotRequired[str]
to_date: NotRequired[str]


def test_generate_params_dict():
data = _TestListDict(type="test", page_number=1, page_size=10)
parameters = generate_parameters_dict(data)
assert parameters == {"type": "test", "page_size": 10, "page_number": 1}


def test_generate_params_dict_with_to_date():
data = _TestListDict(to_date="2021-01-01T00:00:00Z")
parameters = generate_parameters_dict(data)
assert parameters == {"to": "2021-01-01T00:00:00Z"}


def test_generate_params_dict_with_from_date():
data = _TestListDict(from_date="2021-01-01T00:00:00Z")
parameters = generate_parameters_dict(data)
assert parameters == {"from": "2021-01-01T00:00:00Z"}
Empty file.
100 changes: 100 additions & 0 deletions zoom_python_client/client_components/rooms/rooms_component.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import re
from enum import Enum

from typing_extensions import NotRequired, TypedDict

from zoom_python_client.utils.typed_dict_parameters import generate_parameters_dict
from zoom_python_client.zoom_client_interface import ZoomClientInterface


class RoomStatus(Enum):
OFFLINE = "Offline"
AVAILABLE = "Available"
IN_MEETING = "InMeeting"
UNDER_CONSTRUCTION = "UnderConstruction"


class RoomType(Enum):
KIOSK = "Kiosk"
ZOOM_ROOM = "ZoomRoom"
STANDALONE_WHITEBOARD = "StandaloneWhiteboard"
SCHEDULING_DISPLAY_ONLY = "SchedulingDisplayOnly"
DIGITAL_SIGNAGE_ONLY = "DigitalSignageOnly"


class NewPageToken(TypedDict):
"""The parameters for queries that return paginated results.
Args:
next_page_token (str): The token for the next page of results.
page_size (int): The number of records returned with a single API call.
"""

next_page_token: NotRequired[str]
page_size: NotRequired[int]


class RoomsListDict(NewPageToken):
"""The parameters for the get_rooms method.
Args:
status (Status): The status of the room.
type (Type): The type of the room.
unassigned_rooms (bool): Whether or not to include unassigned rooms.
location_id (str): The location ID of the room.
query_name (str): The name of the room.
"""

status: NotRequired[RoomStatus]
type: NotRequired[RoomType]
unassigned_rooms: NotRequired[bool]
location_id: NotRequired[str]
query_name: NotRequired[str]


class RoomsSensorDataDict(NewPageToken):
"""The parameters for the get_sensor_data method.
Args:
from_date (str): The start date and time for the query, should be in yyyy-MM-ddTHH:dd:ssZ format
to_date (str): The end date and time for the query, should be in yyyy-MM-ddTHH:dd:ssZ format
"""

from_date: str
to_date: str


class RoomsComponent:
def __init__(self, client: ZoomClientInterface) -> None:
self.client = client

def get_rooms(self, data: RoomsListDict) -> dict:
api_path = "/rooms"
parameters = generate_parameters_dict(data)

response = self.client.make_get_request(api_path, parameters=parameters)
result = response.json()
return result

def get_room(self, room_id: str) -> dict:
api_path = f"/rooms/{room_id}"

response = self.client.make_get_request(api_path)
result = response.json()
return result

def get_room_sensor_data(self, room_id: str, data: RoomsSensorDataDict) -> dict:
api_path = f"/rooms/{room_id}/sensor_data"

# Validate the date format
regex = r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$" # yyyy-MM-ddTHH:dd:ssZ
if not (
re.match(regex, data["from_date"]) and re.match(regex, data["to_date"])
):
raise ValueError("The date format should be in yyyy-MM-ddTHH:dd:ssZ format")

parameters = generate_parameters_dict(data)

response = self.client.make_get_request(api_path, parameters=parameters)
result = response.json()
return result
6 changes: 1 addition & 5 deletions zoom_python_client/utils/file_system.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import os
from pathlib import Path


def get_project_dir():
current_dir = os.path.abspath(os.path.dirname(__file__))
path = Path(current_dir)
project_dir = path.parent.absolute().parent
return project_dir
return Path(__file__).resolve().parents[2]
Loading

0 comments on commit 13d8847

Please sign in to comment.