Skip to content
This repository was archived by the owner on Jun 26, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
42e65ee
Update sonarr.py
ctalkington Apr 6, 2020
00afca2
Update test_interface.py
ctalkington Apr 6, 2020
c99ea20
Update test_interface.py
ctalkington Apr 6, 2020
3e341c5
Update sonarr.py
ctalkington Apr 6, 2020
48f5b74
Update models.py
ctalkington Apr 6, 2020
dd9ffa8
Create command.json
ctalkington Apr 6, 2020
a4b8cbd
Create command-id.json
ctalkington Apr 6, 2020
ae502fd
Update sonarr.py
ctalkington Apr 6, 2020
83421c7
Update models.py
ctalkington Apr 6, 2020
083e23c
Update models.py
ctalkington Apr 6, 2020
6a1800f
Update models.py
ctalkington Apr 6, 2020
798c4ed
Update models.py
ctalkington Apr 6, 2020
00305d2
Update models.py
ctalkington Apr 6, 2020
05a14af
Update sonarr.py
ctalkington Apr 6, 2020
84b5eca
Update test_interface.py
ctalkington Apr 6, 2020
12a094a
Update test_models.py
ctalkington Apr 6, 2020
3c19733
Update test_models.py
ctalkington Apr 6, 2020
9a36add
Update models.py
ctalkington Apr 6, 2020
4272a23
Update sonarr.py
ctalkington Apr 6, 2020
556d3ca
Update test_models.py
ctalkington Apr 6, 2020
610503f
Update sonarr.py
ctalkington Apr 6, 2020
291261c
Update test_models.py
ctalkington Apr 6, 2020
1e6e3d7
Update test_interface.py
ctalkington Apr 6, 2020
12d8165
Update models.py
ctalkington Apr 6, 2020
b71422e
Update test_models.py
ctalkington Apr 6, 2020
473cb1b
Update test_models.py
ctalkington Apr 6, 2020
5c11d70
Update test_models.py
ctalkington Apr 6, 2020
f8b882e
Update test_models.py
ctalkington Apr 6, 2020
52ad879
Update test_models.py
ctalkington Apr 6, 2020
243d158
Update command.json
ctalkington Apr 6, 2020
1c374cb
Update command.json
ctalkington Apr 6, 2020
14da1df
Update models.py
ctalkington Apr 6, 2020
36f586f
Update test_models.py
ctalkington Apr 6, 2020
bf5f322
Update test_models.py
ctalkington Apr 6, 2020
aea9f21
Update test_models.py
ctalkington Apr 6, 2020
1a2e912
Update test_models.py
ctalkington Apr 6, 2020
3362aec
Update test_models.py
ctalkington Apr 6, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 58 additions & 6 deletions sonarr/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from dataclasses import dataclass
from datetime import datetime
from typing import List, Optional
from typing import List

from .exceptions import SonarrError

Expand Down Expand Up @@ -33,11 +33,11 @@ class Season:

number: int
monitored: bool
downloaded: Optional[int] = 0
episodes: Optional[int] = 0
total_episodes: Optional[int] = 0
progress: Optional[int] = 0
diskspace: Optional[int] = 0
downloaded: int = 0
episodes: int = 0
total_episodes: int = 0
progress: int = 0
diskspace: int = 0

@staticmethod
def from_dict(data: dict):
Expand Down Expand Up @@ -183,6 +183,58 @@ def from_dict(data: dict):
return Info(app_name="Sonarr", version=data.get("version", "Unknown"))


@dataclass(frozen=True)
class CommandItem:
"""Object holding command item information from Sonarr."""

command_id: int
name: int
state: str
queued: datetime
started: datetime
changed: datetime
priority: str = "unknown"
trigger: str = "unknown"
message: str = "Not Provided"
send_to_client: bool = False

@staticmethod
def from_dict(data: dict):
"""Return CommandItem object from Sonarr API response."""
if "started" in data:
started = data.get("started", None)
else:
started = data.get("startedOn", None)

if "queued" in data:
queued = data.get("queued", None)
else:
queued = started

if started is not None:
started = datetime.strptime(started, "%Y-%m-%dT%H:%M:%S.%f%z")

if queued is not None:
queued = datetime.strptime(queued, "%Y-%m-%dT%H:%M:%S.%f%z")

