This commit is contained in:
2026-06-07 20:24:36 +07:00
parent b177fc7a76
commit 54ddd924d9
9 changed files with 1300 additions and 499 deletions

View File

@@ -6,14 +6,12 @@
<component name="ChangeListManager">
<list default="true" id="d308d1cb-09fc-4331-ba20-00f7b43d1576" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.idea/.idea.BABA_YAGA/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.BABA_YAGA/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/BABA_YAGA_Updater/README.md" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/BABA_YAGA_Updater/sections/04_maze.md" beforeDir="false" afterPath="$PROJECT_DIR$/BABA_YAGA_Updater/sections/04_maze.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/BABA_YAGA_Updater/sections/05_networking.md" beforeDir="false" afterPath="$PROJECT_DIR$/BABA_YAGA_Updater/sections/05_networking.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/BABA_YAGA_Updater/sections/06_comeback.md" beforeDir="false" afterPath="$PROJECT_DIR$/BABA_YAGA_Updater/sections/06_comeback.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/BABA_YAGA_Updater/sections/07_roadmap.md" beforeDir="false" afterPath="$PROJECT_DIR$/BABA_YAGA_Updater/sections/07_roadmap.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/BABA_YAGA_Updater/core/models.py" beforeDir="false" afterPath="$PROJECT_DIR$/BABA_YAGA_Updater/core/models.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/BABA_YAGA_Updater/main.py" beforeDir="false" afterPath="$PROJECT_DIR$/BABA_YAGA_Updater/main.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/BABA_YAGA_Updater/mappers/markdown_builder.py" beforeDir="false" afterPath="$PROJECT_DIR$/BABA_YAGA_Updater/mappers/markdown_builder.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/BABA_YAGA_Updater/mappers/sheet_mapper.py" beforeDir="false" afterPath="$PROJECT_DIR$/BABA_YAGA_Updater/mappers/sheet_mapper.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/BABA_YAGA_Updater/sections/08_team_standards.md" beforeDir="false" afterPath="$PROJECT_DIR$/BABA_YAGA_Updater/sections/08_team_standards.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/BABA_YAGA_Updater/sections/09_structure.md" beforeDir="false" afterPath="$PROJECT_DIR$/BABA_YAGA_Updater/sections/09_structure.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/BABA_YAGA_Updater/sections/10_footer.md" beforeDir="false" afterPath="$PROJECT_DIR$/BABA_YAGA_Updater/sections/10_footer.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/BABA_YAGA_Updater/services/readme_editor.py" beforeDir="false" afterPath="$PROJECT_DIR$/BABA_YAGA_Updater/services/readme_editor.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
@@ -105,7 +103,7 @@
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1780826181670</updated>
<workItem from="1780826183468" duration="10287000" />
<workItem from="1780826183468" duration="11195000" />
</task>
<servers />
</component>

View File

@@ -2,11 +2,14 @@ from pydantic import BaseModel
from typing import Optional
class Task(BaseModel):
category: str
phase: str
task_id: str
task_name: str
assignee: str
start_date: str
end_date: str
progress_val: str
status: str
progress: str # e.g., "75%" or "In Progress"
notes: Optional[str] = ""
class ProgressReport(BaseModel):
tasks: list[Task]

View File

