adding comments
This commit is contained in:
159
flit.py
159
flit.py
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user