Skip to content

Commit

Permalink
add settings.cache_directory and new download()
Browse files Browse the repository at this point in the history
  • Loading branch information
marcomusy committed Nov 17, 2023
1 parent 43821d8 commit f0af18a
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 58 deletions.
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
*pyc
.DS_Store
.vedo_recorded_events.log
.vedo_pipeline_graphviz*

docs/examples_db.js
docs/pdoc/html
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![lics](https://img.shields.io/badge/license-MIT-blue.svg)](https://en.wikipedia.org/wiki/MIT_License)
[![Anaconda-Server Badge](https://anaconda.org/conda-forge/vedo/badges/version.svg)](https://anaconda.org/conda-forge/vedo)
[![Ubuntu 23.10 package](https://repology.org/badge/version-for-repo/ubuntu_23_10/vedo.svg)](https://repology.org/project/vedo/versions)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7734756.svg)](https://doi.org/10.5281/zenodo.7734756)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.4587871.svg)](https://doi.org/10.5281/zenodo.4587871)
[![Downloads](https://static.pepy.tech/badge/vedo)](https://pepy.tech/project/vedo)
[![CircleCI](https://circleci.com/gh/marcomusy/vedo.svg?style=svg)](https://circleci.com/gh/marcomusy/vedo)
A lightweight and powerful python module
Expand Down
10 changes: 5 additions & 5 deletions vedo/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ def exe_search_vtk(args):

xref_url = "https://raw.githubusercontent.com/Kitware/vtk-examples/gh-pages/src/Coverage/vtk_vtk-examples_xref.json"

def download_file(dl_path, dl_url, overwrite=False):
def _download_file(dl_path, dl_url, overwrite=False):
file_name = dl_url.split("/")[-1]
# Create necessary sub-directories in the dl_path (if they don't exist).
Path(dl_path).mkdir(parents=True, exist_ok=True)
Expand All @@ -449,7 +449,7 @@ def download_file(dl_path, dl_url, overwrite=False):
raise RuntimeError(f"Failed to download {dl_url}. {e.reason}")
return path

def get_examples(d, vtk_class, lang):
def _get_examples(d, vtk_class, lang):
try:
kv = d[vtk_class][lang].items()
except KeyError as e:
Expand All @@ -461,18 +461,18 @@ def get_examples(d, vtk_class, lang):

vtk_class, language, all_values, number = args.search_vtk, "Python", True, 10000
tmp_dir = tempfile.gettempdir()
path = download_file(tmp_dir, xref_url, overwrite=False)
path = _download_file(tmp_dir, xref_url, overwrite=False)
if not path.is_file():
print(f"The path: {str(path)} does not exist.")

dt = datetime.today().timestamp() - os.path.getmtime(path)
# Force a new download if the time difference is > 10 minutes.
if dt > 600:
path = download_file(tmp_dir, xref_url, overwrite=True)
path = _download_file(tmp_dir, xref_url, overwrite=True)
with open(path, "r", encoding="UTF-8") as json_file:
xref_dict = json.load(json_file)

total_number, examples = get_examples(xref_dict, vtk_class, language)
total_number, examples = _get_examples(xref_dict, vtk_class, language)
if examples:
if total_number <= number or all_values:
print(
Expand Down
95 changes: 56 additions & 39 deletions vedo/file_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,54 +441,71 @@ def _load_file(filename, unpack):
return actor

########################################################################
def download(url, force=False, verbose=True):
def download(url, to_local_file="", force=False, verbose=True):
"""
Retrieve a file from a URL, save it locally and return its path.
Use `force=True` to force a reload and discard cached copies.
Downloads a file from `url` to `to_local_file` if the local copy is outdated.
Arguments:
url : (str)
The URL to download the file from.
to_local_file : (str)
The local file name to save the file to.
If not specified, the file name will be the same as the remote file name.
force : (bool)
Force a new download even if the local file is up to date.
verbose : (bool)
Print verbose messages.
"""
if not url.startswith("https://"):
# vedo.logger.error(f"Invalid URL (must start with https):\n{url}")
# assume it's a file so no need to download
return url
url = url.replace("www.dropbox", "dl.dropbox")
if os.path.exists(url):
# Assume the url is already the local file path
return url
else:
raise FileNotFoundError(f"File not found: {url}")

if "github.com" in url:
url = url.replace("/blob/", "/raw/")
from datetime import datetime
import requests

basename = os.path.basename(url)
# Get the user's home directory
home_directory = os.path.expanduser("~")

if "?" in basename:
basename = basename.split("?")[0]
# Define the path for the cache directory
cachedir = os.path.join(home_directory, settings.cache_directory, "vedo")

tmp_file = NamedTemporaryFile(delete=False)
tmp_file.name = os.path.join(os.path.dirname(tmp_file.name), os.path.basename(basename))
# Create the directory if it does not exist
if not os.path.exists(cachedir):
os.makedirs(cachedir)

if not force and os.path.exists(tmp_file.name):
if verbose:
colors.printc("reusing cached file:", tmp_file.name)
# colors.printc(" (use force=True to force a new download)")
return tmp_file.name
if not to_local_file:
to_local_file = os.path.join(cachedir, os.path.basename(url))
if verbose: print(f"Using local file name: {to_local_file}")

# Check if the local file exists and get its last modified time
if os.path.exists(to_local_file):
to_local_file_modified_time = os.path.getmtime(to_local_file)
else:
to_local_file_modified_time = 0

# Send a HEAD request to get last modified time of the remote file
response = requests.head(url)
if 'Last-Modified' in response.headers:
remote_file_modified_time = datetime.strptime(
response.headers['Last-Modified'], '%a, %d %b %Y %H:%M:%S GMT'
).timestamp()
else:
# If the Last-Modified header not available, assume file needs to be downloaded
remote_file_modified_time = float('inf')

# Download the file if the remote file is newer
if force or remote_file_modified_time > to_local_file_modified_time:
response = requests.get(url)
with open(to_local_file, 'wb') as file:
file.write(response.content)
if verbose: print(f"Downloaded file from {url} -> {to_local_file}")
else:
if verbose: print("Local file is up to date.")
return to_local_file

try:
from urllib.request import urlopen, Request
req = Request(url, headers={"User-Agent": "Mozilla/5.0"})
if verbose:
colors.printc("reading", basename, "from", url.split("/")[2][:40], "...", end="")

except ImportError:
import urllib2
import contextlib
urlopen = lambda url_: contextlib.closing(urllib2.urlopen(url_))
req = url
if verbose:
colors.printc("reading", basename, "from", url.split("/")[2][:40], "...", end="")

with urlopen(req) as response, open(tmp_file.name, "wb") as output:
output.write(response.read())

if verbose:
colors.printc(" done.")
return tmp_file.name

########################################################################
def gunzip(filename):
Expand Down
28 changes: 21 additions & 7 deletions vedo/plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -1468,14 +1468,15 @@ def look_at(self, plane="xy"):
vedo.logger.error(f"in plotter.look() cannot understand argument {plane}")
return self

def record(self, filename=".vedo_recorded_events.log"):
def record(self, filename=""):
"""
Record camera, mouse, keystrokes and all other events.
Recording can be toggled on/off by pressing key "R".
Arguments:
filename : (str)
ascii file to store events. The default is '.vedo_recorded_events.log'.
ascii file to store events.
The default is `settings.cache_directory+"vedo/recorded_events.log"`.
Returns:
a string descriptor of events.
Expand All @@ -1490,6 +1491,13 @@ def record(self, filename=".vedo_recorded_events.log"):
return self
erec = vtk.new("InteractorEventRecorder")
erec.SetInteractor(self.interactor)
if not filename:
if not os.path.exists(settings.cache_directory):
os.makedirs(settings.cache_directory)
home_dir = os.path.expanduser("~")
filename = os.path.join(
home_dir, settings.cache_directory, "vedo", "recorded_events.log")
print("Events will be recorded in", filename)
erec.SetFileName(filename)
erec.SetKeyPressActivationValue("R")
erec.EnabledOn()
Expand All @@ -1502,13 +1510,14 @@ def record(self, filename=".vedo_recorded_events.log"):
erec = None
return events

def play(self, events=".vedo_recorded_events.log", repeats=0):
def play(self, recorded_events="", repeats=0):
"""
Play camera, mouse, keystrokes and all other events.
Arguments:
events : (str)
file o string of events. The default is '.vedo_recorded_events.log'.
file o string of events.
The default is `settings.cache_directory+"vedo/recorded_events.log"`.
repeats : (int)
number of extra repeats of the same events. The default is 0.
Expand All @@ -1524,12 +1533,17 @@ def play(self, events=".vedo_recorded_events.log", repeats=0):
erec = vtk.new("InteractorEventRecorder")
erec.SetInteractor(self.interactor)

if events.endswith(".log"):
if not recorded_events:
home_dir = os.path.expanduser("~")
recorded_events = os.path.join(
home_dir, settings.cache_directory, "vedo", "recorded_events.log")

if recorded_events.endswith(".log"):
erec.ReadFromInputStringOff()
erec.SetFileName(events)
erec.SetFileName(recorded_events)
else:
erec.ReadFromInputStringOn()
erec.SetInputString(events)
erec.SetInputString(recorded_events)

erec.Play()
for _ in range(repeats):
Expand Down
9 changes: 7 additions & 2 deletions vedo/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class Settings:
# To run a demo try:
# vedo --run fonts
# Use this local folder to store downloaded files (default is ~/.cache/vedo)
cache_directory = ".cache"
# Palette number when using an integer to choose a color
palette = 0
Expand Down Expand Up @@ -165,11 +168,11 @@ class Settings:
```
"""

# Restrict the attributes so accidental typos will generate
# an AttributeError exception
# Restrict the attributes so accidental typos will generate an AttributeError exception
__slots__ = [
"default_font",
"default_backend",
"cache_directory",
"palette",
"remember_last_figure_format",
"screenshot_transparent_background",
Expand Down Expand Up @@ -253,6 +256,8 @@ def __init__(self):
self.palette = 0
self.remember_last_figure_format = False

self.cache_directory = ".cache" # "/vedo" is added automatically

self.screenshot_transparent_background = False
self.screeshot_large_image = False

Expand Down
7 changes: 6 additions & 1 deletion vedo/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,12 @@ def show(self, orientation="LR", popup=True):
self.counts = 0
self._build_tree(dot)
self.dot = dot
dot.render(".vedo_pipeline_graphviz", view=popup)

home_dir = os.path.expanduser("~")
gpath = os.path.join(
home_dir, vedo.settings.cache_directory, "vedo", "pipeline_graphviz")

dot.render(gpath, view=popup)


###########################################################################
Expand Down
2 changes: 1 addition & 1 deletion vedo/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
_version = '2023.5.0+dev1'
_version = '2023.5.0+dev2'

0 comments on commit f0af18a

Please sign in to comment.