Init repo

This commit is contained in:
Fred Boniface 2023-08-14 20:26:26 +01:00
commit d6efabe24c
5 changed files with 419 additions and 0 deletions

160
.gitignore vendored Normal file
View File

@ -0,0 +1,160 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

14
LICENSE Normal file
View File

@ -0,0 +1,14 @@
Copyright 2023 Frederick Boniface (git.fjla.uk/fredboniface.co.uk)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the “Software”), to deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

43
README.md Normal file
View File

@ -0,0 +1,43 @@
# map-dots-fetch
A one-shot Python (3.11) script that fetches one or more images from a map-dots server.
Remember that map-dots can be run as a CLI application so if you are not looking to access map-dots images on more than one computer, you are probably best skipping this script and running map-dots locally.
## Running
map-dots-fetch uses imports from the standard-library with the exception of `requests`. You can choose to run it in a venv however it has been written to run in the global environment and should not interfere with any other Python applications that you are running.
map-dots-fetch requires a configuration file, an example of which is provided in this repository. The configuration file must be placed in one of these locations:
Linux systems:
- `~/.config/map-dots-fetch/conf.toml`
- `/etc/map-dots-fetch/conf.toml`
- `./conf.toml` In the same folder as map-dots-fetch.py
Windows systems:
- `C:\Users\{user}\map-dots-fetch.toml` Alongside your user files
- `./conf.toml`
If the config file exists in more than one place, the first file found will be loaded. Files are checked in the order they appear in the lists.
If no config file is present, the script will exit with an exit code of `1`.
## License
Copyright 2023 Frederick Boniface (git.fjla.uk/fredboniface.co.uk)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the “Software”), to deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

27
example.conf.toml Normal file
View File

@ -0,0 +1,27 @@
[server]
# Include the scheme in the url, eg. "https://" or "http://"
url = ""
port = 443
[images]
# Image settings are arrays, this enables you to generate multiple images with
# different settings at the same time.
# Traccar Device ID
deviceId = [1]
# Output Image Size
size = [[1920,1080]]
# Valid values from from & to are: "now", "-hour", "-day", "-week", "-month", "-quarter"
from = ["-month"]
to = ["now"]
# Output Image Style
style = ["circles"]
format = "png"
[files]
destDir = "/var/wallpapers/map-dots"

175
main.py Normal file
View File

