Team of 2 Guide
In this section, you and your partner will work on two different features simultaneously. This will simulate a real-world scenario where multiple developers contribute to the same codebase, potentially leading to merge conflicts.
9.1 Divide Roles
Decide who will be Member A and who will be Member B.
- Member A: Will implement the Average Calculation feature.
- Member B: Will implement the Reset Button feature.
9.2 Create Issues
Member A: Find Issue #6 ("Average Calculation Form") on the Project Board and move it to "In Progress".
Member B: Create a new issue:
- Title: Add Reset Functionality
- Description: Add a button to clear the input field and reset the result label.
- Configure the issue:
- Select the same "Feature" type and assign it to yourself.
- Select Project from the list and change status to "In Progress".
- Link the issue to Milestone 2: Average Calculation.
9.3 Implement Features (Parallel Work)
Both members should execute their tasks simultaneously on their own machines (or separate Codespaces).
Member A: Implement Average Calculation
- Sync your repository: ensure you are on
mainand have the latest changes. - Create a new branch named
feature/average-calcfrommain. - Open
app/main.py. - Add
from statistics import meanto imports (at the top of the file). -
Add the
parse_and_averagefunction (before thehomefunction):def parse_and_average(raw: str | None) -> str: raw = raw or "" tokens = [t for part in raw.split(",") for t in part.strip().split()] if raw else [] values = [float(t) for t in tokens if t] return f"Average: {mean(values):.4f}" -
Update the
home()function to include the form:@ui.page('/') def home() -> None: ui.label("CSE120 GitHub Workshop").classes("text-2xl font-bold") ui.label("Average Calculator").classes("text-lg text-gray-600 mb-4") numbers_input = ui.textarea(label="Numbers (comma or space separated)").classes("w-full h-32") result_label = ui.label("Enter numbers and click Compute").classes("mt-4 text-lg") def compute() -> None: # Connect the UI to the logic function result_label.text = parse_and_average(numbers_input.value) ui.button("Compute Average", on_click=compute).classes("mt-2") ui.separator() -
Add a Unit Test: To ensure your logic helps catch bugs, add a test case. Create
tests/test_average.py:from app.main import parse_and_average def test_average_simple(): assert parse_and_average("1, 2, 3") == "Average: 2.0000"Run the test:
uv run pytest -
Verify it works locally (
uv run fastapi dev). - Stage and commit changes with the message:
feat: add average calculation logic. - Push branch to GitHub.
Member B: Implement Reset Functionality
- Sync your repository: ensure you are on
mainand have the latest changes. - Create a new branch named
feature/reset-buttonfrommain. - Open
app/main.py. -
Update the
home()function to include a reset button. Note: You won't see Member A's code yet. Just implement your button as if you were the only one working on the file.@ui.page('/') def home() -> None: ui.label("CSE120 GitHub Workshop").classes("text-2xl font-bold") ui.label("Average Calculator").classes("text-lg text-gray-600 mb-4") # Add ID or reference to inputs if needed, or just define them numbers_input = ui.textarea(label="Numbers (comma or space separated)").classes("w-full h-32") result_label = ui.label("Enter numbers and click Compute").classes("mt-4 text-lg") def reset() -> None: numbers_input.value = "" result_label.text = "Enter numbers..." # Add the Reset button ui.button("Reset", on_click=reset).classes("mt-2 ml-4 bg-red-500 text-white") -
Verify it works locally (
uv run fastapi dev). - Stage and commit changes with the message:
feat: add reset button. - Push branch to GitHub.
9.4 Create Pull Requests
- Member A: Create a Pull Request for
feature/average-calc->main. - Member B: Create a Pull Request for
feature/reset-button->main.
[!NOTE] Fill out the PR template with a clear description of your changes and link the relevant issue.
9.5 Review and Merge (Conflict Phase)
Step 1: Merge Member A's PR 1. Member B reviews Member A's PR. 2. Member B approves the PR. 3. Member B merges the PR. 4. Member B deletes the branch.
[!NOTE] Wait for the merge to complete and the
mainbranch to update before proceeding to the next step.
Step 2: Member B's PR (Conflict!)
1. Member A goes to review Member B's PR.
2. You will likely see a message: "This branch has conflicts that must be resolved".
- This happens because both of you modified the home() function in app/main.py independently.
Step 3: Resolve Conflict
You have three options to resolve this conflict. Choose ONE method:
Option A: Using GitHub UI (Fastest)
- Member A clicks the Resolve conflicts button on the PR page.
- GitHub will show the conflicting file.
- Goal: Combine both features (Compute and Reset).
-
Manually edit the file in the browser:
- Remove conflict markers (
<<<<<<<,=======,>>>>>>>). - Keep Member A's
computefunction and imports. - Keep Member B's
resetfunction. - Keep BOTH buttons.
- Remove duplicate definitions of input fields.
- CRITICAL: Be very careful with indentation (4 spaces). Python will crash if indentation is wrong!
🆘 Stuck? Click here to see the Solution Code
(See solution code below in Option B)
- Remove conflict markers (
-
Click Mark as resolved.
- Click Commit merge.
Option B: Using VS Code (Recommended for Safety)
This method allows you to run tests before committing, ensuring you didn't break the code.
-
Member A pulls the latest changes to their local machine:
Note: This pulls Member B's changes into your local branch, triggering the conflict locally.git pull origin main -
VS Code will highlight the conflicting file (
app/main.py) in red. - Open
app/main.py. - Use the Merge Editor (Click "Resolve in Merge Editor" button if visible) OR look for the "Accept Current", "Accept Incoming", "Accept Both" links above the conflict.
- Choose Accept Both Changes (or manually edit to keep both logic parts).
- Verify: Run the tests to make sure you didn't break anything!
uv run pytest - Commit and push:
git add app/main.py git commit -m "merge: resolve conflicts between average and reset features" git push origin feature/reset-button
Option C: Using CLI (Advanced)
git pull origin main- Edit
app/main.pyto remove markers. uv run pytestgit add app/main.pygit commit -m "merge: resolve conflicts"git push
Solution Code (Reference for all options)
If you get stuck, here is how the final app/main.py should look:
🆘 Stuck? Click here to see the Solution Code
Here is how the resolved code should look:@ui.page('/')
def home() -> None:
ui.label("CSE120 GitHub Workshop").classes("text-2xl font-bold")
ui.label("Average Calculator").classes("text-lg text-gray-600 mb-4")
# Shared input definitions
numbers_input = ui.textarea(label="Numbers (comma or space separated)").classes("w-full h-32")
result_label = ui.label("Enter numbers and click Compute").classes("mt-4 text-lg")
# Member B's Reset Logic
def reset() -> None:
numbers_input.value = ""
result_label.text = "Enter numbers..."
# Member A's Compute Logic
def compute() -> None:
result_label.text = parse_and_average(numbers_input.value)
# Combined Buttons
ui.button("Compute Average", on_click=compute).classes("mt-2")
ui.separator()
ui.button("Reset", on_click=reset).classes("mt-2 ml-4 bg-red-500 text-white")
- Click Mark as resolved.
- Click Commit merge.
Step 4: Merge Member B's PR 1. Go back to the PR on GitHub. 2. The conflict message should be gone. 3. Member A approves the PR. 4. Member A merges the PR.
9.6 Bug Discovery and Fix
Now that both features are merged, pull the latest main branch locally.
- Member B: Pull the latest code locally.
- Member B: Test the application (
uv run fastapi dev).- Try entering non-numeric values (e.g.,
1, a, 3). - Result: The app crashes! (ValueError).
- Try entering non-numeric values (e.g.,
- Member B: Create an Bug Report Issue on GitHub titled "Fix crash on non-numeric input".
- Member B: Create a branch
patch/input-validationlocally. -
Member B: Update the
parse_and_averagefunction inapp/main.pyto handle errors:def parse_and_average(raw: str | None) -> str: raw = raw or "" tokens = [t for part in raw.split(",") for t in part.strip().split()] if raw else [] if not tokens: return "Error: no values provided" try: values = [float(t) for t in tokens if t] except ValueError: return "Error: non-numeric value detected" if not values: return "Error: no valid numeric values" return f"Average: {mean(values):.4f}" -
Member B: Commit and Push.
- Member B: Create PR.
- Member A: Review and Merge.
9.7 Conclusion
You have successfully: 1. Developed features in parallel. 2. Resolved a merge conflict. 3. Fixed a bug in collaborative code.
Proceed to Step 10: Deploy to Render.