Skip to content

Instantly share code, notes, and snippets.

@helrond
Last active June 12, 2019 18:53
Show Gist options
  • Save helrond/50fabbf59e92dde1c77e5a76021f5582 to your computer and use it in GitHub Desktop.
Save helrond/50fabbf59e92dde1c77e5a76021f5582 to your computer and use it in GitHub Desktop.
A script to build bags which validate against the BagIt spec and a BagIt Profile.
#!/usr/bin/env python
"""
A script to build bags which validate against the BagIt spec and a BagIt Profile.
Usage: build_bags.py [-h] [-t TARGET_DIR] [-b BAG_COUNT] profile_url source_dir
"""
import argparse
import bagit
from datetime import datetime
from os import makedirs, listdir
from os.path import basename, isdir, join, normpath
from random import choice, randint
import requests
from shutil import copyfile, make_archive
import string
import tarfile
import time
from uuid import uuid4
import zipfile
class BagBuilderException(Exception): pass
class BagBuilder:
"""
Builds a bag from a random selection of files in a directory.
The following arguments are required:
profile_url: a HTTP-addressable URL at which a JSON representation of a
BagIt Profile is available. Used to construct valid metadata.
source_dir: a directory containing files, which will be randomly selected
and added to bags.
The following arguments are optional:
target_dir: a location in which bags should be created. Defaults to the
current directory if argument is omitted.
bag_count: the number of bags to create. Defaults to 10 if the argument
is omitted.
"""
def __init__(self, profile_url, source_dir, target_dir=None, bag_count=None):
self.profile_url = profile_url
self.bag_count = int(bag_count) if bag_count else 10
self.source_dir = source_dir
self.target_dir = target_dir
try:
self.profile = requests.get(profile_url).json()
except Exception as e:
raise BagBuilderException("Error fetching BagIt Profile: {}".format(e))
if not isdir(self.source_dir):
raise BagBuilderException("Source directory does not exist")
if (self.target_dir and not isdir(self.target_dir)):
makedirs(self.target_dir)
def run(self):
for i in range(self.bag_count):
self.bag_dir = self.make_dir()
self.add_files()
metadata = self.create_metadata(self.profile)
bagit.make_bag(self.bag_dir, metadata)
self.create_serialized()
def make_dir(self):
dir = str(uuid4())[:8]
if not isdir(join(self.target_dir, dir)):
makedirs(join(self.target_dir, dir))
return join(self.target_dir, dir)
else:
self.make_dir()
def add_files(self):
num_files = randint(1, len(listdir(self.source_dir)))
files = listdir(self.source_dir)
for i in range(num_files):
f = choice(files)
copyfile(join(self.source_dir, f), join(self.bag_dir, f))
def create_metadata(self, profile):
metadata = {}
for field in profile.get('Bag-Info'):
if profile['Bag-Info'][field].get('values'):
metadata[field] = choice(profile['Bag-Info'][field]['values'])
elif 'Date' in field:
metadata[field] = self.random_date()
elif 'Language' in field:
metadata[field] = self.random_language()
elif field == 'Bag-Count':
metadata[field] = self.random_string(randint(5,9))
else:
metadata[field] = self.random_string(randint(5,20))
metadata['BagIt-Profile-Identifier'] = self.profile_url
return metadata
def create_serialized(self):
with tarfile.open("{}.tar".format(self.bag_dir), 'w') as tar:
tar.add(self.bag_dir, arcname=basename(self.bag_dir))
with tarfile.open("{}.tar.gz".format(self.bag_dir), 'w:gz') as targz:
targz.add(self.bag_dir, arcname=basename(self.bag_dir))
make_archive(self.bag_dir, 'zip', self.target_dir, basename(normpath(self.bag_dir)))
def random_string(self, length=10):
letters = string.ascii_lowercase
return ''.join(choice(letters) for i in range(length))
def random_date(self):
d = randint(1, int(time.time()))
return datetime.fromtimestamp(d).strftime('%Y-%m-%d')
def random_language(self):
languages = ["aar", "abk", "ace", "ach", "ada", "ady", "afa", "afh", "afr", "ain", "aka", "akk", "alb", "ale", "alg", "alt", "amh", "ang", "anp", "apa", "ara", "arc", "arg", "arm", "arn", "arp", "art", "arw", "asm", "ast", "ath", "aus", "ava", "ave", "awa", "aym", "aze", "bad", "bai", "bak", "bal", "bam", "ban", "baq", "bas", "bat", "bej", "bel", "bem", "ben", "ber", "bho", "bih", "bik", "bin", "bis", "bla", "bnt", "bos", "bra", "bre", "btk", "bua", "bug", "bul", "bur", "byn", "cad", "cai", "car", "cat", "cau", "ceb", "cel", "cha", "chb", "che", "chg", "chi", "chk", "chm", "chn", "cho", "chp", "chr", "chu", "chv", "chy", "cmc", "cop", "cor", "cos", "cpe", "cpf", "cpp", "cre", "crh", "crp", "csb", "cus", "cze", "dak", "dan", "dar", "day", "del", "den", "dgr", "din", "div", "doi", "dra", "dsb", "dua", "dum", "dut", "dyu", "dzo", "efi", "egy", "eka", "elx", "eng", "enm", "epo", "est", "ewe", "ewo", "fan", "fao", "fat", "fij", "fil", "fin", "fiu", "fon", "fre", "frm", "fro", "frr", "frs", "fry", "ful", "fur", "gaa", "gay", "gba", "gem", "geo", "ger", "gez", "gil", "gla", "gle", "glg", "glv", "gmh", "goh", "gon", "gor", "got", "grb", "grc", "gre", "grn", "gsw", "guj", "gwi", "hai", "hat", "hau", "haw", "heb", "her", "hil", "him", "hin", "hit", "hmn", "hmo", "hrv", "hsb", "hun", "hup", "iba", "ibo", "ice", "ido", "iii", "ijo", "iku", "ile", "ilo", "ina", "inc", "ind", "ine", "inh", "ipk", "ira", "iro", "ita", "jav", "jbo", "jpn", "jpr", "jrb", "kaa", "kab", "kac", "kal", "kam", "kan", "kar", "kas", "kau", "kaw", "kaz", "kbd", "kha", "khi", "khm", "kho", "kik", "kin", "kir", "kmb", "kok", "kom", "kon", "kor", "kos", "kpe", "krc", "krl", "kro", "kru", "kua", "kum", "kur", "kut", "lad", "lah", "lam", "lao", "lat", "lav", "lez", "lim", "lin", "lit", "lol", "loz", "ltz", "lua", "lub", "lug", "lui", "lun", "luo", "lus", "mac", "mad", "mag", "mah", "mai", "mak", "mal", "man", "mao", "map", "mar", "mas", "may", "mdf", "mdr", "men", "mga", "mic", "min", "mis", "mkh", "mlg", "mlt", "mnc", "mni", "mno", "moh", "mon", "mos", "mul", "mun", "mus", "mwl", "mwr", "myn", "myv", "nah", "nai", "nap", "nau", "nav", "nbl", "nde", "ndo", "nds", "nep", "new", "nia", "nic", "niu", "nno", "nob", "nog", "non", "nor", "nqo", "nso", "nub", "nwc", "nya", "nym", "nyn", "nyo", "nzi", "oci", "oji", "ori", "orm", "osa", "oss", "ota", "oto", "paa", "pag", "pal", "pam", "pan", "pap", "pau", "peo", "per", "phi", "phn", "pli", "pol", "pon", "por", "pra", "pro", "pus", "qaa-qtz", "que", "raj", "rap", "rar", "roa", "roh", "rom", "rum", "run", "rup", "rus", "sad", "sag", "sah", "sai", "sal", "sam", "san", "sas", "sat", "scn", "sco", "sel", "sem", "sga", "sgn", "shn", "sid", "sin", "sio", "sit", "sla", "slo", "slv", "sma", "sme", "smi", "smj", "smn", "smo", "sms", "sna", "snd", "snk", "sog", "som", "son", "sot", "spa", "srd", "srn", "srp", "srr", "ssa", "ssw", "suk", "sun", "sus", "sux", "swa", "swe", "syc", "syr", "tah", "tai", "tam", "tat", "tel", "tem", "ter", "tet", "tgk", "tgl", "tha", "tib", "tig", "tir", "tiv", "tkl", "tlh", "tli", "tmh", "tog", "ton", "tpi", "tsi", "tsn", "tso", "tuk", "tum", "tup", "tur", "tut", "tvl", "twi", "tyv", "udm", "uga", "uig", "ukr", "umb", "und", "urd", "uzb", "vai", "ven", "vie", "vol", "vot", "wak", "wal", "war", "was", "wel", "wen", "wln", "wol", "xal", "xho", "yao", "yap", "yid", "yor", "ypk", "zap", "zbl", "zen", "zha", "znd", "zul", "zun", "zxx", "zza"]
num = randint(0, len(languages))
return languages[num]
parser = argparse.ArgumentParser(description='Builds bags which validate against the BagIt spec and a BagIt Profile.')
parser.add_argument('profile_url', help='URL for a BagIt Profile.')
parser.add_argument('source_dir', help='A directory containing files which will be randomly added to bags.')
parser.add_argument('-t', '--target_dir', help='Target directory in which bags will be created. Defaults to current directory.')
parser.add_argument('-b', '--bag_count', help='The number of bags to generate. Defaults to 10')
args = parser.parse_args()
BagBuilder(args.profile_url, args.source_dir, args.target_dir, args.bag_count).run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment