"""vCard 3.0 erzeugen - das Format, das iOS beim Antippen zuverlaessig oeffnet.""" from datetime import datetime _ESCAPES = (("\\", "\\\\"), (";", "\\;"), (",", "\\,"), ("\n", "\\n")) def _escape(value) -> str: text = str(value) for char, replacement in _ESCAPES: text = text.replace(char, replacement) return text def _line(name: str, value) -> list: return [name + ":" + _escape(value)] if value else [] def _display_name(card: dict) -> str: joined = " ".join(filter(None, (card.get("first_name"), card.get("last_name")))) return joined or card.get("company") or "Unbenannt" def _note(card: dict) -> str: parts = [] if card.get("notes"): parts.append(card["notes"]) stamp = card.get("captured_at") or card.get("created_at") scanned = "" if stamp: try: when = datetime.fromisoformat(stamp).strftime("%d.%m.%Y um %H:%M") scanned = "Gescannt am " + when except ValueError: scanned = "Gescannt am " + str(stamp) if card.get("place"): scanned = (scanned + " in " if scanned else "Gescannt in ") + card["place"] if scanned: parts.append(scanned) return "\n".join(parts) def build(card: dict) -> str: lines = ["BEGIN:VCARD", "VERSION:3.0"] lines.append( "N:{};{};;;".format( _escape(card.get("last_name") or ""), _escape(card.get("first_name") or "") ) ) lines.append("FN:" + _escape(_display_name(card))) lines += _line("ORG", card.get("company")) lines += _line("TITLE", card.get("job_title")) if card.get("email"): lines.append("EMAIL;TYPE=INTERNET,WORK:" + _escape(card["email"])) if card.get("phone"): lines.append("TEL;TYPE=WORK,VOICE:" + _escape(card["phone"])) if card.get("mobile"): lines.append("TEL;TYPE=CELL:" + _escape(card["mobile"])) if card.get("fax"): lines.append("TEL;TYPE=WORK,FAX:" + _escape(card["fax"])) if card.get("website"): lines.append("URL:" + _escape(card["website"])) if any(card.get(k) for k in ("street", "postal_code", "city", "country")): lines.append( "ADR;TYPE=WORK:;;{};{};;{};{}".format( _escape(card.get("street") or ""), _escape(card.get("city") or ""), _escape(card.get("postal_code") or ""), _escape(card.get("country") or ""), ) ) if card.get("lat") is not None and card.get("lon") is not None: lines.append("GEO:{};{}".format(card["lat"], card["lon"])) note = _note(card) if note: lines.append("NOTE:" + _escape(note)) lines.append("END:VCARD") return "\r\n".join(lines) + "\r\n" def filename(card: dict) -> str: base = _display_name(card).replace(" ", "-") safe = "".join(c for c in base if c.isalnum() or c in "-_") return (safe or "kontakt") + ".vcf"