Skip to content

Commit 0144851

Browse files
authored
Merge pull request #3 from tomasohara/tom-dev
integrate tom-dev
2 parents 7eab7b5 + 257087c commit 0144851

63 files changed

Lines changed: 12635 additions & 461 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
# Jupyter
3636
.ipynb_checkpoints
3737

38+
# (maldito) Mac
39+
.DS_Store
40+
3841
#...............................................................................
3942
# Idiosyncratic stuff for Tom
4043

@@ -47,10 +50,14 @@
4750
**/*.[0-9][0-9][A-Za-z]*[0-9][0-9]
4851
# ex: usage.list.08Jan22.2
4952
**/*.[0-9][0-9][A-Za-z]*[0-9][0-9].*
53+
versioned-files
5054

5155
# Emacs
5256
TAGS
5357

5458
# OS-specific directories
5559
linux
5660
win32
61+
mac-os
62+
# note: darwin21.1.0 => mac-os
63+
darwin21.1.0

TODO.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Things to do
2+
3+
- Check for duplicate code such as inadvertent merges artifacts. This is
4+
probably more of an issue with aliases (e.g., tomohara-aliases.bash).
5+
- Add simple example-based tests for common scripts (see simple_batspp.py).
6+

anaconda-aliases.bash

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Anaconda support based on conda installation
22
#
3+
# usage example:
4+
# source ~/bin/tomohara-aliases.bash
5+
# source ~/bin/anaconda-aliases.bash
6+
#
7+
# TODO: reproduce alias definitions from tomohara-aliases.bash here so independend
8+
#
39
#------------------------------------------------------------------------
410
# Copyright (c) 2020 Thomas P. O'Hara
511
#
@@ -47,11 +53,11 @@ function init-condaN() {
4753
eval "$conda_setup"
4854
else
4955
if [ -f "$anaconda_dir/etc/profile.d/conda.sh" ]; then
50-
. "$anaconda_dir/etc/profile.d/conda.sh"
56+
. "$anaconda_dir/etc/profile.d/conda.sh"
5157
## OLD: else
5258
## OLD: export PATH="$anaconda_dir/bin:$PATH"
5359
fi
54-
prepend-path "$anaconda_dir/bin"
60+
prepend-path "$anaconda_dir/bin"
5561
fi
5662

5763
# Restore Bash prompt and put conda environment in xterm title instead
@@ -66,7 +72,7 @@ function init-condaN() {
6672
}
6773
alias init-conda3='init-condaN $anaconda3_dir'
6874
alias init-conda2='init-condaN $anaconda2_dir'
69-
alias init-conda=init-conda2
75+
## OLD: alias init-conda=init-conda2
7076
# TODO: alias init-conda=init-conda3
7177

7278
# Work around for intermittent problems w/ 'conda activate' requiring 'source activate' instead.
@@ -80,7 +86,7 @@ function activation-helper () {
8086
## TODO: local conda_path=$(/usr/bin/which conda 2> /dev/null)
8187
local conda_path=""
8288
if [ "$conda_path" = "" ]; then
83-
conda_command="source"
89+
conda_command="source"
8490
fi
8591
## DEBUG:
8692
## echo "Issuing: $conda_command" "$command" "$env"
@@ -101,7 +107,8 @@ alias conda-list-env='conda list env'
101107
#
102108
## OLD: alias conda-list-env-hack='ls ~/.conda/envs'
103109
# conda-list-env-hack(): show environent names from typical directories
104-
function conda-list-env-hack () { ls ~/.conda/envs ${ANACONDA_HOME}/envs 2> /dev/null | grep -v ':$' | sort | echoize; }
110+
function conda-list-env-hack-aux { ls ~/.conda/envs ${ANACONDA_HOME}/envs 2> /dev/null | grep -v ':$' | sort; }
111+
function conda-list-env-hack { conda-list-env-hack-aux | echoize; }
105112
#
106113
alias conda-env-list='conda env list'
107114
alias conda-env-name='conda env list | extract_matches.perl "^(\S+ ) " | echoize'
@@ -116,18 +123,18 @@ function conda-activate-env {
116123
local use_hack="${2:-0}"
117124
## if [ "$env" = "" ]; then
118125
if [[ ("$env" = "") || ("$env" = "-") ]]; then
119-
echo "Usage: conda-activate-env"
120-
echo ""
121-
echo "Note: available environments:"
122-
## TODO: use columns
123-
## BAD:
124-
if [ "$use_hack" != "0" ]; then
125-
## OLD: conda-list-env-hack | perl -pe 's/^/ /;'
126-
conda-list-env-hack | perl -pe 's/ / /;'
127-
else
128-
conda-env-name
129-
fi
130-
echo ""
126+
echo "Usage: conda-activate-env env [use_hack=0]"
127+
echo ""
128+
echo "Note: available environments:"
129+
## TODO: use columns
130+
## BAD:
131+
if [ "$use_hack" != "0" ]; then
132+
## OLD: conda-list-env-hack | perl -pe 's/^/ /;'
133+
conda-list-env-hack | perl -pe 's/ / /;'
134+
else
135+
conda-env-name
136+
fi
137+
echo ""
131138
fi
132139
## OLD: source activate "$env";
133140
conda-activate "$env"
@@ -143,22 +150,25 @@ function conda-deactivate-env {
143150
add-conda-env-to-xterm-title
144151
}
145152

146-
# Miniconda3 initializaion
153+
# older Miniconda3-based initializaion
147154
# <<< conda initialize <<<
148155
# !! Contents within this block are managed by 'conda init' !!
149-
function init-miniconda3 () {
156+
## OLD: function init-miniconda3 () {
157+
function old-init-conda () {
150158
## OLD: local base="/usr/local/misc/programs/anaconda3"
151159
local base="$ANACONDA_HOME"
152160
local conda_setup
153161
conda_setup="$("$base/bin/conda" 'shell.bash' 'hook' 2> /dev/null)"
154162
if [ $? -eq 0 ]; then
155-
eval "$conda_setup"
163+
eval "$conda_setup"
156164
else
157-
if [ -f "$base/etc/profile.d/conda.sh" ]; then
165+
if [ -f "$base/etc/profile.d/conda.sh" ]; then
158166
. "$base/etc/profile.d/conda.sh"
159-
else
160-
export PATH="$base/bin:$PATH"
161-
fi
167+
## OLD
168+
## else
169+
## export PATH="$base/bin:$PATH"
170+
fi
171+
prepend-path "$base/bin"
162172
fi
163173

164174
# Restore Bash prompt and put conda environment in xterm title instead
@@ -170,22 +180,23 @@ function init-miniconda3 () {
170180
# TODO: determine the version, make sure ipython gets installed
171181
function conda-create-env () {
172182
if [ "$1" = "" ]; then
173-
echo "usage: conda-create-env name [python_version=3.7]"
174-
return
183+
echo "usage: conda-create-env name [python_version=3.9]"
184+
return
175185
fi
176186
local name="$1"
177187
local python_version="$2"
178-
if [ "$python_version" = "" ]; then python_version="3.7"; fi
188+
if [ "$python_version" = "" ]; then python_version="3.9"; fi
179189
ensure-conda-loaded
190+
echo "issuing: conda create --yes --name '$name' python='$python_version'"
180191
conda create --yes --name "$name" python="$python_version"
181192
}
182193

183-
# ensure-conda-loaded(): Make sure conda environment loaded (e.g., via miniconda3)
194+
# ensure-conda-loaded(): Make sure conda environment loaded (e.g., via old-init-conda)
184195
#
185196
function ensure-conda-loaded {
186197
if [ "$CONDA_PYTHON_EXE" = "" ]; then
187-
echo "Warning: Conda not initialized so using miniconda3"
188-
init-miniconda3;
198+
echo "Warning: Conda not initialized so using old-init-conda"
199+
old-init-conda;
189200
fi
190201
}
191202

assertion.tcl

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/sh
2+
#-*-tcl-*-
3+
# the next line restarts using wish \
4+
exec wish "$0" -- ${1+"$@"}
5+
#
6+
#!/usr/local/bin/wish -f
7+
#
8+
# assertion.tcl
9+
#
10+
# Simple TK script to display a message box for an assertion.
11+
# This is meant to be invoked from a C or C++ program as follows:
12+
#
13+
# String msg = "main.c 73: argc >= 1"
14+
# String cmd = "assertion.tcl " + msg;
15+
# system(cmd);
16+
#
17+
# TODO: filter the string so that tcl doesn't interpret it
18+
# add buttons for ignore/retry/cancel etc
19+
#
20+
21+
# Initialize for debugging (or not)
22+
wm title . "Assertion Failed!"
23+
## message .stupid -aspect 10000 -justify center -text "----- $argv -----";
24+
message .stupid -aspect 10000 -justify center -text " $argv ";
25+
pack .stupid
26+

backup_dir.py

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Python script that performs backups of the current directory. It creates a tar file
4+
# using find to select files less than a given size in bytes and up to a given recent
5+
# time period based on number of days. In addition, it has an option to create an
6+
# encrypted archive either via the zip utility.
7+
#
8+
# Note:
9+
# - Written by Tana A. given Bash snippets I use.
10+
#
11+
12+
"""Simple program to make backups of a source based on several parameters"""
13+
import time
14+
import os
15+
import socket
16+
import shutil
17+
import logging
18+
import tarfile
19+
from datetime import date
20+
import py7zr
21+
import click
22+
23+
# Local modules
24+
from mezcla import debug
25+
from mezcla import system
26+
27+
# Environment constants
28+
HOME_DIR = system.getenv_text("HOME", "~",
29+
"User home directory")
30+
BACKUP_DIR = system.getenv_text("BACKUP_DIR", HOME_DIR,
31+
"Base directory for backups")
32+
33+
34+
def create_backup_folder(source):
35+
"""Try to create the backup folder if it doesn't exist"""
36+
## OLD: backup_dir = os.path.join(os.environ["HOME"], socket.gethostname())
37+
backup_dir = os.path.join(BACKUP_DIR, socket.gethostname())
38+
base_dir = source.split("/")[-2]
39+
try:
40+
os.makedirs(backup_dir, exist_ok=True)
41+
except OSError as err:
42+
print(err)
43+
total, used, free = tuple(
44+
number / 1073741824 for number in shutil.disk_usage(backup_dir)
45+
)
46+
print(f"{backup_dir}: Total={total:1f} Used={used:.1f} Free={free:.1f} "
47+
f"{free/total*100:.1f}% of the total space)"
48+
)
49+
logging.basicConfig(
50+
## OLD: filename=f"{backup_dir}/_make-{base_dir}-incremental-backup-"
51+
filename=f"_make-{base_dir}-incremental-backup-"
52+
f'{date.today().strftime("%Y%m%d")}.log',
53+
filemode="w",
54+
encoding="utf-8",
55+
level=logging.DEBUG,
56+
)
57+
logging.debug("Debug starts now")
58+
59+
60+
def backup_derive(source, max_days_old, max_size_chars):
61+
"""Sets type of backup depending on modification time and file max_size_chars"""
62+
logging.info("Starts setting basename")
63+
64+
# Backup source, equivalent to $HOME/$HOSTNAME
65+
## OLD: backup_dir = os.path.join(os.environ["HOME"], socket.gethostname())
66+
backup_dir = os.path.join(BACKUP_DIR, socket.gethostname())
67+
68+
hostname = socket.gethostname() ##Get's hostname
69+
base_dir = source.split("/")[-2] ##Backup folder name
70+
## OLD: max_days_old=31
71+
## -or-: max_days_old=92; -or-: max_days_old=366;
72+
## -or-: max_days_old=$(calc-int "5 * 365.25");
73+
## -or-: max_days_old=36525 ## (i.e., 100 years--no limit)
74+
## OLD: max_size_chars=$(calc-int "5 * 1024**2")
75+
## -or-: max_size_chars=131072 -or-: max_size_chars=1048577
76+
## -or-: max_size_chars=1000000000 -or-: max_size_chars=1099511627776
77+
##(i.e., 1TB--effectively no limit)
78+
## TODO: max_days_old=$(calc-int "5 * 365.25"); max_size_chars=$(calc-int "10**9")
79+
# max_days_old = 5 * 365.25
80+
# max_size_chars = 10 ** 9
81+
if (
82+
max_days_old > 36525 and max_size_chars > 1048576
83+
): ##If 100 years and 1TB--effectively no limit
84+
basename = f"{backup_dir}/full-{hostname}-{base_dir}-"
85+
elif max_days_old > 365.25 * 5 and max_size_chars > 1024: ##If 5 years and 1GB
86+
basename = f"{backup_dir}/fullish-{hostname}-{base_dir}-"
87+
else:
88+
basename = f"{backup_dir}/incr-{hostname}-{base_dir}-"
89+
basename = basename + date.today().strftime(
90+
"%Y%m%d"
91+
) ##Adds date in YY-MM-DD format
92+
print(f"basename: {basename}")
93+
logging.info("basename:%s", basename)
94+
logging.info("Finish setting basename")
95+
return basename
96+
97+
98+
def sort_files(walkdir, size, days):
99+
"""Walks inside selected path and sift files based on given parameters"""
100+
logging.info("Starts walking inside path, some errors are expected")
101+
max_days_old = days * 86400 # Time in days converted to seconds
102+
max_size_chars = size * 1048576 # max_size_chars in megabytes converted to bytes
103+
lista = [] # Initializes a new empty list
104+
# Walks inside every source and file in walkdir
105+
for root, _, files in os.walk(walkdir):
106+
for file in files:
107+
try:
108+
if (
109+
os.stat(os.path.join(root, file)).st_size < max_size_chars
110+
and time.time() - os.path.getmtime(os.path.join(root, file))
111+
< max_days_old
112+
): ##If size is less than max size and time (in epoch seconds) less than max days
113+
lista.append(os.path.join(root, file))
114+
except OSError as err:
115+
logging.warning(err)
116+
pass
117+
logging.debug("list: %s", lista)
118+
logging.info("Finish walking inside path")
119+
return lista
120+
121+
122+
def create_tar(basename, lista):
123+
"""Creates a simple 7z file and writes files on it"""
124+
logging.info("Starts creating tar.gz file")
125+
with tarfile.open(basename + ".tar.gz", "w:gz") as archive:
126+
for file in lista:
127+
try:
128+
archive.add(file)
129+
except OSError as err: ##Broken links usually gives errors at this point
130+
logging.warning(err)
131+
pass
132+
except Exception as err: # pylint: disable=broad-except
133+
logging.critical(err)
134+
print(err)
135+
logging.info("Finish creating tar.gz file")
136+
137+
138+
def create_encrypted_7z(password, basename, lista):
139+
"""Creates a encrypted 7z file and writes files on it"""
140+
logging.info("Starts creating encrypted 7z file")
141+
with py7zr.SevenZipFile(basename + ".7z", "w", password=password) as archive:
142+
for file in lista:
143+
try:
144+
archive.write(file)
145+
except OSError as err:
146+
logging.warning(err)
147+
pass
148+
except Exception as err: # pylint: disable=broad-except
149+
logging.critical(err)
150+
## OLD: print("Unable to continue. Please see log")
151+
system.print_error("Unable to continue:\n\t{err}")
152+
logging.info("Finish creating encrypted 7z file")
153+
154+
155+
@click.command()
156+
@click.option(
157+
"--password",
158+
prompt=True,
159+
hide_input=True,
160+
default="",
161+
confirmation_prompt=True,
162+
help="Blank for no encryptation",
163+
)
164+
@click.option("-S", "--source", default=os.getcwd, help="Alternative source")
165+
@click.option("-f", "--full", is_flag=True, help="Full backup. Overrides size and days")
166+
@click.option("-d", "--days", required=True, type=int, help="Max days since modification")
167+
@click.option("-s", "--size", required=True, type=int, help="Max size in MB")
168+
def main(password, source, full, days, size):
169+
"""Main function"""
170+
create_backup_folder(source)
171+
logging.info("Checking --full flag")
172+
if full:
173+
logging.info("Changing days and size to infinite (--full)")
174+
days = size = float("inf")
175+
basename = backup_derive(source, days, size)
176+
lista = sort_files(source, days, size)
177+
logging.info("Testing password")
178+
if password:
179+
logging.info("Password given")
180+
create_encrypted_7z(password, basename, lista)
181+
else:
182+
logging.info("Password not given")
183+
create_tar(basename, lista)
184+
185+
186+
if __name__ == "__main__":
187+
debug.trace_current_context(level=debug.QUITE_DETAILED)
188+
debug.trace_fmt(4, "Environment options: {eo}",
189+
eo=system.formatted_environment_option_descriptions())
190+
# pylint: disable=no-value-for-parameter
191+
main()

0 commit comments

Comments
 (0)