adding comments

This commit is contained in:
Ville Rantanen
2020-12-29 09:24:58 +02:00
parent faef16a690
commit 73f5c34482

159
flit.py
View File

@@ -1,5 +1,4 @@
#!/usr/bin/python3
#!/usr/bin/env python3
from datetime import datetime, timedelta
import argparse
import os
@@ -9,10 +8,30 @@ import string
from urllib.parse import quote
import json
"""FLIT: Fluttering folders for simple file sharing.
The script maintains a set of folders, named so that they are hard to guess,
and contents are deleted after expiration date.
Attributes:
CONFIG (str): Configuration file name stored in each shared folder.
"""
CONFIG = ".flit.json"
def parse_opts():
"""Options parser
Args:
None
Returns:
Namespace: Options from the command line
"""
class _HelpAction(argparse._HelpAction):
def __call__(self, parser, namespace, values, option_string=None):
parser.print_help()
@@ -94,10 +113,30 @@ def parse_opts():
def random_char():
"""Random character generator
Args:
None
Returns:
str: A single uppercase letter or number
"""
return random.choice(string.ascii_uppercase + string.digits)
def random_name():
"""Random unique name for a new share. Share names follow the syntax [NUM-ABC-DEF],
Where NUM is a 3 digit growing number (to help navigating new folders), but the
rest are random, to ensure secrecy.
Args:
None
Returns:
str: New folder name
"""
while True:
existing_names = [c["name"] for c in get_folders()]
index = 0
@@ -118,6 +157,17 @@ def random_name():
def create_new(p, days, description):
"""Creates a new folder, and stores configuration
Args:
p (str): Share name
days (int): Days to store the folder
description (str): Description of the fileshare
Returns:
None
"""
os.mkdir(p)
now = datetime.now()
del_delta = timedelta(days=days)
@@ -130,6 +180,17 @@ def create_new(p, days, description):
def copy_files(cwd, new_name, files):
"""Copy files to a new share
Args:
c (str): Working directory when the script was started
new_name (str): Name of the new share
files (list): List of paths to copy
Returns:
None
"""
target = os.path.abspath(new_name)
for f in opts.files:
print(f"Copying: {f}")
@@ -150,9 +211,17 @@ def copy_files(cwd, new_name, files):
)
def get_stats(p):
# TODO: named tuple
"""Get share properties
Args:
p (str): Name of the share
Returns:
Dict: Share properties
"""
config = read_config(p)
del_time = datetime.fromisoformat(config["delete_time"])
@@ -171,6 +240,16 @@ def get_stats(p):
def get_sub_files(p):
"""Get 1st level files in a share
Args:
p (str): Name of the share
Returns:
List: Folders and Files in the share
"""
file_list = sorted([x for x in os.listdir(p) if not x.startswith(".")])
dir_list = [x + "/" for x in file_list if os.path.isdir(os.path.join(p, x))]
file_list = [x for x in file_list if os.path.isfile(os.path.join(p, x))]
@@ -178,15 +257,38 @@ def get_sub_files(p):
def get_folders():
"""Get share properties for all the shares
Args:
None:
Returns:
List: List that contain `get_stats(p)` for every share
"""
dir_list = [p for p in os.listdir(".") if os.path.exists(os.path.join(p, CONFIG))]
configs = [get_stats(p) for p in dir_list]
configs.sort(key=lambda c: c["created"], reverse=True)
return configs
def list_folders(root_folder, verbose=False, filter_name=None):
def list_folders(root_url, verbose=False, filter_name=None):
"""Show folder list
Args:
root_url (str): URL to root of the share system
verbose (bool): If set true, displays folder contents
filter_name (str): If set, display only one folder matching the name
Returns:
None
"""
folders = get_folders()
print("Folder Created ToDelete InDays")
header_format = "{:" + str(12 + len(root_url)) + "} {} {} {} {}"
print(header_format.format("URL","Created","ToDelete","InDays","Description"))
for c in folders:
if filter_name is not None:
if filter_name != c["name"]:
@@ -194,7 +296,7 @@ def list_folders(root_folder, verbose=False, filter_name=None):
due = "*" if c["due"] else " "
print(
"{}/{}/ {} {} {: 4d}d{} {}".format(
root_folder,
root_url,
c["name"],
c["created"].isoformat()[0:10],
c["delete_time"].isoformat()[0:10],
@@ -206,10 +308,19 @@ def list_folders(root_folder, verbose=False, filter_name=None):
if verbose:
sub_files = get_sub_files(c["name"])
for sp in sub_files:
print(" {}/{}/{}".format(root_folder, c["name"], quote(sp, "/")))
print(" {}/{}/{}".format(root_url, c["name"], quote(sp, "/")))
def del_due_folders():
"""Delete folders where due date has passed
Args:
None
Returns:
None
"""
folders = get_folders()
for c in folders:
if c["due"]:
@@ -218,11 +329,37 @@ def del_due_folders():
def read_config(p):
"""Read the share configuration file
Args:
p (str): Share name
Returns:
Dict: Share config
Example:
{
"created": "2020-12-28T23:04:07.275200",
"delete_time": "2020-12-29T23:04:07.275200",
"description": "Test share"
}
"""
with open(os.path.join(p, CONFIG), "rt") as fp:
return json.load(fp)
def write_config(p, config):
"""Write the share configuration file
Args:
p (str): Share name
config (Dict): Share configuration
Returns:
None
"""
with open(os.path.join(p, CONFIG), "wt") as fp:
return json.dump(config, fp, indent=2, sort_keys=True)
@@ -232,6 +369,8 @@ if __name__ == "__main__":
cwd = os.getcwd()
os.chdir(os.path.dirname(__file__))
if opts.command == "add":
""" Add a new share """
new_name = random_name()
create_new(new_name, opts.days, opts.description)
print(os.path.abspath(new_name))
@@ -240,7 +379,11 @@ if __name__ == "__main__":
list_folders(opts.root, verbose=True, filter_name=new_name)
if opts.command == "list" or opts.command is None:
""" List shares """
list_folders(opts.root, verbose=opts.verbose)
if opts.command == "del":
""" Delete due shares """
del_due_folders()