changed = data.get("stateChangeTime", None)
if changed is not None:
changed = datetime.strptime(changed, "%Y-%m-%dT%H:%M:%S.%f%z")

return CommandItem(
command_id=data.get("id", 0),
name=data.get("name", "Unknown"),
state=data.get("state", "unknown"),
priority=data.get("priority", "unknown"),
trigger=data.get("trigger", "unknown"),
message=data.get("message", "Not Provided"),
send_to_client=data.get("sendUpdatesToClient", False),
queued=queued,
started=started,
changed=changed,
)


@dataclass(frozen=True)
class QueueItem:
"""Object holding queue item information from Sonarr."""
Expand Down
21 changes: 20 additions & 1 deletion sonarr/sonarr.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@

from .__version__ import __version__
from .exceptions import SonarrAccessRestricted, SonarrConnectionError, SonarrError
from .models import Application, Episode, QueueItem, SeriesItem, WantedResults
from .models import (
Application,
CommandItem,
Episode,
QueueItem,
SeriesItem,
WantedResults,
)


class Sonarr:
Expand Down Expand Up @@ -155,6 +162,18 @@ async def calendar(self, start: int = None, end: int = None) -> List[Episode]:

return [Episode.from_dict(result) for result in results]

async def commands(self) -> List[CommandItem]:
"""Query the status of all currently started commands."""
results = await self._request("command")

return [CommandItem.from_dict(result) for result in results]

async def command_status(self, command_id: int) -> CommandItem:
"""Query the status of a previously started command."""
result = await self._request(f"command/{command_id}")

return CommandItem.from_dict(result)

async def queue(self) -> List[QueueItem]:
"""Get currently downloading info."""
results = await self._request("queue")
Expand Down
27 changes: 27 additions & 0 deletions tests/fixtures/command-id.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "RefreshSeries",
"message": "Updating The Andy Griffith Show",
"body": {
"isNewSeries": false,
"sendUpdatesToClient": true,
"updateScheduledTask": true,
"completionMessage": "Completed",
"requiresDiskAccess": false,
"isExclusive": false,
"name": "RefreshSeries",
"trigger": "manual",
"suppressMessages": false
},
"priority": "normal",
"status": "started",
"queued": "2020-04-06T16:57:51.406504Z",
"started": "2020-04-06T16:57:51.417931Z",
"trigger": "manual",
"state": "started",
"manual": true,
"startedOn": "2020-04-06T16:57:51.406504Z",
"stateChangeTime": "2020-04-06T16:57:51.417931Z",
"sendUpdatesToClient": true,
"updateScheduledTask": true,
"id": 368630
}
36 changes: 36 additions & 0 deletions tests/fixtures/command.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[
{
"name": "RefreshSeries",
"body": {
"isNewSeries": false,
"sendUpdatesToClient": true,
"updateScheduledTask": true,
"completionMessage": "Completed",
"requiresDiskAccess": false,
"isExclusive": false,
"name": "RefreshSeries",
"trigger": "manual",
"suppressMessages": false
},
"priority": "normal",
"status": "started",
"queued": "2020-04-06T16:54:06.41945Z",
"started": "2020-04-06T16:54:06.421322Z",
"trigger": "manual",
"state": "started",
"manual": true,
"startedOn": "2020-04-06T16:54:06.41945Z",
"stateChangeTime": "2020-04-06T16:54:06.421322Z",
"sendUpdatesToClient": true,
"updateScheduledTask": true,
"id": 368621
},
{
"name": "RefreshSeries",
"state": "started",
"startedOn": "2020-04-06T16:57:51.406504Z",
"stateChangeTime": "2020-04-06T16:57:51.417931Z",
"sendUpdatesToClient": true,
"id": 368629
}
]
60 changes: 54 additions & 6 deletions tests/test_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ async def test_app(aresponses):

