feat: Phase 4 grouping with entry-break dividers
Add group.py route, phase4.html template, and supporting state changes. Photos are shown as a flat stream; clicking divider zones inserts entry-break boundaries that split photos into labelled groups. Labels persist via group_labels dict. Done materialises groups into state.groups and advances to write phase. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,5 +14,7 @@
|
||||
"local_datetime": "2023-09-06T10:00:00", "tag": "untagged", "order": 2}
|
||||
],
|
||||
"groups": [],
|
||||
"notes": ""
|
||||
"notes": "",
|
||||
"dividers": [],
|
||||
"group_labels": {}
|
||||
}
|
||||
|
||||
@@ -14,5 +14,7 @@
|
||||
"local_datetime": "2023-09-06T10:00:00", "tag": "skip", "order": 2}
|
||||
],
|
||||
"groups": [],
|
||||
"notes": ""
|
||||
"notes": "",
|
||||
"dividers": [],
|
||||
"group_labels": {}
|
||||
}
|
||||
|
||||
@@ -12,5 +12,7 @@
|
||||
"local_datetime": "2023-09-05T14:30:00", "tag": "story", "order": 1}
|
||||
],
|
||||
"groups": [],
|
||||
"notes": "I remember the airport was chaos."
|
||||
"notes": "I remember the airport was chaos.",
|
||||
"dividers": [],
|
||||
"group_labels": {}
|
||||
}
|
||||
|
||||
@@ -14,16 +14,18 @@
|
||||
"groups": [
|
||||
{
|
||||
"id": "g1", "photo_ids": ["asset-1"], "entry_type": "journal",
|
||||
"title": "", "body": "", "location_city": "", "location_country": "",
|
||||
"label": "", "title": "", "body": "", "location_city": "", "location_country": "",
|
||||
"date": "2023-09-05", "hero_photo_id": null, "shortcode_hints": "",
|
||||
"status": "draft"
|
||||
},
|
||||
{
|
||||
"id": "g2", "photo_ids": ["asset-2"], "entry_type": "story",
|
||||
"title": "", "body": "", "location_city": "", "location_country": "",
|
||||
"label": "", "title": "", "body": "", "location_city": "", "location_country": "",
|
||||
"date": "2023-09-05", "hero_photo_id": null, "shortcode_hints": "",
|
||||
"status": "draft"
|
||||
}
|
||||
],
|
||||
"notes": "I remember the airport was chaos."
|
||||
"notes": "I remember the airport was chaos.",
|
||||
"dividers": [],
|
||||
"group_labels": {}
|
||||
}
|
||||
|
||||
@@ -14,18 +14,20 @@
|
||||
"groups": [
|
||||
{
|
||||
"id": "g1", "photo_ids": ["asset-1"], "entry_type": "journal",
|
||||
"title": "Arrival in Almaty", "body": "Chaos at the airport.",
|
||||
"label": "", "title": "Arrival in Almaty", "body": "Chaos at the airport.",
|
||||
"location_city": "Almaty", "location_country": "Kazakhstan",
|
||||
"date": "2023-09-05", "hero_photo_id": "asset-1", "shortcode_hints": "",
|
||||
"status": "written"
|
||||
},
|
||||
{
|
||||
"id": "g2", "photo_ids": ["asset-2"], "entry_type": "story",
|
||||
"title": "The Market", "body": "Colours everywhere.",
|
||||
"label": "", "title": "The Market", "body": "Colours everywhere.",
|
||||
"location_city": "Almaty", "location_country": "Kazakhstan",
|
||||
"date": "2023-09-05", "hero_photo_id": "asset-2", "shortcode_hints": "gallery block",
|
||||
"status": "skipped"
|
||||
}
|
||||
],
|
||||
"notes": ""
|
||||
"notes": "",
|
||||
"dividers": [],
|
||||
"group_labels": {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import json
|
||||
|
||||
|
||||
def test_photos_shown_as_stream(base_url, page, seed_state):
|
||||
album_id = seed_state("phase4_state")
|
||||
page.goto(f"{base_url}/group?album_id={album_id}")
|
||||
assert page.locator(".stream-photo").count() == 2
|
||||
|
||||
|
||||
def test_insert_divider_creates_group_boundary(base_url, page, seed_state, flask_app):
|
||||
album_id = seed_state("phase4_state")
|
||||
page.goto(f"{base_url}/group?album_id={album_id}")
|
||||
page.locator(".divider-zone").first.hover()
|
||||
page.locator(".insert-divider-btn").first.click()
|
||||
page.wait_for_timeout(300)
|
||||
assert page.locator(".group-block").count() == 2
|
||||
|
||||
|
||||
def test_remove_divider_merges_groups(base_url, page, seed_state):
|
||||
album_id = seed_state("phase4_state")
|
||||
page.goto(f"{base_url}/group?album_id={album_id}")
|
||||
page.locator(".divider-zone").first.hover()
|
||||
page.locator(".insert-divider-btn").first.click()
|
||||
page.wait_for_timeout(200)
|
||||
page.locator(".remove-divider-btn").first.click()
|
||||
page.wait_for_timeout(200)
|
||||
assert page.locator(".group-block").count() == 1
|
||||
|
||||
|
||||
def test_label_edit_persists(base_url, page, seed_state, flask_app):
|
||||
album_id = seed_state("phase4_state")
|
||||
page.goto(f"{base_url}/group?album_id={album_id}")
|
||||
page.locator(".divider-zone").first.hover()
|
||||
page.locator(".insert-divider-btn").first.click()
|
||||
page.wait_for_timeout(200)
|
||||
page.locator(".group-label").first.fill("Morning walk")
|
||||
page.locator(".group-label").first.press("Enter")
|
||||
page.wait_for_timeout(300)
|
||||
page.reload()
|
||||
assert "Morning walk" in page.locator(".group-label").first.input_value()
|
||||
|
||||
|
||||
def test_done_advances_to_write(base_url, page, seed_state):
|
||||
album_id = seed_state("phase4_state")
|
||||
page.goto(f"{base_url}/group?album_id={album_id}")
|
||||
page.locator("#done-btn").click()
|
||||
page.wait_for_url("**/write**")
|
||||
Reference in New Issue
Block a user