-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathabstractions.py
More file actions
133 lines (103 loc) · 3.58 KB
/
abstractions.py
File metadata and controls
133 lines (103 loc) · 3.58 KB
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
121
122
123
124
125
126
127
128
129
130
131
132
133
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Iterable
# =============================================================================
# Pet Ingestor Interface
# =============================================================================
@dataclass
class AdoptablePet:
"""Represents a pet available for adoption."""
name: str
species: str # "dog" or "cat"
breed: str
location: str
description: str = ""
adoption_url: str | None = None
image_url: str | None = None
age_string: str | None = None
sex: str | None = None
size_group: str | None = None
pet_id: str | None = None
class PetSource(ABC):
"""Interface for fetching pets from various adoption APIs."""
@property
@abstractmethod
def source_name(self) -> str:
"""Return the name of the pet source."""
...
@abstractmethod
def fetch_pets(self) -> Iterable[AdoptablePet]:
"""Fetch available pets from the source."""
...
# =============================================================================
# Social Media Poster Interface
# =============================================================================
@dataclass
class Post:
"""Represents a social media post about an adoptable pet."""
text: str
image_url: str | None = None
link: str | None = None
alt_text: str | None = None # For image accessibility
tags: list[str] = field(default_factory=list)
@dataclass
class PostResult:
"""Result of attempting to publish a post."""
success: bool
post_id: str | None = None
post_url: str | None = None
error_message: str | None = None
class SocialPoster(ABC):
"""
Abstract base class for social media platform implementations.
Concrete implementations should inherit from this class and implement
the abstract methods for their specific platform (e.g., Bluesky, Instagram).
"""
@property
@abstractmethod
def platform_name(self) -> str:
"""Return the name of the social media platform."""
...
@abstractmethod
def authenticate(self) -> bool:
"""
Authenticate with the platform.
Returns:
True if authentication was successful, False otherwise.
"""
...
@abstractmethod
def publish(self, post: Post) -> PostResult:
"""
Publish a post to the platform.
Args:
post: The post to publish.
Returns:
PostResult indicating success/failure and relevant details.
"""
...
def is_authenticated(self) -> bool:
"""Check if currently authenticated. Override if platform supports this."""
return False
def format_post(self, pet: AdoptablePet) -> Post:
"""
Create a Post from an AdoptablePet.
Override this method to customize post formatting for specific platforms.
"""
text = f"Meet {pet.name}! This adorable {pet.breed} {pet.species} is looking for a forever home in {pet.location}."
if pet.description:
text += f"\n\n{pet.description}"
if pet.adoption_url:
text += f"\n\nAdopt {pet.name}: {pet.adoption_url}"
return Post(
text=text,
image_url=pet.image_url,
link=pet.adoption_url,
alt_text=f"Photo of {pet.name}, a {pet.breed} {pet.species} available for adoption",
tags=[
"adoptdontshop",
"rescue",
pet.species,
pet.breed.lower().replace(" ", ""),
],
)