sync photo and display photo

This commit is contained in:
2026-08-05 15:56:16 +02:00
parent 8a653ca395
commit a1d4c9eacb
5 changed files with 32 additions and 5 deletions
+1
View File
@@ -20,6 +20,7 @@ CREATE TABLE IF NOT EXISTS contacts (
anniversary DATE NULL,
notes TEXT NULL,
photo_base64 LONGTEXT NULL,
photo_url VARCHAR(2048) NULL,
emails JSON NULL,
phones JSON NULL,
addresses JSON NULL,
+4 -4
View File
@@ -76,7 +76,7 @@ def list_contacts(
cur.execute(
f"""SELECT id, account, uid, full_name, given_name, family_name, organization,
job_title, birthday, notes, emails, phones, addresses, urls, social_profiles, categories, updated_at
job_title, birthday, notes, photo_url, emails, phones, addresses, urls, social_profiles, categories, updated_at
FROM contacts {where_clause}{search_clause}
ORDER BY full_name
LIMIT %s OFFSET %s""",
@@ -98,7 +98,7 @@ def get_contact(contact_id: int, current_user: str = Depends(get_current_user)):
with conn.cursor() as cur:
cur.execute(
f"""SELECT id, account, uid, full_name, given_name, family_name, organization,
job_title, birthday, notes, emails, phones, addresses, urls, social_profiles, categories, updated_at
job_title, birthday, notes, photo_url, emails, phones, addresses, urls, social_profiles, categories, updated_at
FROM contacts {where_clause} {id_clause}""",
params + [contact_id],
)
@@ -121,7 +121,7 @@ def birthdays_today(current_user: str = Depends(get_current_user)):
with conn.cursor() as cur:
cur.execute(
f"""SELECT id, account, uid, full_name, given_name, family_name, organization,
job_title, birthday, notes, emails, phones, addresses, urls, social_profiles, categories, updated_at
job_title, birthday, notes, photo_url, emails, phones, addresses, urls, social_profiles, categories, updated_at
FROM contacts {where_clause} {month_day_clause}
ORDER BY full_name""",
params + [today.month, today.day],
@@ -293,7 +293,7 @@ def web_contact(
with conn.cursor() as cur:
cur.execute(
f"""SELECT id, account, uid, full_name, given_name, family_name, organization,
job_title, birthday, notes, emails, phones, addresses, urls, social_profiles, categories, updated_at
job_title, birthday, notes, photo_url, emails, phones, addresses, urls, social_profiles, categories, updated_at
FROM contacts {where_clause} {id_clause}""",
params + [contact_id],
)
+1
View File
@@ -15,6 +15,7 @@ class ContactOut(BaseModel):
job_title: str | None
birthday: date | None
notes: str | None
photo_url: str | None
emails: list
phones: list
addresses: list
+11
View File
@@ -65,6 +65,14 @@
border-bottom: 1px solid #eee;
}
.contact-photo {
width: 80px;
height: 80px;
border-radius: 50%;
object-fit: cover;
flex-shrink: 0;
}
.contact-name {
font-size: 1.5rem;
font-weight: 600;
@@ -207,6 +215,9 @@
{% if is_admin and contact.account %}
<span class="badge badge-account">{{ contact.account }}</span>
{% endif %}
{% if contact.photo_url %}
<img src="{{ contact.photo_url }}" alt="Foto" class="contact-photo" onerror="this.style.display='none'">
{% endif %}
</div>
{% if contact.birthday %}
+15 -1
View File
@@ -86,6 +86,19 @@ def parse_vcard(raw_text: str, account: str, etag: str | None = None) -> dict |
categories = [c.strip() for c in vcard.categories.value] if hasattr(vcard, "categories") else []
photo_url = None
photo_base64 = None
if hasattr(vcard, "photo"):
photo_obj = vcard.photo
params = getattr(photo_obj, "params", {})
encoding = params.get("ENCODING") or params.get("encoding")
value = photo_obj.value
if isinstance(value, str) and value.startswith("http"):
photo_url = value
elif encoding and str(encoding).upper() == "B" and isinstance(value, bytes):
import base64
photo_base64 = base64.b64encode(value).decode("ascii")
birthday = str(vcard.bday.value)[:10] if hasattr(vcard, "bday") else None
org = None
@@ -110,7 +123,8 @@ def parse_vcard(raw_text: str, account: str, etag: str | None = None) -> dict |
"birthday": birthday,
"anniversary": None,
"notes": _scalar(_get(vcard, "note")),
"photo_base64": None,
"photo_base64": photo_base64,
"photo_url": photo_url,
"emails": json.dumps(emails, ensure_ascii=False),
"phones": json.dumps(phones, ensure_ascii=False),
"addresses": json.dumps(addresses, ensure_ascii=False),