@@ -1,12 +1,14 @@
import sys
import os
from services.gsheet_client import GSheetClient
from mappers.sheet_mapper import SheetMapper
from mappers.markdown_builder import MarkdownBuilder
from services.readme_editor import ReadmeEditor
from config.settings import settings
def main():
try:
print("🚀 Starting README update from Google Sheets...")
print("🚀 Starting README Update Process...")
# 1. Fetch data from Google Sheets
client = GSheetClient()
@@ -18,17 +20,50 @@ def main():
# 2. Map raw rows to Core Models
report = SheetMapper.map_rows_to_report(raw_rows)
print(f"Parsed {len(report.tasks)} tasks.")
print(f"Data fetched: {len(report.tasks)} tasks parsed.")
# 3. Build Markdown content
markdown_content = MarkdownBuilder.build_table(report)
# 3. Prepare Dynamic Content
markdown_table = MarkdownBuilder.build_table(report)
progress_summary = MarkdownBuilder.build_team_progress(report)
# 4. Update README.md
# 4. First, update the local section files with the new dynamic data
print("🔧 Injecting data into local sections...")
editor = ReadmeEditor()
if editor.update_section(markdown_content):
print("🎉 README.md successfully updated!")
# Inject progress into 08_team_standards.md
editor.inject_content_between_markers(
"sections/08_team_standards.md",
progress_summary,
"<!-- START_PROGRESS -->",
"<!-- END_PROGRESS -->"
)
# Inject task table into 07_roadmap.md (or whichever section has the marker)
# Note: Your main README markers were originally in 07_roadmap or similar
editor.inject_content_between_markers(
"sections/07_roadmap.md",
markdown_table,
"<!-- START_UPDATES -->",
"<!-- END_UPDATES -->"
)
# 5. Aggregate all sections into one big README content
print("📚 Aggregating all sections from 00 to 10...")
all_sections_content = ""
section_files = sorted([f for f in os.listdir("sections") if f.endswith(".md")])
for filename in section_files:
file_path = os.path.join("sections", filename)
with open(file_path, "r", encoding="utf-8") as f:
all_sections_content += f.read() + "\n\n---\n\n"
# 6. Update the ROOT README.md (../README.md)
print(f"📄 Updating Root README.md at: {settings.README_PATH}")
root_editor = ReadmeEditor(file_path=settings.README_PATH)
if root_editor.update_whole_readme(all_sections_content):
print("🎉 Root README.md successfully updated from all sections!")
else:
print("❌ Failed to update README.md.")
print("❌ Failed to update Root README.md.")
except Exception as e:
print(f"💥 An unexpected error occurred: {e}")

View File