@ -0,0 +1,175 @@
### map-dots-fetch
### Connects to a map-dots instance and fetches images, saving them to the filesystem. Cross platform support is
### provided and only the Python standard library is used - it should run on any system with Python 3.x installed
### The script is designed to be run on a schedule using systemd-timers of cron. The choice is yours - of course
### you can also run it manually if you wish.
### A configuration file should be created at /etc/map-dots-fetch/conf.toml or ~/.config/map-dots-fetch/conf.toml for Linux
### or at \Users\{username}\map-dots-fetch.toml for Windows.
### If no configuration file can be found at these paths, the program directory will be checked.
### If both paths exist on Linux, the file in your home directory will take precedence
### Configurable Options (Example configuration):
"""
conf.toml
---------
[server]
# Include the scheme in the url, eg. "https://" or "http://"
url = ""
port = 443
[images]
# Image settings are arrays, this enables you to generate multiple images with
# different settings at the same time.
# Traccar Device ID
deviceId = [1]
# Output Image Size
size = [[1920,1080]]
# Valid values from from & to are: "now", "-hour", "-day", "-week", "-month", "-quarter"
from = ["-month"]
to = ["now"]
# Output Image Style
style = ["circles"]
format = "png"
[files]
destDir = "/var/wallpapers/map-dots"
"""
##### LICENSE #####
"""
Copyright 2023 Frederick Boniface (git.fjla.uk/fredboniface.co.uk)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the Software), to deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
import os, sys, requests, datetime, tomllib
from pathlib import Path
pre_start = datetime.datetime.now().timestamp()
print("Finding configuration file")
user = os.environ.get('USERNAME', os.environ.get('USER'))
os_type = os.name
global_conf_path = "/etc/map-dots-fetch/conf.toml"
home_path = Path.home()
local_conf_path = f"{home_path}/.config/map-dots-fetch/conf.toml"
win_conf_path = f"{home_path}\\map-dots-fetch.toml"
if os.path.exists(local_conf_path) and os_type == "posix":
conf_path = local_conf_path
elif os.path.exists(global_conf_path) and os_type == "posix":
conf_path = global_conf_path
elif os.path.exists(win_conf_path) and os_type == "nt":
conf_path = win_conf_path
elif os.path.exists("conf.toml"):
conf_path = "conf.toml"
else:
if os_type == "posix":
print(f"Unable to find configuration file after searching '{global_conf_path}', '{local_conf_path}', and './conf.toml'")
elif os_type == "nt":
print(f"Unable to find configuration file after searching '{win_conf_path}' and this directory")
else:
print(f"System type: {os_type}, not supported.")
sys.exit(1)
print(f"Loading configuration file: {conf_path}")
with open(conf_path, 'rb') as conf:
conf_values = tomllib.load(conf)
request_url = f"{conf_values['server']['url']}:{conf_values['server']['port']}/traccar/"
# Define usable datetime objects
dateMap = {
'now': datetime.datetime.utcnow(),
'-hour': datetime.datetime.utcnow() - datetime.timedelta(hours=1),
'-day': datetime.datetime.utcnow() - datetime.timedelta(days=1),
'-week': datetime.datetime.utcnow() - datetime.timedelta(weeks=1),
'-month': datetime.datetime.utcnow() - datetime.timedelta(days=30), # Approximate 30 days
'-quarter': datetime.datetime.utcnow() - datetime.timedelta(days=90) # Approximate 90 days
}
print("Ensuring destination folders exist")
os.makedirs(conf_values['files']['destDir'], exist_ok=True)
for i in range(len(conf_values['images']['deviceId'])):
img_dev_id = conf_values['images']['deviceId'][i]
img_width, img_height = conf_values['images']['size'][i]
img_from = dateMap[conf_values['images']['from'][i]].strftime("%Y-%m-%dT%H:%M:%SZ")
img_to = dateMap[conf_values['images']['to'][i]].strftime("%Y-%m-%dT%H:%M:%SZ")
img_style = conf_values['images']['style'][i]
img_format = conf_values['images']['format']
print("Requesting image:")
print(f"ID: {img_dev_id}, Size: {img_width}x{img_height}, From/To: {img_from}, {img_to}, Style: {img_style}, Format: {img_format}")
params = {
"id": img_dev_id,
"width": img_width,
"height": img_height,
"from": img_from,
"to": img_to,
"format": img_format
}
pre_req_time = datetime.datetime.now().timestamp()
try:
res = requests.get(request_url, params=params, timeout=120)
except requests.Timeout:
print("HTTP request timed out after 2 minutes")
sys.exit(1)
except requests.RequestException as e:
print("Error with HTTP request: ", e)
sys.exit(1)
post_res_time = datetime.datetime.now().timestamp()
print(f"HTTP request completed in {post_res_time - pre_req_time} seconds")
if res.status_code != 200:
print(f"HTTP Response error: {res.status_code}")
sys.exit(1)
print("Ensuring image directory exists")
image_dir = os.path.join(conf_values['files']['destDir'], f"{i}")
os.makedirs(image_dir, exist_ok=True)
print("Deleting old image files")
for filename in os.listdir(image_dir):
file_path = os.path.join(image_dir, filename)
try:
if os.path.isfile(file_path):
os.unlink(file_path)
except Exception as e:
print("Error deleting old file: ", e)
print("Saving image")
image_filename = f"mapdot-{i}-{img_to}.png"
image_path = os.path.join(image_dir, image_filename)
try:
with open(image_path, "wb") as image_file:
image_file.write(res.content)
print(f"Image saved: {image_path}")
except Exception as e:
print(f"Unable to save image file: {e}")
pre_finish = datetime.datetime.now().timestamp()
print(f"map-dots-fetch completed in: {pre_finish - pre_start} seconds")