Update
This commit is contained in:
14
.idea/.idea.BABA_YAGA/.idea/workspace.xml
generated
14
.idea/.idea.BABA_YAGA/.idea/workspace.xml
generated
@@ -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>
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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 ""
|
||||
if "DURING" in s or "PROCESS" in s:
|
||||
return ""
|
||||
if "NOT STARTED" in s:
|
||||
return ""
|
||||
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 = ""
|
||||
elif any(x in status_clean for x in ["progress", "doing", "active"]):
|
||||
badge = ""
|
||||
elif any(x in status_clean for x in ["planned", "todo", "waiting"]):
|
||||
badge = ""
|
||||
elif any(x in status_clean for x in ["bug", "error", "fix"]):
|
||||
badge = ""
|
||||
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
|
||||
@@ -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)
|
||||
|
||||
@@ -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%` |  |
|
||||
| | `1.3` | Design UI Style, Horror Theme & Moodboard | `Phương` | 18/05/2026 - 23/05/2026 | `██████████ 100%` |  |
|
||||
| | `1.4` | Prototype Maze Layout & Environment Concept | `Phương` | 20/05/2026 - 26/05/2026 | `██████████ 100%` |  |
|
||||
| | `1.5` | Plan Multiplayer Structure & Networking Logic | `Duy,Phương` | 24/05/2026 - 01/06/2026 | `██████████ 100%` |  |
|
||||
| **Phase 2: Core Multiplayer & Player Systems** | `2.1` | Setup Unity Project & Fusion Multiplayer | `Phương` | 01/06/2026 - 06/06/2026 | `██████████ 100%` |  |
|
||||
| | `2.2` | Develop Lobby System & Room Management | `Phương` | 04/06/2026 - 11/06/2026 | `██████████ 100%` |  |
|
||||
| | `2.3` | Develop Online Connection & Matchmaking | `Phương` | 08/06/2026 - 15/06/2026 | `██████████ 100%` |  |
|
||||
| | `2.4` | Develop Player Spawn & Synchronization | `Duy,Phương` | 10/06/2026 - 16/06/2026 | `█████░░░░░ 50%` |  |
|
||||
| | `2.5` | Develop Random Role Assignment (Seeker / Runner) | `Duy` | 14/06/2026 - 18/06/2026 | `░░░░░░░░░░ 5%` |  |
|
||||
| | `2.6` | Develop Character Movement System | `Tuấn` | 15/06/2026 - 21/06/2026 | `░░░░░░░░░░ 0%` |  |
|
||||
| | `2.7` | Develop Camera & Flashlight System | `Tuấn` | 18/06/2026 - 25/06/2026 | `░░░░░░░░░░ 0%` |  |
|
||||
| | `2.8` | Develop Environment Interaction System | `Tuấn` | 22/06/2026 - 01/07/2026 | `░░░░░░░░░░ 0%` |  |
|
||||
| **Phase 3: Gameplay Features & Environment** | `3.1` | Develop Trap System | `Duy` | 01/07/2026 - 09/07/2026 | `░░░░░░░░░░ 0%` |  |
|
||||
| | `3.2` | Develop Item System (Battery, Mini Map, Buffs) | `Duy` | 05/07/2026 - 13/07/2026 | `░░░░░░░░░░ 0%` |  |
|
||||
| | `3.3` | Develop Gameplay Mechanics & Match Flow | `Duy, Tuấn` | 10/07/2026 - 19/07/2026 | `░░░░░░░░░░ 0%` |  |
|
||||
| | `3.4` | Develop Maze Generation System | `Duy` | 01/07/2026 - 13/07/2026 | `░░░░░░░░░░ 0%` |  |
|
||||
| | `3.5` | Design Environment Assets & Maze Details | `Tuấn` | 10/07/2026 - 21/07/2026 | `░░░░░░░░░░ 0%` |  |
|
||||
| | `3.6` | Implement Horror Lighting & Fog Effects | `Phương` | 18/07/2026 - 26/07/2026 | `░░░░░░░░░░ 0%` |  |
|
||||
| | `3.7` | Develop UI HUD & Ingame Interface | `Phương` | 15/07/2026 - 25/07/2026 | `░░░░░░░░░░ 0%` |  |
|
||||
| | `3.8` | Develop Win/Lose Screen & Match Result UI | `Phương` | 24/07/2026 - 01/08/2026 | `░░░░░░░░░░ 0%` |  |
|
||||
| **Phase 4: Polish, Testing & Finalization** | `4.1` | Integrate Sound Effects & Background Music | `Tuấn` | 01/08/2026 - 06/08/2026 | `░░░░░░░░░░ 0%` |  |
|
||||
| | `4.2` | Multiplayer Testing & Synchronization Check | `Tuấn` | 04/08/2026 - 09/08/2026 | `░░░░░░░░░░ 0%` |  |
|
||||
| | `4.3` | Gameplay Balancing & Bug Fixing | `Duy,Phương` | 07/08/2026 - 12/08/2026 | `░░░░░░░░░░ 0%` |  |
|
||||
| | `4.4` | UI Polish & Optimization | `Phương` | 09/08/2026 - 13/08/2026 | `░░░░░░░░░░ 0%` |  |
|
||||
| | `4.5` | Create Game Design Document (GDD) | `Duy,Phương` | 09/08/2026 - 14/08/2026 | `░░░░░░░░░░ 0%` |  |
|
||||
| | `4.6` | Final Build, Demo & Presentation Preparation | `TEAM` | 12/08/2026 - 16/08/2026 | `░░░░░░░░░░ 0%` |  |
|
||||
|
||||
<!-- END_UPDATES -->
|
||||
|
||||
<table width="100%" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border: none;">
|
||||
|
||||
@@ -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Ự) -->
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user