File size: 4,148 Bytes
4b66c61
bb83c5b
adeb8fd
bb83c5b
 
 
4b66c61
 
bb83c5b
 
 
4b66c61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
adeb8fd
 
4b66c61
 
bb83c5b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
from dataclasses import dataclass, asdict
from typing import List, Dict, Any, Optional, Tuple
import uuid
import json
from pathlib import Path
from config import DATA_FILE
from utils import normalize_skill_list

# -----------------------------
# Profile Data Class
# -----------------------------
@dataclass
class Profile:
    id: str
    username: str
    offers: List[str]
    wants: List[str]
    availability: str
    preferences: str
    avatar: Optional[str] = None

    @staticmethod
    def from_dict(d: Dict[str, Any]) -> "Profile":
        return Profile(
            id=str(d.get("id") or uuid.uuid4()),
            username=str(d.get("username") or "").strip(),
            offers=normalize_skill_list(
                ",".join(d.get("offers", []))
                if isinstance(d.get("offers"), list)
                else d.get("offers")
            ),
            wants=normalize_skill_list(
                ",".join(d.get("wants", []))
                if isinstance(d.get("wants"), list)
                else d.get("wants")
            ),
            availability=str(d.get("availability") or ""),
            preferences=str(d.get("preferences") or ""),
            avatar=d.get("avatar"),
        )

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)

# -----------------------------
# ProfileStore
# -----------------------------
class ProfileStore:
    def __init__(self, path: Path = DATA_FILE) -> None:
        self.path = path
        self._ensure_file()

    def _ensure_file(self) -> None:
        if not self.path.exists():
            self.path.write_text("[]", encoding="utf-8")

    def load_all(self) -> List[Profile]:
        try:
            data = json.loads(self.path.read_text(encoding="utf-8"))
            return [Profile.from_dict(d) for d in data if isinstance(d, dict)]
        except json.JSONDecodeError:
            return []

    def save_all(self, profiles: List[Profile]) -> None:
        self.path.write_text(
            json.dumps([p.to_dict() for p in profiles], indent=2, ensure_ascii=False),
            encoding="utf-8",
        )

    def find_by_username(self, username: str) -> Optional[Profile]:
        username = (username or "").strip()
        for p in self.load_all():
            if p.username.lower() == username.lower():
                return p
        return None

    def add_or_update(self, profile: Profile) -> Tuple[bool, str]:
        ok, err = self.validate_profile(profile)
        if not ok:
            return False, f"Validation failed: {err}"

        profiles = self.load_all()
        existing = next((p for p in profiles if p.username.lower() == profile.username.lower()), None)

        if existing:
            existing.offers = profile.offers
            existing.wants = profile.wants
            existing.availability = profile.availability
            existing.preferences = profile.preferences
            existing.avatar = profile.avatar
            self.save_all(profiles)
            return True, "Profile updated."

        profile.id = profile.id or str(uuid.uuid4())
        profiles.append(profile)
        self.save_all(profiles)
        return True, "Profile created."

    def delete(self, username: str) -> Tuple[bool, str]:
        username = (username or "").strip()
        profiles = self.load_all()
        profiles_new = [p for p in profiles if p.username.lower() != username.lower()]
        if len(profiles_new) == len(profiles):
            return False, "Profile not found."
        self.save_all(profiles_new)
        return True, "Profile deleted."

    @staticmethod
    def validate_profile(profile: Profile) -> Tuple[bool, Optional[str]]:
        if not profile.username:
            return False, "Username is required."
        if len(profile.username) > 60:
            return False, "Username must be 60 characters or fewer."
        if not profile.offers and not profile.wants:
            return False, "At least one offer or want is required."
        for s in profile.offers + profile.wants:
            if not s or len(s) > 120:
                return False, "Invalid skill entry."
        return True, None