Skip to content

Commit

Permalink
update helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
Tim Santor committed Sep 4, 2024
1 parent 701b88b commit ed30ec5
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 79 deletions.
57 changes: 23 additions & 34 deletions {{cookiecutter.project_slug}}/helpers/ip.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import functools
import ipaddress
import logging
from abc import ABC
from abc import abstractmethod

import requests
from django.conf import settings
Expand All @@ -13,14 +11,31 @@
logger = logging.getLogger(__name__)


class GeoLocationAPI(ABC):
@abstractmethod
def get_ip_info(self, ip_address: str) -> dict:
pass
@functools.lru_cache(maxsize=1024)
def get_ip_info(ip_address: str) -> dict:
"""
Use 3rd party API to obtain Geolocation data from a given IP.
Args:
ip_address (str): The IP address to get information about.
class IPStackAPI(GeoLocationAPI):
def get_ip_info(self, ip_address: str) -> dict:
Returns:
dict: A dictionary containing the information about the IP address,
or an empty dictionary if the IP address is internal or the API
response is not valid JSON.
"""

if not settings.IPSTACK_ACCESS_KEY:
logger.warning("IPSTACK_ACCESS_KEY is not set.")
return {}

merged_ips = list(set(settings.INTERNAL_IPS) | set(settings.IPSTACK_IGNORE_IPS))

if not ip_address or ip_address in merged_ips or is_private_ip(ip_address):
logger.warning("IP address is internal or ignored: %s", ip_address)
return {}

try:
params = {
"fields": "main",
"hostname": 1,
Expand All @@ -36,32 +51,6 @@ def get_ip_info(self, ip_address: str) -> dict:
return r.json()
except JSONDecodeError:
return {}


@functools.lru_cache(maxsize=1024)
def get_ip_info(ip_address: str, api: GeoLocationAPI) -> dict:
"""
Use 3rd party API to obtain Geolocation data from a given IP.
Args:
ip_address (str): The IP address to get information about.
api (GeoLocationAPI): The API to use to get the IP info.
Returns:
dict: A dictionary containing the information about the IP address,
or an empty dictionary if the IP address is internal or the API response
is not valid JSON.
"""
if (
not ip_address
or ip_address in settings.INTERNAL_IPS
or is_private_ip(ip_address)
):
logger.warning("IP address is internal: %s", ip_address)
return {}

try:
return api.get_ip_info(ip_address)
except Exception as e: # noqa: BLE001
logger.warning("Could not get IP info from API: %s", str(e))
return {}
Expand Down
59 changes: 15 additions & 44 deletions {{cookiecutter.project_slug}}/helpers/qr.py
Original file line number Diff line number Diff line change
@@ -1,65 +1,36 @@
import base64
import logging
from io import BytesIO

import qrcode
from django.core.files.base import ContentFile
from qrcode.image.svg import SvgPathFillImage
from qrcode.main import QRCode

logger = logging.getLogger()
logger = logging.getLogger(__name__)


def make_qr_code(data: str, box_size: int = 10, border: int = 1) -> SvgPathFillImage:
"""
Generate a QR code, does not save the image.
Args:
data (str): The data to be encoded in the QR code.
box_size (int, optional): The size of each box in the QR code grid.
Defaults to 10.
border (int, optional): The thickness of the border. Defaults to 1.
Returns:
SvgPathFillImage: The generated QR code as an SVG image.
"""
qr = QRCode(
def make_qr_code(data, box_size=10, border=0):
"""Generate a QR code, does not save the image."""
qr = qrcode.QRCode(
version=None,
error_correction=qrcode.constants.ERROR_CORRECT_H,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=box_size,
border=border,
image_factory=SvgPathFillImage,
)
qr.add_data(data)
qr.make(fit=True)
return qr.make_image(fill_color="black", back_color="white")


def _in_memory_qr(data: str) -> BytesIO:
"""
Save file to in-memory bytes buffer, not to disk and return the buffer.
Args:
data (str): The data to be encoded in the QR code.
Returns:
BytesIO: The in-memory bytes buffer containing the QR code.
"""
def make_in_memory_qr(data):
"""Save file to in-memory bytes buffer, not to disk and return the buffer."""
img = make_qr_code(data)
bytes_io = BytesIO()
img.save(bytes_io)
img.save(bytes_io, format="PNG")
return bytes_io


def make_qr_content_file(data: str, filename: str) -> ContentFile:
"""
Return content file for use in Django.
Args:
data (str): The data to be encoded in the QR code.
filename (str): The name of the file.
Returns:
ContentFile: The content file containing the QR code.
"""
bytes_io = _in_memory_qr(data)
return ContentFile(bytes_io.getvalue(), name=f"{filename}")
def make_qr_code_b64(data):
"""Return a Base64 encoded QR code for use in HTML. NOTE: Base64 encoded
images are NOT well supported in email clients."""
bytes_io = make_in_memory_qr(data)
img_str = base64.b64encode(bytes_io.getvalue()).decode()
return f"data:image/png;base64, {img_str}"
1 change: 1 addition & 0 deletions {{cookiecutter.project_slug}}/helpers/ua.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def get_ua_platform(user_agent: UserAgent) -> str:


def get_ua(request: HttpRequest) -> str:
"""Return User Agent string."""
return request.headers.get("user-agent", "")


Expand Down
2 changes: 1 addition & 1 deletion {{cookiecutter.project_slug}}/mkdocs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ markdown_extensions:
- pymdownx.tilde
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_sv
emoji_generator: !!python/name:material.extensions.emoji.to_svg

# Plugins
plugins:
Expand Down

0 comments on commit ed30ec5

Please sign in to comment.