@pytest.mark.asyncio
async def test_calendar(aresponses):
"""Test calendar is handled correctly."""
"""Test calendar method is handled correctly."""
aresponses.add(
MATCH_HOST,
"/api/calendar?start=2014-01-26&end=2014-01-27",
Expand All @@ -74,9 +74,56 @@ async def test_calendar(aresponses):
assert isinstance(response[0], models.Episode)


@pytest.mark.asyncio
async def test_commands(aresponses):
"""Test commands method is handled correctly."""
aresponses.add(
MATCH_HOST,
"/api/command",
"GET",
aresponses.Response(
status=200,
headers={"Content-Type": "application/json"},
text=load_fixture("command.json"),
),
)

async with ClientSession() as session:
client = Sonarr(HOST, API_KEY, session=session)
response = await client.commands()

assert response
assert isinstance(response, List)

assert response[0]
assert isinstance(response[0], models.CommandItem)


@pytest.mark.asyncio
async def test_command_status(aresponses):
"""Test command_status method is handled correctly."""
aresponses.add(
MATCH_HOST,
"/api/command/368630",
"GET",
aresponses.Response(
status=200,
headers={"Content-Type": "application/json"},
text=load_fixture("command-id.json"),
),
)

async with ClientSession() as session:
client = Sonarr(HOST, API_KEY, session=session)
response = await client.command_status(368630)

assert response
assert isinstance(response, models.CommandItem)


@pytest.mark.asyncio
async def test_queue(aresponses):
"""Test queue is handled correctly."""
"""Test queue method is handled correctly."""
aresponses.add(
MATCH_HOST,
"/api/queue",
Expand All @@ -103,7 +150,7 @@ async def test_queue(aresponses):

@pytest.mark.asyncio
async def test_series(aresponses):
"""Test series is handled correctly."""
"""Test series method is handled correctly."""
aresponses.add(
MATCH_HOST,
"/api/series",
Expand Down Expand Up @@ -136,7 +183,7 @@ async def test_series(aresponses):

@pytest.mark.asyncio
async def test_update(aresponses):
"""Test update is handled correctly."""
"""Test update method is handled correctly."""
aresponses.add(
MATCH_HOST,
"/api/system/status",
Expand Down Expand Up @@ -187,16 +234,17 @@ async def test_update(aresponses):

@pytest.mark.asyncio
async def test_wanted(aresponses):
"""Test queue is handled correctly."""
"""Test queue method is handled correctly."""
aresponses.add(
MATCH_HOST,
"/api/wanted/missing",
"/api/wanted/missing?sortKey=airDateUtc&page=1&pageSize=10&sortDir=desc",
"GET",
aresponses.Response(
status=200,
headers={"Content-Type": "application/json"},
text=load_fixture("wanted-missing.json"),
),
match_querystring=True,
)

async with ClientSession() as session:
Expand Down
28 changes: 28 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

INFO = json.loads(load_fixture("system-status.json"))
CALENDAR = json.loads(load_fixture("calendar.json"))
COMMAND = json.loads(load_fixture("command.json"))
DISKSPACE = json.loads(load_fixture("diskspace.json"))
QUEUE = json.loads(load_fixture("queue.json"))
SERIES = json.loads(load_fixture("series.json"))
Expand Down Expand Up @@ -48,6 +49,33 @@ def test_info() -> None:
assert info.version == "2.0.0.1121"


def test_command_item() -> None:
"""Test the CommandItem model."""
item = models.CommandItem.from_dict(COMMAND[0])

assert item
assert item.name == "RefreshSeries"
assert item.message == "Not Provided"
assert item.state == "started"
assert item.priority == "normal"
assert item.trigger == "manual"
assert item.started == datetime(2020, 4, 6, 16, 54, 6, 421322, tzinfo=timezone.utc)
assert item.queued == datetime(2020, 4, 6, 16, 54, 6, 419450, tzinfo=timezone.utc)
assert item.changed == datetime(2020, 4, 6, 16, 54, 6, 421322, tzinfo=timezone.utc)

item = models.CommandItem.from_dict(COMMAND[1])

assert item
assert item.name == "RefreshSeries"
assert item.message == "Not Provided"
assert item.state == "started"
assert item.priority == "unknown"
assert item.trigger == "unknown"
assert item.started == datetime(2020, 4, 6, 16, 57, 51, 406504, tzinfo=timezone.utc)
assert item.queued == datetime(2020, 4, 6, 16, 57, 51, 406504, tzinfo=timezone.utc)
assert item.changed == datetime(2020, 4, 6, 16, 57, 51, 417931, tzinfo=timezone.utc)


def test_episode() -> None:
"""Test the Episode model."""
episode = models.Episode.from_dict(CALENDAR[0])
Expand Down