Created
April 5, 2021 15:06
-
-
Save tomthorogood/a2429249266070b2f5bbbaebfc2c7778 to your computer and use it in GitHub Desktop.
VCards in python+flask
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Simplistic example using send_file to send the contents generated in the service | |
@app.route("/vcard/<href_token>") | |
def get_person_vcard(request: requests.HTTPRequest, href_token: str): | |
vcard_stream = self.vcard_service.get_vcard(b64decode(href_token.encode('UTF-8')).decode('UTF-8')) | |
return send_file(vcard_stream, mimetype='text/vcard') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# I used pydantic to create models of the fields I was interestd in including in the vcard: | |
from enum import Enum | |
from typing import List, Optional | |
from pydantic import BaseModel | |
class VCardPhoneType(Enum): | |
text = "text" | |
voice = "voice" | |
fax = "fax" | |
cell = "cell" | |
pager = "pager" | |
textphone = "textphone" # TDD | |
class VCardPhone(BaseModel): | |
types: List[VCardPhoneType] | |
value: str | |
class VCard(BaseModel): | |
last_name: str | |
name_extras: List[str] = [] | |
display_name: str | |
titles: List[str] = [] | |
departments: List[str] = [] | |
email: Optional[str] | |
phones: List[VCardPhone] = [] | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# I created a VCardService that is responsible for querying data and creating a model instance. (This is only a portion of | |
# the implementation, just to show the VCard parts: | |
from flask import render_template | |
from io import BytesIO | |
class VCardService: | |
@property | |
def request_is_authenticated(self): | |
session = self.injector.get(LocalProxy) | |
return bool(session.get("uwnetid")) | |
def get_vcard(self, href: str) -> BytesIO: | |
person = self.query_person(href) # Pseudocode; cutting contextual stuff unrelated to the problem | |
name_parts = list(reversed(person.display_name.split())) | |
vcard = VCard.construct( | |
last_name=name_parts.pop(0), | |
name_extras=name_parts, | |
display_name=person.display_name, | |
) | |
self.do_other_work(vcard, person) # Pseudocode | |
# Render the vcard template with the user's data, | |
content = render_template('vcard.vcf.jinja2', **vcard.dict()) | |
# Remove all the extra lines that jinja2 leaves in there. (ugh.) | |
content = '\n'.join(filter(lambda line: bool(line.strip()), content.split('\n'))) | |
# Create a file-like object to send to the client | |
file_ = BytesIO() | |
file_.write(content.encode('UTF-8')) | |
file_.seek(0) | |
return file_ | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{# | |
Do not adhere to conventional indendation in this file. | |
Line breaks for long lines must only happen _inside_ jinja blocks. | |
field must be on a single line, with no indents. This will | |
still leave lots of vacant lines that should be cleaned up. | |
Specification: https://tools.ietf.org/html/rfc6350 | |
Model: husky_directory.models.vcard.VCard | |
#} | |
BEGIN:VCARD | |
{# woofington;dawg;husky #} | |
N:{{ last_name }};{% for e in name_extras %}{{ e }}{{ | |
';' if not loop.last else '' }} | |
{% endfor %} | |
{# Husky Dawg Woofington #} | |
FN:{{ display_name }} | |
{% for title in titles %} | |
TITLE:{{ title }} | |
{% endfor %} | |
{% for dept in departments %} | |
ORG:{{ dept }} | |
{% endfor %} | |
{% if not email is blank %} | |
{# EMAIL;type=INTERNET;type=WORK:[email protected] #} | |
EMAIL;type=INTERNET;type=WORK:{{ email }} | |
{% endif %} | |
{% for phone in phones %} | |
{# TEL;type=voice,pager:5558675309 #} | |
TEL;type={% for pt in phone['types'] %}{ {{ pt }}{{ | |
',' if not loop.last else '' }}:{{ phone['value'] }} | |
{% endfor %} {# pt in phone['types'] #} | |
{% endfor %} {# phone in phones #} | |
END:VCARD |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment