diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 7f21d01..3ba13e0 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1 @@ blank_issues_enabled: false -contact_links: - - name: Integration Support - url: https://github.com/BottlecapDave/HomeAssistant-OctopusEnergy/discussions - about: Please ask and answer questions here. diff --git a/.github/workflows/deploy_docs.yml b/.github/workflows/deploy_docs.yml deleted file mode 100644 index 9bf0d7f..0000000 --- a/.github/workflows/deploy_docs.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Deploy docs -on: - push: - paths: - - 'mkdocs.yml' - - '_docs/**' - branches: - - main -permissions: - contents: write -jobs: - deploy_docs: - name: Deploy docs - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 - with: - python-version: 3.x - - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV - - uses: actions/cache@v3 - with: - key: mkdocs-material-${{ env.cache_id }} - path: .cache - restore-keys: | - mkdocs-material- - - run: pip install mkdocs-material - - run: mkdocs gh-deploy --force diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..ef09f75 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,47 @@ +name: Docs +on: + workflow_dispatch: + push: + paths: + - 'mkdocs.yml' + - '_docs/**' +jobs: + build_docs: + if: ${{ (github.repository_owner == 'BottlecapDave' && (github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main')) == false }} + name: Build docs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: 3.x + - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV + - uses: actions/cache@v3 + with: + key: mkdocs-material-${{ env.cache_id }} + path: .cache + restore-keys: | + mkdocs-material- + - run: pip install mkdocs-material + - run: mkdocs build --strict + + deploy_docs: + if: ${{ github.repository_owner == 'BottlecapDave' && (github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main') }} + name: Deploy docs + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: 3.x + - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV + - uses: actions/cache@v3 + with: + key: mkdocs-material-${{ env.cache_id }} + path: .cache + restore-keys: | + mkdocs-material- + - run: pip install mkdocs-material + - run: mkdocs gh-deploy --strict --force diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml index b107eea..656cf69 100644 --- a/.github/workflows/issues.yml +++ b/.github/workflows/issues.yml @@ -2,9 +2,9 @@ name: Close inactive issues on: schedule: - cron: "30 1 * * *" - jobs: close-issues: + if: ${{ github.repository_owner == 'BottlecapDave' }} runs-on: ubuntu-latest permissions: issues: write diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3dfb616..6257398 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,15 +1,22 @@ name: Main on: schedule: - - cron: '0 6 * * *' + - cron: '0 1 * * *' push: branches: - develop - main + paths-ignore: + - 'mkdocs.yml' + - '_docs/**' pull_request: - workflow_dispatch: + paths-ignore: + - 'mkdocs.yml' + - '_docs/**' jobs: validate: + if: ${{ github.event_name != 'schedule' || github.repository_owner == 'BottlecapDave' }} + name: Validate runs-on: "ubuntu-latest" steps: - uses: "actions/checkout@v4" @@ -18,8 +25,9 @@ jobs: uses: "hacs/action@main" with: category: "integration" - test: - name: Test + unit_tests: + if: ${{ github.event_name != 'schedule' || github.repository_owner == 'BottlecapDave' }} + name: Unit Tests runs-on: ubuntu-latest steps: - name: Checkout @@ -31,18 +39,33 @@ jobs: - name: Install Python modules run: | pip install -r requirements.test.txt - - name: Unit tests suite + - name: Run unit tests run: | python -m pytest tests/unit - - name: Integration tests suite + integration_tests: + if: ${{ github.event_name != 'schedule' || github.repository_owner == 'BottlecapDave' }} + name: Integration Tests + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: asdf_install + uses: asdf-vm/actions/install@v3 + - name: Install Python modules + run: | + pip install -r requirements.test.txt + - name: Run integration tests run: | python -m pytest tests/integration release: name: Release - if: github.ref == 'refs/heads/main' - needs: + if: ${{ github.repository_owner == 'BottlecapDave' && github.ref == 'refs/heads/main' }} + needs: - validate - - test + - unit_tests + - integration_tests runs-on: ubuntu-latest steps: - name: Checkout diff --git a/README.md b/README.md index d343fc3..59b6211 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ To install, place the contents of `custom_components` into the `= current_timestamp.replace(second=0, microsecond=0) and (next_bus is None or next_bus["Due"] > bus_time["Due"]): + if bus_time["due"] >= current_timestamp.replace(second=0, microsecond=0) and (next_bus is None or next_bus["due"] > bus_time["due"]): next_bus = bus_time return next_bus -def calculate_minutes_remaining(target_timestamp, current_timestamp): +def calculate_minutes_remaining(target_timestamp: datetime, current_timestamp: datetime): if target_timestamp is not None and current_timestamp is not None: if (target_timestamp >= current_timestamp): return (target_timestamp - current_timestamp).seconds // 60 diff --git a/tests/integration/api_client/test_get_bus_times.py b/tests/integration/api_client/test_get_bus_times.py index 93f777a..dc4ed7a 100644 --- a/tests/integration/api_client/test_get_bus_times.py +++ b/tests/integration/api_client/test_get_bus_times.py @@ -22,20 +22,17 @@ async def test_when_get_buses_is_called_then_next_bus_is_returned(): for bus in buses: assert bus is not None - assert "Due" in bus - assert bus["Due"] is not None + assert "due" in bus + assert bus["due"] is not None - assert "ServiceNumber" in bus - assert bus["ServiceNumber"] is not None + assert "service_number" in bus + assert bus["service_number"] is not None - assert "Destination" in bus - bus["Destination"] is not None + assert "destination" in bus + bus["destination"] is not None - assert "IsFG" in bus - assert bus["IsFG"] == "Y" or bus["IsFG"] == "N" - - assert "IsLive" in bus - assert bus["IsLive"] == "Y" or bus["IsFG"] == "N" + assert "is_live" in bus + assert bus["is_live"] == True or bus["is_live"] == False passes += 1 except: diff --git a/tests/integration/utils/test_get_next_bus.py b/tests/integration/utils/test_get_next_bus.py index efe9257..7877279 100644 --- a/tests/integration/utils/test_get_next_bus.py +++ b/tests/integration/utils/test_get_next_bus.py @@ -33,20 +33,17 @@ async def test_when_get_next_bus_is_called_then_next_bus_is_returned(target_buse # Assert assert next_bus is not None - assert "Due" in next_bus - assert next_bus["Due"].replace(tzinfo=pytz.UTC) >= datetime.utcnow().replace(tzinfo=pytz.UTC) + assert "due" in next_bus + assert next_bus["due"].replace(tzinfo=pytz.UTC) >= datetime.utcnow().replace(tzinfo=pytz.UTC) - assert "ServiceNumber" in next_bus + assert "service_number" in next_bus if target_buses is not None and len(target_buses) > 0: - assert next_bus["ServiceNumber"] in target_buses + assert next_bus["service_number"] in target_buses - assert "Destination" in next_bus + assert "destination" in next_bus - assert "IsFG" in next_bus - assert next_bus["IsFG"] == "Y" or next_bus["IsFG"] == "N" - - assert "IsLive" in next_bus - assert next_bus["IsLive"] == "Y" or next_bus["IsFG"] == "N" + assert "is_live" in next_bus + assert next_bus["is_live"] == True or next_bus["is_live"] == False passes += 1 except: diff --git a/tests/unit/utils/test_get_buses.py b/tests/unit/utils/test_get_buses.py index 0db1651..76ecebd 100644 --- a/tests/unit/utils/test_get_buses.py +++ b/tests/unit/utils/test_get_buses.py @@ -16,12 +16,11 @@ async def test_when_get_buses_is_called_and_set_time_in_past_is_returned_then_due_timestamp_is_correct(raw_due, expected_due): # Arrange bus_times = [{ - 'ServiceRef': '0', - 'ServiceNumber': '43', - 'Destination': 'Newton Road Shops', - 'Due': raw_due, - 'IsFG': 'N', - 'IsLive': 'Y' + 'service_ref': '0', + 'service_number': '43', + 'destination': 'Newton Road Shops', + 'due': raw_due, + 'is_live': True }] # Act @@ -33,17 +32,14 @@ async def test_when_get_buses_is_called_and_set_time_in_past_is_returned_then_du raw_bus = bus_times[0] # Assert - assert "Due" in bus - assert bus["Due"] == expected_due + assert "due" in bus + assert bus["due"] == expected_due - assert "ServiceNumber" in bus - assert bus["ServiceNumber"] == raw_bus["ServiceNumber"] + assert "service_number" in bus + assert bus["service_number"] == raw_bus["service_number"] - assert "Destination" in bus - assert bus["Destination"] == raw_bus["Destination"] + assert "destination" in bus + assert bus["destination"] == raw_bus["destination"] - assert "IsFG" in bus - assert bus["IsFG"] == raw_bus["IsFG"] - - assert "IsLive" in bus - assert bus["IsLive"] == raw_bus["IsLive"] \ No newline at end of file + assert "is_live" in bus + assert bus["is_live"] == raw_bus["is_live"] \ No newline at end of file diff --git a/tests/unit/utils/test_get_next_bus.py b/tests/unit/utils/test_get_next_bus.py index 2d7ce33..6de04cb 100644 --- a/tests/unit/utils/test_get_next_bus.py +++ b/tests/unit/utils/test_get_next_bus.py @@ -17,28 +17,25 @@ async def test_when_get_next_bus_is_called_then_due_timestamp_is_correct(target_ # Arrange buses = [ { - 'ServiceRef': '0', - 'ServiceNumber': '49', - 'Destination': 'Newton Road Shops', - 'Due': now + timedelta(days=1, minutes=64), - 'IsFG': 'N', - 'IsLive': 'Y' + 'service_ref': '0', + 'service_number': '49', + 'destination': 'Newton Road Shops', + 'due': now + timedelta(days=1, minutes=64), + 'is_live': True }, { - 'ServiceRef': '0', - 'ServiceNumber': '43', - 'Destination': 'Newton Road Shops', - 'Due': now + timedelta(minutes=35), - 'IsFG': 'N', - 'IsLive': 'Y' + 'service_ref': '0', + 'service_number': '43', + 'destination': 'Newton Road Shops', + 'due': now + timedelta(minutes=35), + 'is_live': True }, { - 'ServiceRef': '0', - 'ServiceNumber': '42', - 'Destination': 'Newton Road Shops', - 'Due': now + timedelta(minutes=64), - 'IsFG': 'N', - 'IsLive': 'Y' + 'service_ref': '0', + 'service_number': '42', + 'destination': 'Newton Road Shops', + 'due': now + timedelta(minutes=64), + 'is_live': True } ] @@ -55,39 +52,34 @@ async def test_when_get_next_bus_is_called_then_due_timestamp_is_correct(target_ else: assert next_bus is not None - assert "Due" in next_bus - assert next_bus["Due"] == expected_next_bus["Due"] + assert "due" in next_bus + assert next_bus["due"] == expected_next_bus["due"] - assert "ServiceNumber" in next_bus - assert next_bus["ServiceNumber"] == expected_next_bus["ServiceNumber"] + assert "service_number" in next_bus + assert next_bus["service_number"] == expected_next_bus["service_number"] - assert "Destination" in next_bus - assert next_bus["Destination"] == expected_next_bus["Destination"] + assert "destination" in next_bus + assert next_bus["destination"] == expected_next_bus["destination"] - assert "IsFG" in next_bus - assert next_bus["IsFG"] == expected_next_bus["IsFG"] - - assert "IsLive" in next_bus - assert next_bus["IsLive"] == expected_next_bus["IsLive"] + assert "is_live" in next_bus + assert next_bus["is_live"] == expected_next_bus["is_live"] @pytest.mark.asyncio async def test_when_get_next_bus_is_called_and_buses_in_the_past_then_correct_next_bus_is_picked(): # Arrange buses = [{ - 'ServiceRef': '0', - 'ServiceNumber': '43', - 'Destination': 'Newton Road Shops', - 'Due': now - timedelta(minutes=35), - 'IsFG': 'N', - 'IsLive': 'Y' + 'service_ref': '0', + 'service_number': '43', + 'destination': 'Newton Road Shops', + 'due': now - timedelta(minutes=35), + 'is_live': True }, { - 'ServiceRef': '0', - 'ServiceNumber': '42', - 'Destination': 'Newton Road Shops', - 'Due': now + timedelta(minutes=64), - 'IsFG': 'N', - 'IsLive': 'Y' + 'service_ref': '0', + 'service_number': '42', + 'destination': 'Newton Road Shops', + 'due': now + timedelta(minutes=64), + 'is_live': True }] expected_next_bus = buses[1] @@ -101,39 +93,34 @@ async def test_when_get_next_bus_is_called_and_buses_in_the_past_then_correct_ne else: assert next_bus is not None - assert "Due" in next_bus - assert next_bus["Due"] == expected_next_bus["Due"] - - assert "ServiceNumber" in next_bus - assert next_bus["ServiceNumber"] == expected_next_bus["ServiceNumber"] + assert "due" in next_bus + assert next_bus["due"] == expected_next_bus["due"] - assert "Destination" in next_bus - assert next_bus["Destination"] == expected_next_bus["Destination"] + assert "service_number" in next_bus + assert next_bus["service_number"] == expected_next_bus["service_number"] - assert "IsFG" in next_bus - assert next_bus["IsFG"] == expected_next_bus["IsFG"] + assert "destination" in next_bus + assert next_bus["destination"] == expected_next_bus["destination"] - assert "IsLive" in next_bus - assert next_bus["IsLive"] == expected_next_bus["IsLive"] + assert "is_live" in next_bus + assert next_bus["is_live"] == expected_next_bus["is_live"] @pytest.mark.asyncio async def test_when_subset_of_buses_looked_for_then_correct_bus_is_picked(): # Arrange buses = [{ - 'ServiceRef': '0', - 'ServiceNumber': '12 A', - 'Destination': 'Newton Road Shops', - 'Due': now - timedelta(minutes=35), - 'IsFG': 'N', - 'IsLive': 'Y' + 'service_ref': '0', + 'service_number': '12 A', + 'destination': 'Newton Road Shops', + 'due': now - timedelta(minutes=35), + 'is_live': True }, { - 'ServiceRef': '0', - 'ServiceNumber': '12', - 'Destination': 'Newton Road Shops', - 'Due': now + timedelta(minutes=64), - 'IsFG': 'N', - 'IsLive': 'Y' + 'service_ref': '0', + 'service_number': '12', + 'destination': 'Newton Road Shops', + 'due': now + timedelta(minutes=64), + 'is_live': True }] expected_next_bus = buses[1] @@ -147,17 +134,14 @@ async def test_when_subset_of_buses_looked_for_then_correct_bus_is_picked(): else: assert next_bus is not None - assert "Due" in next_bus - assert next_bus["Due"] == expected_next_bus["Due"] - - assert "ServiceNumber" in next_bus - assert next_bus["ServiceNumber"] == expected_next_bus["ServiceNumber"] + assert "due" in next_bus + assert next_bus["due"] == expected_next_bus["due"] - assert "Destination" in next_bus - assert next_bus["Destination"] == expected_next_bus["Destination"] + assert "service_number" in next_bus + assert next_bus["service_number"] == expected_next_bus["service_number"] - assert "IsFG" in next_bus - assert next_bus["IsFG"] == expected_next_bus["IsFG"] + assert "destination" in next_bus + assert next_bus["destination"] == expected_next_bus["destination"] - assert "IsLive" in next_bus - assert next_bus["IsLive"] == expected_next_bus["IsLive"] \ No newline at end of file + assert "is_live" in next_bus + assert next_bus["is_live"] == expected_next_bus["is_live"] \ No newline at end of file