Added VCard format support

This commit is contained in:
Scott Idem
2022-07-06 17:53:49 -04:00
parent 0a41699f5b
commit 462acd3c7f
3 changed files with 197 additions and 1 deletions

View File

@@ -18,7 +18,7 @@ from . import config
from app.log import log, logging from app.log import log, logging
# Import the routers here first: # Import the routers here first:
from app.routers import api_crud, api, importing, sql, account, activity_log, address, archive, archive_content, contact, cont_edu_cert, cont_edu_cert_person, data_store, event, event_badge, event_badge_template, event_device, event_exhibit, event_exhibit_tracking, event_file, event_importing, event_location, event_person, event_person_detail, event_person_tracking, event_presentation, event_presenter, event_registration, event_session, flask_cfg, hosted_file, journal, journal_entry, log_client_viewing, lookup, membership_cfg, membership_group, membership_person_group, membership_person, membership_person_profile, membership_type, membership_person_type, order, order_v3, order_line, order_cart, organization, page, person, person_user, post, post_comment, product, site, site_domain, user, websockets, e_cvent, c_idaa, e_impexium from app.routers import api_crud, api, importing, sql, account, activity_log, address, archive, archive_content, contact, cont_edu_cert, cont_edu_cert_person, data_store, event, event_badge, event_badge_template, event_device, event_exhibit, event_exhibit_tracking, event_file, event_importing, event_location, event_person, event_person_detail, event_person_tracking, event_presentation, event_presenter, event_registration, event_session, flask_cfg, hosted_file, journal, journal_entry, log_client_viewing, lookup, membership_cfg, membership_group, membership_person_group, membership_person, membership_person_profile, membership_type, membership_person_type, order, order_v3, order_line, order_cart, organization, page, person, person_user, post, post_comment, product, qr, site, site_domain, user, websockets, e_cvent, c_idaa, e_impexium
from app.db_sql import db from app.db_sql import db
@@ -314,6 +314,10 @@ app.include_router(
# prefix='/product', # prefix='/product',
tags=['Product'], tags=['Product'],
) )
app.include_router(
qr.router,
tags=['QR'],
)
app.include_router( app.include_router(
site.router, site.router,
prefix='/site', prefix='/site',

View File

@@ -75,6 +75,9 @@ class Event_Badge_Base(BaseModel):
email: Optional[str] email: Optional[str]
phone: Optional[str]
display_phone: Optional[str]
address_line_1: Optional[str] address_line_1: Optional[str]
address_line_2: Optional[str] address_line_2: Optional[str]
address_line_3: Optional[str] address_line_3: Optional[str]

189
app/routers/qr.py Normal file
View File

@@ -0,0 +1,189 @@
import os, pathlib, qrcode
from fastapi import APIRouter, Body, Depends, File, Form, Header, HTTPException, Query, Response, status, UploadFile
from fastapi.responses import FileResponse
from pydantic import BaseModel, EmailStr, Field
from typing import Dict, List, Optional, Set, Union
from app.lib_general import log, logging, common_route_params, Common_Route_Params
from app.config import settings
from app.db_sql import redis_lookup_id_random
# from app.methods.hosted_file_methods import create_hosted_file_obj, load_hosted_file_obj, save_file, create_hosted_file_link
# from app.models.hosted_file_models import Hosted_File_Base
from app.models.response_models import Resp_Body_Base, mk_resp
router = APIRouter()
# ### BEGIN ### API QR ### get_qr() ###
# Updated 2022-07-06
@router.get('/qr/{account_id}/{qr_id}', response_model=Resp_Body_Base)
@router.get('/qr/{qr_id}', response_model=Resp_Body_Base)
async def get_qr(
# account_id: str = Query(..., min_length=11, max_length=22),
qr_id: str = Query(..., min_length=11, max_length=22),
account_id: str = Query(None, min_length=11, max_length=22),
regen: bool = False,
qr_type: str = Query(None, min_length=1, max_length=10),
filename: str = Query(None, min_length=4, max_length=100),
n: str = Query('', max_length=100),
fn: str = Query('', max_length=100),
org: str = Query('', max_length=255),
url: str = Query('', max_length=500),
email: str = Query('', max_length=255),
tel: str = Query('', max_length=25),
adr: str = Query('', max_length=200),
adr_poa: str = Query('', max_length=100), # Address Post Office Address
adr_ext: str = Query('', max_length=100), # Address Extended Address
adr_str: str = Query('', max_length=100), # Address Street
adr_loc: str = Query('', max_length=100), # Address Locality
adr_reg: str = Query('', max_length=100), # Address Region
adr_postal: str = Query('', max_length=100), # Address Postal Code
adr_country: str = Query('', max_length=100), # Address Country
obj_type: str = Query(None, max_length=100),
obj_id: str = Query(None, max_length=100),
key: str = Query(None, max_length=200),
val: str = Query(None, max_length=1000),
js: str = Query(None, max_length=1000),
str: str = Query(None, max_length=1000),
return_file: bool = False,
# commons: Common_Route_Params = Depends(common_route_params),
x_account_id: str = Header(None, min_length=11, max_length=22),
response: Response = Response,
):
log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
log.debug(locals())
# if commons.x_account_id_random:
# account_id_random = commons.x_account_id_random
if x_account_id:
account_id_random = x_account_id
elif account_id:
account_id_random = account_id
# ### SECTION ### Secondary data validation
if account_id := redis_lookup_id_random(record_id_random=account_id_random, table_name='account'): pass
else: return mk_resp(data=None, status_code=404, response=response, status_message='The account ID was invalid or not found.')
hosted_tmp_root_path = settings.PATH_HOSTED_TMP_ROOT
log.info(f'Hosted Tmp Root Path: {hosted_tmp_root_path}')
if os.path.exists(hosted_tmp_root_path):
log.info(f'Hosted tmp root path found: {hosted_tmp_root_path}')
else:
log.error(f'Hosted tmp root path not found: {hosted_tmp_root_path}')
return mk_resp(data=False, status_code=400, response=response) # Bad Request
hosted_tmp_root_path_w_subdir = os.path.join(hosted_tmp_root_path, account_id_random)
if os.path.exists(hosted_tmp_root_path_w_subdir):
log.info(f'Hosted tmp root path with subdirectory found: {hosted_tmp_root_path_w_subdir}')
else:
log.info(f'Hosted tmp root path with subdirectory not found: {hosted_tmp_root_path_w_subdir}; Creating...')
pathlib.Path(hosted_tmp_root_path_w_subdir).mkdir(parents=True, exist_ok=True)
hosted_tmp_root_path_w_subdir_qr_id = os.path.join(hosted_tmp_root_path_w_subdir, qr_id)
if regen: pass
elif os.path.exists(hosted_tmp_root_path_w_subdir_qr_id):
if return_file or filename:
if not filename:
filename = f'qr_{account_id_random}.png'
return FileResponse(hosted_tmp_root_path_w_subdir_qr_id, filename=filename)
else:
return mk_resp(data=True, response=response)
else:
log.error(f'The QR file was not found on the server. Account ID: {account_id_random}; QR ID: {qr_id}; Going to create a new one.')
# return mk_resp(data=False, status_code=404, response=commons.response, status_message='The QR file was not found on the server.') # Not Found
qr_data = None
if qr_type:
log.info(f'Found QR Type: {qr_type}')
if qr_type == 'mecard':
# Use MeCard (QR code) format
# https://en.wikipedia.org/wiki/MeCard_(QR_code)
qr_data = f'MECARD:N:{n};EMAIL:{email};ADR:{adr};;'
#qr_data = 'MECARD:N:'+event_badge.given_name+' '+event_badge.family_name+';EMAIL:'+event_badge.email+';ADR:'+event_badge.city+' '+event_badge.state_province+' '+event_badge.country+';;'
log.debug(qr_data)
elif qr_type == 'vcard':
# Use VCard format
# https://en.wikipedia.org/wiki/VCard
# qr_data = f'BEGIN:VCARD VERSION:3.0\nN:{n}\nFN:{fn}\nORG:{org}\nURL:{url}\nEMAIL:{email}\nTEL:TYPE=VOICE:{tel}\nADR:TYPE=postal:{adr_poa};{adr_ext};{adr_str};{adr_loc};{adr_reg};{adr_postal};{adr_country}\nEND:VCARD'
# qr_data = f'BEGIN:VCARD VERSION:3.0\nN:{n}\nFN:{fn}\nORG:{org}\nURL:{url}\nEMAIL:{email}\nTEL:{tel}\nADR:{adr_poa};{adr_ext};{adr_str};{adr_loc};{adr_reg};{adr_postal};{adr_country}\nEND:VCARD'
qr_data = f'BEGIN:VCARD VERSION:3.0\nN:{n}\nFN:{fn}\nORG:{org}\nEMAIL:{email}\n'
if url:
qr_data = f'{qr_data}URL:{url}\n'
if tel:
qr_data = f'{qr_data}TEL:{tel}\n'
if adr_loc:
qr_data = f'{qr_data}ADR:{adr_poa};{adr_ext};{adr_str};{adr_loc};{adr_reg};{adr_postal};{adr_country}\n'
qr_data = f'{qr_data}END:VCARD'
log.debug(qr_data)
elif qr_type == 'obj':
qr_data = f'OBJ:ot:{obj_type},oi:{obj_id}' # NOTE: These are not quoted values "". Key value below still is quoted.
log.debug(qr_data)
elif qr_type == 'kv':
qr_data = f'KV:k:"{key}",v:"{val}"'
elif qr_type == 'js':
# qr_data_dict = {'example':'example JSON'}
# json_str = json.dumps(qr_data_dict)
# qr_data = f'JS:{json_str}'
qr_data = f'JS:{js}'
elif qr_type == 'str':
qr_data = str
else:
# json_str = request.json()
# string = json_str.get('STR', None)
qr_data = None
log.debug(qr_data)
# Create qr code instance
qr = qrcode.QRCode(
version = 1,
error_correction = qrcode.constants.ERROR_CORRECT_M,
box_size = 10,
border = 1,
)
# The data that you want to store
# Add data
qr.add_data(qr_data)
qr.make(fit=True)
# Create an image from the QR Code instance
img = qr.make_image()
# Save it somewhere, change the extension as needed:
img.save(f'{hosted_tmp_root_path_w_subdir_qr_id}') # .png ???
if os.path.exists(hosted_tmp_root_path_w_subdir_qr_id):
if return_file or filename:
if not filename:
filename = f'qr_{account_id_random}.png'
return FileResponse(hosted_tmp_root_path_w_subdir_qr_id, filename=filename)
else:
return mk_resp(data=True, response=response)
else:
log.error(f'The QR file was not found on the server and could not be created. Account ID: {account_id_random}; QR ID: {qr_id}')
return mk_resp(data=False, status_code=404, response=response, status_message='The QR file was not found on the server and could not be created.') # Not Found
# ### END ### API QR ### get_qr() ###