@@ -1,35 +1,115 @@
from core.models import ProgressReport
from core.models import ProgressReport, Task
class MarkdownBuilder:
@staticmethod
def _parse_progress(p_val: str) -> int:
if p_val == "1":
return 100
try:
return int(p_val.replace("%", ""))
except:
return 0
@staticmethod
def _get_status_badge(status: str) -> str:
s = status.upper().strip()
if "COMPLETED" in s:
return "![Completed](https://img.shields.io/badge/-COMPLETED-2ea44f?style=flat-square)"
if "DURING" in s or "PROCESS" in s:
return "![In Progress](https://img.shields.io/badge/-IN_PROGRESS-005cc5?style=flat-square)"
if "NOT STARTED" in s:
return "![Not Started](https://img.shields.io/badge/-NOT_STARTED-6a737d?style=flat-square)"
return f"`{status}`"
@staticmethod
def build_table(report: ProgressReport) -> str:
if not report.tasks:
return "_No tasks updated._"
return "_No tasks found._"
# Table header
header = "| Category | Task | Status | Progress | Notes |\n"
separator = "| :--- | :--- | :--- | :--- | :--- |\n"
header = "| Phase | ID | Task | Assignee | Timeline | Progress | Status |\n"
separator = "| :--- | :--- | :--- | :--- | :--- | :--- | :--- |\n"
rows = []
current_phase = ""
for task in report.tasks:
# Map status to professional badges
status_clean = task.status.lower().strip()
if any(x in status_clean for x in ["done", "complete", "finished"]):
badge = "![Done](https://img.shields.io/badge/-DONE-2ea44f?style=flat-square)"
elif any(x in status_clean for x in ["progress", "doing", "active"]):
badge = "![In Progress](https://img.shields.io/badge/-PROGRESS-005cc5?style=flat-square)"
elif any(x in status_clean for x in ["planned", "todo", "waiting"]):
badge = "![Planned](https://img.shields.io/badge/-PLANNED-6a737d?style=flat-square)"
elif any(x in status_clean for x in ["bug", "error", "fix"]):
badge = "![Bug](https://img.shields.io/badge/-FIXING-d73a49?style=flat-square)"
else:
badge = f"`{task.status}`" # Fallback to code text
# Progress bar simulation using HTML/Unicode if needed,
# but simple text "75%" is often cleaner.
row = f"| **{task.category}** | {task.task_name} | {badge} | `{task.progress}` | {task.notes} |"
rows.append(row)
phase_display = f"**{task.phase}**" if task.phase != current_phase else ""
current_phase = task.phase
badge = MarkdownBuilder._get_status_badge(task.status)
prog_val = MarkdownBuilder._parse_progress(task.progress_val)
filled = prog_val // 10
bar = "" * filled + "" * (10 - filled)
rows.append(
f"| {phase_display} | `{task.task_id}` | {task.task_name} | `{task.assignee}` | {task.start_date} - {task.end_date} | `{bar} {prog_val}%` | {badge} |"
)
return header + separator + "\n".join(rows)
@staticmethod
def build_team_progress(report: ProgressReport) -> str:
if not report.tasks:
return ""
phases = {}
for t in report.tasks:
if t.phase not in phases:
phases[t.phase] = []
phases[t.phase].append(t)
total_tasks = len(report.tasks)
completed = len([t for t in report.tasks if "COMPLETED" in t.status.upper()])
in_progress = len([t for t in report.tasks if "DURING" in t.status.upper()])
avg_progress = sum(MarkdownBuilder._parse_progress(t.progress_val) for t in report.tasks) / total_tasks
# Build phase rows — NO leading spaces (prevents GitHub treating them as code blocks)
phase_rows_html = ""
colors = ["#B026FF", "#33CCFF", "#FF3333", "#FFD700"]
for i, (name, t_list) in enumerate(phases.items()):
p_total = len(t_list)
p_done = len([t for t in t_list if "COMPLETED" in t.status.upper()])
p_avg = sum(MarkdownBuilder._parse_progress(t.progress_val) for t in t_list) / p_total
color = colors[i % len(colors)]
# Key fix: use textwrap.dedent-style — keep lines flush left, no indentation
phase_rows_html += (
f'<tr>'
f'<td colspan="2" style="padding-top:10px;font-size:12px;color:#EEE;">'
f'<table width="100%" border="0" cellpadding="0" cellspacing="0">'
f'<tr><td align="left">{name}</td><td align="right">{p_avg:.0f}% ({p_done}/{p_total})</td></tr>'
f'</table>'
f'<div style="width:100%;background:#222;height:6px;border-radius:3px;overflow:hidden;margin-top:4px;">'
f'<div style="width:{p_avg}%;background:{color};height:100%;"></div>'
f'</div>'
f'</td>'
f'</tr>'
)
# Entire dashboard on as few lines as possible — no indented lines
dashboard = (
'<div align="center">'
'<table width="100%" border="0" cellpadding="0" cellspacing="0" style="background:#0D1117;border:1px solid #30363D;border-radius:8px;">'
'<tr>'
'<td width="40%" align="center" valign="middle" style="padding:25px;border-right:1px solid #30363D;">'
'<img src="https://api.iconify.design/lucide/target.svg?color=%23FFD700" width="32" height="32"/>'
f'<div style="margin:10px 0 5px 0;color:#FFD700;font-size:12px;letter-spacing:1px;font-weight:bold;">SYSTEM READINESS</div>'
f'<div style="font-size:48px;font-weight:800;color:#FFF;margin:5px 0;">{avg_progress:.1f}%</div>'
'<table border="0" cellpadding="0" cellspacing="0" style="margin-top:15px;">'
'<tr>'
f'<td align="center" style="padding:0 10px;"><div style="color:#2ea44f;font-size:16px;font-weight:bold;">{completed}</div><div style="color:#8B949E;font-size:9px;">DONE</div></td>'
f'<td style="border-left:1px solid #30363D;padding:0 10px;" align="center"><div style="color:#005cc5;font-size:16px;font-weight:bold;">{in_progress}</div><div style="color:#8B949E;font-size:9px;">ACTIVE</div></td>'
f'<td style="border-left:1px solid #30363D;padding:0 10px;" align="center"><div style="color:#8B949E;font-size:16px;font-weight:bold;">{total_tasks - completed - in_progress}</div><div style="color:#8B949E;font-size:9px;">BACKLOG</div></td>'
'</tr>'
'</table>'
'</td>'
'<td width="60%" valign="top" style="padding:20px;">'
'<div style="color:#8B949E;font-size:11px;text-transform:uppercase;letter-spacing:1px;font-weight:bold;margin-bottom:10px;">Phase Breakdown</div>'
f'<table width="100%" border="0" cellpadding="0" cellspacing="0">{phase_rows_html}</table>'
'</td>'
'</tr>'
'</table>'
'</div>'
)
return dashboard

View File

@@ -4,17 +4,31 @@ class SheetMapper:
@staticmethod
def map_rows_to_report(rows: list[list]) -> ProgressReport:
tasks = []
current_phase = "General"
for row in rows:
# Ensure the row has enough columns, fill missing with empty strings
padded_row = row + [""] * (5 - len(row))
if not row or len(row) < 2:
continue
# Detect Phase row (e.g. Row 1: ['', 'Phase 1: ...'])
# Phase rows usually have few columns or column 2/3 is empty
if len(row) <= 2 or (len(row) > 2 and row[2] == ""):
if row[1] and "Phase" in row[1]:
current_phase = row[1].strip()
continue
task = Task(
category=padded_row[0],
task_name=padded_row[1],
status=padded_row[2],
progress=padded_row[3],
notes=padded_row[4]
)
tasks.append(task)
# Detect Task row (e.g. Row 2: ['', '1.1', 'Task Name', ...])
if len(row) >= 10:
task = Task(
phase=current_phase,
task_id=row[1],
task_name=row[2],
assignee=row[3],
start_date=row[4],
end_date=row[5],
progress_val=row[8],
status=row[9]
)
tasks.append(task)
return ProgressReport(tasks=tasks)

View File

@@ -5,7 +5,36 @@
<br>
<!-- START_UPDATES -->
<!-- Hệ thống tự động cập nhật có thể chèn log vào đây -->
| Phase | ID | Task | Assignee | Timeline | Progress | Status |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| **Phase 1: Game Concept & Design** | `1.1` | Finalize Game Concept & Gameplay Direction | `TEAM` | 15/05/2026 - 18/05/2026 | `██████████ 100%` | ![Completed](https://img.shields.io/badge/-COMPLETED-2ea44f?style=flat-square) |
| | `1.3` | Design UI Style, Horror Theme & Moodboard | `Phương` | 18/05/2026 - 23/05/2026 | `██████████ 100%` | ![Completed](https://img.shields.io/badge/-COMPLETED-2ea44f?style=flat-square) |
| | `1.4` | Prototype Maze Layout & Environment Concept | `Phương` | 20/05/2026 - 26/05/2026 | `██████████ 100%` | ![Completed](https://img.shields.io/badge/-COMPLETED-2ea44f?style=flat-square) |
| | `1.5` | Plan Multiplayer Structure & Networking Logic | `Duy,Phương` | 24/05/2026 - 01/06/2026 | `██████████ 100%` | ![Completed](https://img.shields.io/badge/-COMPLETED-2ea44f?style=flat-square) |
| **Phase 2: Core Multiplayer & Player Systems** | `2.1` | Setup Unity Project & Fusion Multiplayer | `Phương` | 01/06/2026 - 06/06/2026 | `██████████ 100%` | ![Completed](https://img.shields.io/badge/-COMPLETED-2ea44f?style=flat-square) |
| | `2.2` | Develop Lobby System & Room Management | `Phương` | 04/06/2026 - 11/06/2026 | `██████████ 100%` | ![Completed](https://img.shields.io/badge/-COMPLETED-2ea44f?style=flat-square) |
| | `2.3` | Develop Online Connection & Matchmaking | `Phương` | 08/06/2026 - 15/06/2026 | `██████████ 100%` | ![Completed](https://img.shields.io/badge/-COMPLETED-2ea44f?style=flat-square) |
| | `2.4` | Develop Player Spawn & Synchronization | `Duy,Phương` | 10/06/2026 - 16/06/2026 | `█████░░░░░ 50%` | ![In Progress](https://img.shields.io/badge/-IN_PROGRESS-005cc5?style=flat-square) |
| | `2.5` | Develop Random Role Assignment (Seeker / Runner) | `Duy` | 14/06/2026 - 18/06/2026 | `░░░░░░░░░░ 5%` | ![In Progress](https://img.shields.io/badge/-IN_PROGRESS-005cc5?style=flat-square) |
| | `2.6` | Develop Character Movement System | `Tuấn` | 15/06/2026 - 21/06/2026 | `░░░░░░░░░░ 0%` | ![Not Started](https://img.shields.io/badge/-NOT_STARTED-6a737d?style=flat-square) |
| | `2.7` | Develop Camera & Flashlight System | `Tuấn` | 18/06/2026 - 25/06/2026 | `░░░░░░░░░░ 0%` | ![Not Started](https://img.shields.io/badge/-NOT_STARTED-6a737d?style=flat-square) |
| | `2.8` | Develop Environment Interaction System | `Tuấn` | 22/06/2026 - 01/07/2026 | `░░░░░░░░░░ 0%` | ![Not Started](https://img.shields.io/badge/-NOT_STARTED-6a737d?style=flat-square) |
| **Phase 3: Gameplay Features & Environment** | `3.1` | Develop Trap System | `Duy` | 01/07/2026 - 09/07/2026 | `░░░░░░░░░░ 0%` | ![Not Started](https://img.shields.io/badge/-NOT_STARTED-6a737d?style=flat-square) |
| | `3.2` | Develop Item System (Battery, Mini Map, Buffs) | `Duy` | 05/07/2026 - 13/07/2026 | `░░░░░░░░░░ 0%` | ![Not Started](https://img.shields.io/badge/-NOT_STARTED-6a737d?style=flat-square) |
| | `3.3` | Develop Gameplay Mechanics & Match Flow | `Duy, Tuấn` | 10/07/2026 - 19/07/2026 | `░░░░░░░░░░ 0%` | ![Not Started](https://img.shields.io/badge/-NOT_STARTED-6a737d?style=flat-square) |
| | `3.4` | Develop Maze Generation System | `Duy` | 01/07/2026 - 13/07/2026 | `░░░░░░░░░░ 0%` | ![Not Started](https://img.shields.io/badge/-NOT_STARTED-6a737d?style=flat-square) |
| | `3.5` | Design Environment Assets & Maze Details | `Tuấn` | 10/07/2026 - 21/07/2026 | `░░░░░░░░░░ 0%` | ![Not Started](https://img.shields.io/badge/-NOT_STARTED-6a737d?style=flat-square) |
| | `3.6` | Implement Horror Lighting & Fog Effects | `Phương` | 18/07/2026 - 26/07/2026 | `░░░░░░░░░░ 0%` | ![Not Started](https://img.shields.io/badge/-NOT_STARTED-6a737d?style=flat-square) |
| | `3.7` | Develop UI HUD & Ingame Interface | `Phương` | 15/07/2026 - 25/07/2026 | `░░░░░░░░░░ 0%` | ![Not Started](https://img.shields.io/badge/-NOT_STARTED-6a737d?style=flat-square) |
| | `3.8` | Develop Win/Lose Screen & Match Result UI | `Phương` | 24/07/2026 - 01/08/2026 | `░░░░░░░░░░ 0%` | ![Not Started](https://img.shields.io/badge/-NOT_STARTED-6a737d?style=flat-square) |
| **Phase 4: Polish, Testing & Finalization** | `4.1` | Integrate Sound Effects & Background Music | `Tuấn` | 01/08/2026 - 06/08/2026 | `░░░░░░░░░░ 0%` | ![In Progress](https://img.shields.io/badge/-IN_PROGRESS-005cc5?style=flat-square) |
| | `4.2` | Multiplayer Testing & Synchronization Check | `Tuấn` | 04/08/2026 - 09/08/2026 | `░░░░░░░░░░ 0%` | ![Not Started](https://img.shields.io/badge/-NOT_STARTED-6a737d?style=flat-square) |
| | `4.3` | Gameplay Balancing & Bug Fixing | `Duy,Phương` | 07/08/2026 - 12/08/2026 | `░░░░░░░░░░ 0%` | ![Not Started](https://img.shields.io/badge/-NOT_STARTED-6a737d?style=flat-square) |
| | `4.4` | UI Polish & Optimization | `Phương` | 09/08/2026 - 13/08/2026 | `░░░░░░░░░░ 0%` | ![Not Started](https://img.shields.io/badge/-NOT_STARTED-6a737d?style=flat-square) |
| | `4.5` | Create Game Design Document (GDD) | `Duy,Phương` | 09/08/2026 - 14/08/2026 | `░░░░░░░░░░ 0%` | ![Not Started](https://img.shields.io/badge/-NOT_STARTED-6a737d?style=flat-square) |
| | `4.6` | Final Build, Demo & Presentation Preparation | `TEAM` | 12/08/2026 - 16/08/2026 | `░░░░░░░░░░ 0%` | ![Not Started](https://img.shields.io/badge/-NOT_STARTED-6a737d?style=flat-square) |
<!-- END_UPDATES -->
<table width="100%" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border: none;">

View File

@@ -2,6 +2,12 @@
> Dự án được phát triển và vận hành bởi một **Micro-Studio 3 thành viên**. Để đạt được chất lượng tiệm cận AAA-Indie trong một thời gian giới hạn, chúng tôi áp dụng mô hình chuyên môn hóa sâu (Deep Specialization) theo từng phân hệ kỹ thuật lõi.
<!-- START_PROGRESS -->
<div align="center"><table width="100%" border="0" cellpadding="0" cellspacing="0" style="background:#0D1117;border:1px solid #30363D;border-radius:8px;"><tr><td width="40%" align="center" valign="middle" style="padding:25px;border-right:1px solid #30363D;"><img src="https://api.iconify.design/lucide/target.svg?color=%23FFD700" width="32" height="32"/><div style="margin:10px 0 5px 0;color:#FFD700;font-size:12px;letter-spacing:1px;font-weight:bold;">SYSTEM READINESS</div><div style="font-size:48px;font-weight:800;color:#FFF;margin:5px 0;">29.0%</div><table border="0" cellpadding="0" cellspacing="0" style="margin-top:15px;"><tr><td align="center" style="padding:0 10px;"><div style="color:#2ea44f;font-size:16px;font-weight:bold;">7</div><div style="color:#8B949E;font-size:9px;">DONE</div></td><td style="border-left:1px solid #30363D;padding:0 10px;" align="center"><div style="color:#005cc5;font-size:16px;font-weight:bold;">3</div><div style="color:#8B949E;font-size:9px;">ACTIVE</div></td><td style="border-left:1px solid #30363D;padding:0 10px;" align="center"><div style="color:#8B949E;font-size:16px;font-weight:bold;">16</div><div style="color:#8B949E;font-size:9px;">BACKLOG</div></td></tr></table></td><td width="60%" valign="top" style="padding:20px;"><div style="color:#8B949E;font-size:11px;text-transform:uppercase;letter-spacing:1px;font-weight:bold;margin-bottom:10px;">Phase Breakdown</div><table width="100%" border="0" cellpadding="0" cellspacing="0"><tr><td colspan="2" style="padding-top:10px;font-size:12px;color:#EEE;"><table width="100%" border="0" cellpadding="0" cellspacing="0"><tr><td align="left">Phase 1: Game Concept & Design</td><td align="right">100% (4/4)</td></tr></table><div style="width:100%;background:#222;height:6px;border-radius:3px;overflow:hidden;margin-top:4px;"><div style="width:100.0%;background:#B026FF;height:100%;"></div></div></td></tr><tr><td colspan="2" style="padding-top:10px;font-size:12px;color:#EEE;"><table width="100%" border="0" cellpadding="0" cellspacing="0"><tr><td align="left">Phase 2: Core Multiplayer & Player Systems</td><td align="right">44% (3/8)</td></tr></table><div style="width:100%;background:#222;height:6px;border-radius:3px;overflow:hidden;margin-top:4px;"><div style="width:44.375%;background:#33CCFF;height:100%;"></div></div></td></tr><tr><td colspan="2" style="padding-top:10px;font-size:12px;color:#EEE;"><table width="100%" border="0" cellpadding="0" cellspacing="0"><tr><td align="left">Phase 3: Gameplay Features & Environment</td><td align="right">0% (0/8)</td></tr></table><div style="width:100%;background:#222;height:6px;border-radius:3px;overflow:hidden;margin-top:4px;"><div style="width:0.0%;background:#FF3333;height:100%;"></div></div></td></tr><tr><td colspan="2" style="padding-top:10px;font-size:12px;color:#EEE;"><table width="100%" border="0" cellpadding="0" cellspacing="0"><tr><td align="left">Phase 4: Polish, Testing & Finalization</td><td align="right">0% (0/6)</td></tr></table><div style="width:100%;background:#222;height:6px;border-radius:3px;overflow:hidden;margin-top:4px;"><div style="width:0.0%;background:#FFD700;height:100%;"></div></div></td></tr></table></td></tr></table></div>
<!-- END_PROGRESS -->
<br>
<!-- TEAM DOSSIERS (BẢNG HỒ SƠ NHÂN SỰ) -->

View File

@@ -1,30 +1,49 @@
import re
import os
from config.settings import settings
class ReadmeEditor:
def __init__(self, file_path: str = None):
self.file_path = file_path or settings.README_PATH
def update_section(self, new_content: str):
with open(self.file_path, 'r', encoding='utf-8') as f:
content = f.read()
def update_whole_readme(self, sections_content: str):
"""
Ghi đè toàn bộ nội dung README.md bằng nội dung đã gộp từ các sections.
"""
try:
# Đảm bảo thư mục cha tồn tại nếu đường dẫn là ../README.md
os.makedirs(os.path.dirname(os.path.abspath(self.file_path)), exist_ok=True)
with open(self.file_path, 'w', encoding='utf-8') as f:
f.write(sections_content)
return True
except Exception as e:
print(f"Error writing to {self.file_path}: {e}")
return False
def inject_content_between_markers(self, file_path: str, content_to_inject: str, start_marker: str, end_marker: str):
"""
Tiêm nội dung vào giữa 2 markers trong một file cụ thể.
"""
import re
if not os.path.exists(file_path):
return False
with open(file_path, 'r', encoding='utf-8') as f:
full_text = f.read()
# Regex to find content between markers
pattern = re.compile(
f"({re.escape(settings.START_MARKER)})(.*?)({re.escape(settings.END_MARKER)})",
f"({re.escape(start_marker)})(.*?)({re.escape(end_marker)})",
re.DOTALL
)
if not pattern.search(content):
print(f"Markers {settings.START_MARKER} and {settings.END_MARKER} not found in {self.file_path}")
if not pattern.search(full_text):
return False
updated_content = pattern.sub(
f"\\1\n\n{new_content}\n\n\\3",
content
updated_text = pattern.sub(
f"\\1\n\n{content_to_inject}\n\n\\3",
full_text
)
with open(self.file_path, 'w', encoding='utf-8') as f:
f.write(updated_content)
with open(file_path, 'w', encoding='utf-8') as f:
f.write(updated_text)
return True

1475
README.md

File diff suppressed because it is too large Load Diff