Spaces:
Sleeping
Sleeping
Download integrations/sheets.py from yania-n/ProjectCraft: direct link, hf CLI and curl.
- Browser
- Download file 4.67 kB
-
https://huggingface.co/spaces/yania-n/ProjectCraft/resolve/main/integrations/sheets.py
- Command line
-
hf download hf://spaces/yania-n/ProjectCraft/integrations/sheets.py
-
curl -L -o sheets.py https://huggingface.co/spaces/yania-n/ProjectCraft/resolve/main/integrations/sheets.py
4.67 kB
| """Google Sheets integration — exports RACI and Risk Register as shareable spreadsheets.""" | |
| import asyncio | |
| import os | |
| from core.planner import ERROR_MESSAGES, ProjectPlan, RACIEntry | |
| SCOPES = ["https://www.googleapis.com/auth/spreadsheets"] | |
| def _build_service(): | |
| """Build Google Sheets API service. Return None if credentials unavailable.""" | |
| try: | |
| from google.oauth2.credentials import Credentials | |
| from googleapiclient.discovery import build | |
| except ImportError: | |
| return None | |
| client_id = os.environ.get("GOOGLE_CLIENT_ID") | |
| client_secret = os.environ.get("GOOGLE_CLIENT_SECRET") | |
| if not client_id or not client_secret: | |
| return None | |
| try: | |
| credentials = Credentials( | |
| token=None, | |
| client_id=client_id, | |
| client_secret=client_secret, | |
| scopes=SCOPES, | |
| ) | |
| return build("sheets", "v4", credentials=credentials) | |
| except Exception: | |
| return None | |
| def _raci_rows(plan: ProjectPlan) -> list[list]: | |
| """Build rows for RACI sheet using plan.raci entries.""" | |
| # Collect all unique teams in order of appearance | |
| teams: list[str] = [] | |
| for entry in plan.raci: | |
| for team in (entry.responsible + [entry.accountable] + entry.consulted + entry.informed): | |
| if team and team not in teams: | |
| teams.append(team) | |
| header = ["Task"] + teams | |
| rows = [header] | |
| for entry in plan.raci: | |
| row = [entry.task] | |
| for team in teams: | |
| roles: list[str] = [] | |
| if team in entry.responsible: | |
| roles.append("R") | |
| if team == entry.accountable: | |
| roles.append("A") | |
| if team in entry.consulted: | |
| roles.append("C") | |
| if team in entry.informed: | |
| roles.append("I") | |
| row.append("/".join(roles) if roles else "") | |
| rows.append(row) | |
| return rows | |
| def _risks_rows(plan: ProjectPlan) -> list[list]: | |
| """Build rows for Risk Register using plan.risks entries.""" | |
| header = ["Phase", "Description", "Likelihood", "Impact", "Mitigation"] | |
| rows = [header] | |
| for risk in plan.risks: | |
| rows.append([ | |
| risk.phase_id or "General", | |
| risk.description, | |
| risk.likelihood, | |
| risk.impact, | |
| risk.mitigation, | |
| ]) | |
| return rows | |
| async def export_raci(plan: ProjectPlan) -> dict: | |
| """ | |
| Create a new Google Spreadsheet with RACI data. | |
| Returns {"success": bool, "url": str | None, "error": str | None} | |
| """ | |
| service = _build_service() | |
| if service is None: | |
| return {"success": False, "url": None, "error": ERROR_MESSAGES["google_auth"]} | |
| try: | |
| rows = _raci_rows(plan) | |
| def _create() -> str: | |
| body = {"properties": {"title": "ProjectCraft — RACI Matrix"}} | |
| spreadsheet = service.spreadsheets().create(body=body, fields="spreadsheetId").execute() | |
| sheet_id = spreadsheet["spreadsheetId"] | |
| service.spreadsheets().values().update( | |
| spreadsheetId=sheet_id, | |
| range="Sheet1!A1", | |
| valueInputOption="RAW", | |
| body={"values": rows}, | |
| ).execute() | |
| return f"https://docs.google.com/spreadsheets/d/{sheet_id}" | |
| url = await asyncio.to_thread(_create) | |
| return {"success": True, "url": url, "error": None} | |
| except Exception as exc: | |
| return {"success": False, "url": None, "error": str(exc)} | |
| async def export_risks(plan: ProjectPlan) -> dict: | |
| """ | |
| Create a new Google Spreadsheet with Risk Register data. | |
| Returns {"success": bool, "url": str | None, "error": str | None} | |
| """ | |
| service = _build_service() | |
| if service is None: | |
| return {"success": False, "url": None, "error": ERROR_MESSAGES["google_auth"]} | |
| try: | |
| rows = _risks_rows(plan) | |
| def _create() -> str: | |
| body = {"properties": {"title": "ProjectCraft — Risk Register"}} | |
| spreadsheet = service.spreadsheets().create(body=body, fields="spreadsheetId").execute() | |
| sheet_id = spreadsheet["spreadsheetId"] | |
| service.spreadsheets().values().update( | |
| spreadsheetId=sheet_id, | |
| range="Sheet1!A1", | |
| valueInputOption="RAW", | |
| body={"values": rows}, | |
| ).execute() | |
| return f"https://docs.google.com/spreadsheets/d/{sheet_id}" | |
| url = await asyncio.to_thread(_create) | |
| return {"success": True, "url": url, "error": None} | |
| except Exception as exc: | |
| return {"success": False, "url": None, "error": str(exc)} | |