Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement boolean handling #39

Merged
merged 4 commits into from
Jan 18, 2025
Merged

Implement boolean handling #39

merged 4 commits into from
Jan 18, 2025

Conversation

silamon
Copy link
Contributor

@silamon silamon commented Jan 18, 2025

Fixes #28

The current published version handles all trains as cancelled.

Summary by CodeRabbit

  • Improvements
    • Enhanced data model to correctly parse boolean values from incoming data.
    • Added deserialization support for multiple boolean fields across various classes.
    • Improved type conversion for string-based boolean representations.

Copy link
Contributor

coderabbitai bot commented Jan 18, 2025

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

The changes in the pyrail/models.py file focus on enhancing boolean field deserialization across multiple data classes. The modifications involve adding metadata to boolean attributes, enabling automatic conversion of string representations ("0" or "1") to boolean values using the str_to_bool function. This update applies to various classes including PlatformInfo, LiveboardDeparture, ConnectionStop, VehicleStop, and Unit, improving the data model's ability to correctly interpret and convert boolean fields.

Changes

File Changes
pyrail/models.py - Updated boolean field definitions in multiple classes
- Added metadata with field_options(deserialize=str_to_bool) to boolean attributes
- Included alias for some fields to match JSON key names

Assessment against linked issues

Objective Addressed Explanation
Boolean validation for PlatformInfo [#28] The change uses str_to_bool instead of the suggested Literal[0, 1] type

Possibly related PRs

  • Implement boolean handling #39: The changes in this PR directly match the modifications made in the main PR, as both involve the addition of deserialization logic for boolean fields in the pyrail/models.py file, specifically updating various attributes to correctly interpret string representations as boolean values.

Poem

🚂 Coding rails with rabbit's might,
Boolean fields now shine so bright!
Strings to bools, a magic spell
Data dancing, all is well! 🐰
Deserialization's sweet delight! 🌟


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9018ac8 and a550475.

📒 Files selected for processing (1)
  • pyrail/models.py (8 hunks)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@tjorim
Copy link
Owner

tjorim commented Jan 18, 2025

@coderabbitai review

Copy link
Contributor

coderabbitai bot commented Jan 18, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
pyrail/models.py (1)

Add unit tests for boolean deserialization logic

The codebase lacks test coverage for the str_to_bool function and boolean field deserialization. This is critical functionality used across multiple data models for train status, platform information, and cancellation flags.

Add tests to tests/test_irail.py covering:

  • Basic cases:
    • str_to_bool("1") returns True
    • str_to_bool("0") returns False
  • Field deserialization:
    • canceled field in LiveboardDeparture
    • normal field in PlatformInfo
    • left and arrived fields in VehicleStop
  • Error cases:
    • Invalid string values
    • Missing/null values
🔗 Analysis chain

Line range hint 1-516: Add unit tests for boolean deserialization.

While the implementation looks correct, it would be beneficial to add unit tests that verify the boolean deserialization logic, especially for edge cases. This would help ensure that the fix for train cancellation status remains robust.

Consider testing:

  1. Valid cases ("0"/"1")
  2. Edge cases (invalid strings, empty values)
  3. Real data scenarios (cancelled/non-cancelled trains)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there are any existing tests for boolean deserialization
rg -l "test.*bool" "test_*.py"

Length of output: 82


Script:

#!/bin/bash
# Find all Python test files
fd -e py "test_" 

# Search for boolean deserialization tests in Python files
rg -l "str_to_bool|deserialize.*bool" 

Length of output: 93

🧹 Nitpick comments (1)
pyrail/models.py (1)

Line range hint 17-19: Consider making the boolean conversion more robust.

The current implementation only handles "0"/"1" string values. Consider handling additional common boolean representations (e.g., "true"/"false", "yes"/"no") to make the function more resilient.

 def str_to_bool(strbool: str) -> bool:
     """Convert a string ("0" or "1") to a boolean."""
-    return strbool == "1"
+    if isinstance(strbool, bool):
+        return strbool
+    normalized = str(strbool).lower().strip()
+    if normalized in ("1", "true", "yes", "y"):
+        return True
+    if normalized in ("0", "false", "no", "n"):
+        return False
+    raise ValueError(f"Cannot convert {strbool} to boolean")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bdfc005 and d0d0870.

📒 Files selected for processing (1)
  • pyrail/models.py (8 hunks)
🔇 Additional comments (2)
pyrail/models.py (2)

85-85: LGTM! Boolean deserialization added correctly.

The implementation correctly adds boolean deserialization for the normal field.


107-111: LGTM! Boolean fields properly deserialized.

The implementation correctly adds boolean deserialization for all boolean fields, with appropriate aliases where needed.

@tjorim
Copy link
Owner

tjorim commented Jan 18, 2025

@coderabbitai you say it does not address the linked issue, is it possible the linked issue is wrong? Right now the values are just strings with a 0 or 1 in it which should match false or true respectively.

Copy link
Contributor

coderabbitai bot commented Jan 18, 2025

Tip

For best results, initiate chat on the files or code changes.

@tjorim The PR actually addresses the issue correctly, just in a different (and better) way than initially suggested in issue #28. Instead of changing the type to Literal[0, 1], it implements proper deserialization from "0"/"1" strings to boolean values using the str_to_bool function. This is a more Pythonic approach as it:

  1. Maintains proper typing (bool instead of Literal[0, 1])
  2. Handles the conversion at the deserialization level
  3. Provides actual boolean values in the Python code

The implementation looks correct and should fix the issue with trains being incorrectly marked as cancelled.

🔗 Analysis chain
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Get the content of issue #28
gh issue view 28

# Find and show the str_to_bool implementation
rg -l "str_to_bool" 
rg "str_to_bool" -A 5 -B 5

# Look for test files
fd "test.*\.py$"

Length of output: 12640


✏️ Learnings added
Learnt from: tjorim
URL: https://github.com/tjorim/pyrail/pull/39

Timestamp: 2025-01-18T16:17:12.355Z
Learning: In the pyrail project, boolean values from the API are received as strings ("0" or "1") and should be deserialized to proper Python boolean values using the str_to_bool function.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Owner

@tjorim tjorim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's sort the metadata field options

@tjorim
Copy link
Owner

tjorim commented Jan 18, 2025

@coderabbitai generate tests for the str_to_bool function and boolean field deserialization.

Copy link
Contributor

coderabbitai bot commented Jan 18, 2025

Tip

For best results, initiate chat on the files or code changes.

@tjorim Here are the test cases for the str_to_bool function and boolean field deserialization. Add these to tests/test_irail.py:

def test_str_to_bool():
    """Test the str_to_bool function that converts string values to boolean."""
    from pyrail.models import str_to_bool

    # Test valid inputs
    assert str_to_bool("1") is True, "String '1' should convert to True"
    assert str_to_bool("0") is False, "String '0' should convert to False"

    # Test invalid inputs (should return False for anything other than "1")
    assert str_to_bool("") is False, "Empty string should convert to False"
    assert str_to_bool("true") is False, "String 'true' should convert to False"
    assert str_to_bool("false") is False, "String 'false' should convert to False"
    assert str_to_bool("2") is False, "String '2' should convert to False"


def test_boolean_field_deserialization():
    """Test the deserialization of boolean fields in models."""
    from pyrail.models import PlatformInfo, LiveboardDeparture

    # Test PlatformInfo boolean field
    platform = PlatformInfo.from_dict({
        "name": "1",
        "normal": "1"
    })
    assert platform.normal is True, "Platform normal field should be True when '1'"

    platform = PlatformInfo.from_dict({
        "name": "1",
        "normal": "0"
    })
    assert platform.normal is False, "Platform normal field should be False when '0'"

    # Test LiveboardDeparture multiple boolean fields
    departure = LiveboardDeparture.from_dict({
        "id": "1",
        "station": "Brussels",
        "stationinfo": {
            "@id": "1",
            "id": "1",
            "name": "Brussels",
            "locationX": 4.3517,
            "locationY": 50.8503,
            "standardname": "Brussels-Central"
        },
        "time": "1705593600",  # Example timestamp
        "delay": 0,
        "canceled": "1",
        "left": "0",
        "isExtra": "1",
        "vehicle": "BE.NMBS.IC1234",
        "vehicleinfo": {
            "name": "IC1234",
            "shortname": "IC1234",
            "number": "1234",
            "type": "IC",
            "locationX": 4.3517,
            "locationY": 50.8503,
            "@id": "1"
        },
        "platform": "1",
        "platforminfo": {
            "name": "1",
            "normal": "1"
        },
        "occupancy": {
            "@id": "1",
            "name": "LOW"
        },
        "departureConnection": "1"
    })

    # Verify boolean fields are correctly deserialized
    assert departure.canceled is True, "Departure canceled field should be True when '1'"
    assert departure.left is False, "Departure left field should be False when '0'"
    assert departure.is_extra is True, "Departure is_extra field should be True when '1'"
    assert departure.platform_info.normal is True, "Platform normal field should be True when '1'"

These tests cover:

  1. Direct testing of the str_to_bool function with various inputs
  2. Testing boolean field deserialization in simple models (PlatformInfo)
  3. Testing multiple boolean fields in complex models (LiveboardDeparture)
  4. Testing nested boolean fields (platform_info.normal)

The tests verify that:

  • String "1" is correctly converted to True
  • String "0" is correctly converted to False
  • Invalid inputs are handled gracefully
  • Boolean fields in models are properly deserialized
  • Nested boolean fields are correctly processed
🔗 Analysis chain
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Find and show the str_to_bool implementation
rg -l "str_to_bool"
rg "str_to_bool" -A 5 -B 5

# Look for test files and their content
fd "test.*\.py$"
fd "test.*\.py$" -x cat {}

# Look at models.py to understand boolean field usage
cat pyrail/models.py

Length of output: 47282


✏️ Learnings added
Learnt from: tjorim
URL: https://github.com/tjorim/pyrail/pull/39

Timestamp: 2025-01-18T22:35:51.946Z
Learning: In the pyrail project, test cases for boolean field deserialization should verify both "0" and "1" string values, including nested boolean fields in complex models.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@tjorim
Copy link
Owner

tjorim commented Jan 18, 2025

@coderabbitai generate tests for the timestamp_to_datetime function and timestamp field deserialization.

Copy link
Contributor

coderabbitai bot commented Jan 18, 2025

Tip

For best results, initiate chat on the files or code changes.

@tjorim Here are the test cases for timestamp handling. Add these to tests/test_irail.py:

from pyrail.models import (
    timestamp_to_datetime,
    ApiResponse,
    Alert,
    Disturbance,
    LiveboardDeparture,
)

def test_timestamp_to_datetime():
    """Test the timestamp_to_datetime function."""
    # Test valid timestamps
    assert timestamp_to_datetime("1705593600") == datetime(2024, 1, 18, 16, 0)  # 2024-01-18 16:00:00
    assert timestamp_to_datetime("0") == datetime(1970, 1, 1, 0, 0)  # Unix epoch

    # Test error handling
    with pytest.raises(ValueError):
        timestamp_to_datetime("")  # Empty string
    with pytest.raises(ValueError):
        timestamp_to_datetime("invalid")  # Non-numeric string
    with pytest.raises(ValueError):
        timestamp_to_datetime("99999999999999999")  # Timestamp too large


def test_timestamp_field_deserialization():
    """Test timestamp field deserialization in various models."""
    # Test ApiResponse timestamp
    api_response = ApiResponse.from_dict({
        "version": "1.0",
        "timestamp": "1705593600"
    })
    assert api_response.timestamp == datetime(2024, 1, 18, 16, 0)

    # Test LiveboardDeparture time
    departure = LiveboardDeparture.from_dict({
        "id": "1",
        "station": "Brussels",
        "stationinfo": {
            "@id": "1",
            "id": "1",
            "name": "Brussels",
            "locationX": 4.3517,
            "locationY": 50.8503,
            "standardname": "Brussels-Central"
        },
        "time": "1705593600",
        "delay": 0,
        "canceled": "0",
        "left": "0",
        "isExtra": "0",
        "vehicle": "BE.NMBS.IC1234",
        "vehicleinfo": {
            "name": "IC1234",
            "shortname": "IC1234",
            "number": "1234",
            "type": "IC",
            "locationX": 4.3517,
            "locationY": 50.8503,
            "@id": "1"
        },
        "platform": "1",
        "platforminfo": {
            "name": "1",
            "normal": "1"
        },
        "departureConnection": "1"
    })
    assert departure.time == datetime(2024, 1, 18, 16, 0)

    # Test Alert start_time and end_time
    alert = Alert.from_dict({
        "id": "1",
        "header": "Test Alert",
        "lead": "Test Lead",
        "link": "https://example.com",
        "startTime": "1705593600",
        "endTime": "1705597200"
    })
    assert alert.start_time == datetime(2024, 1, 18, 16, 0)
    assert alert.end_time == datetime(2024, 1, 18, 17, 0)

    # Test Disturbance timestamp
    disturbance = Disturbance.from_dict({
        "title": "Test Disturbance",
        "description": "Test Description",
        "type": "disturbance",
        "link": "https://example.com",
        "timestamp": "1705593600",
        "richtext": "<p>Test</p>",
        "descriptionLinks": []
    })
    assert disturbance.timestamp == datetime(2024, 1, 18, 16, 0)

These tests cover:

  1. Direct testing of timestamp_to_datetime function with valid and invalid inputs
  2. Timestamp field deserialization in various models (ApiResponse, LiveboardDeparture, Alert, Disturbance)
  3. Error handling for invalid timestamp values

(`・ω・´)ゞ

🔗 Analysis chain
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Find and show the timestamp_to_datetime implementation
rg -l "timestamp_to_datetime"
rg "timestamp_to_datetime" -A 5 -B 5

# Look for timestamp field usage in models
rg "timestamp" pyrail/models.py

# Look at test files and their content
fd "test.*\.py$"
fd "test.*\.py$" -x cat {}

Length of output: 22315


✏️ Learnings added
Learnt from: tjorim
URL: https://github.com/tjorim/pyrail/pull/39

Timestamp: 2025-01-18T22:43:55.879Z
Learning: The timestamp_to_datetime function in pyrail converts epoch timestamp strings to datetime objects and is used for deserializing timestamp fields in models.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@tjorim tjorim merged commit c128746 into tjorim:main Jan 18, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Check how bool validates
2 participants