Source code for regolith.commands
"""Implementation of commands for command line."""
import json
import os
import re
import sys
from copy import copy
from pprint import pprint
from regolith.builder import builder, BUILDERS
from regolith.deploy import deploy as dploy
from regolith.emailer import emailer
from regolith.helper import HELPERS, helpr
from regolith.runcontrol import RunControl
from regolith.tools import string_types
email = emailer
RE_AND = re.compile(r"\s+and\s+")
RE_SPACE = re.compile(r"\s+")
INGEST_COLL_LU = {".bib": "citations"}
[docs]def add_cmd(rc):
"""Adds documents to a collection in a database."""
db = rc.client[rc.db]
coll = db[rc.coll]
docs = [
json.loads(doc) if isinstance(doc, string_types) else doc
for doc in rc.documents
]
rc.client.insert_many(rc.db, rc.coll, docs)
def _ingest_citations(rc):
import bibtexparser
from bibtexparser.bparser import BibTexParser
from bibtexparser.customization import getnames
parser = BibTexParser()
parser.ignore_nonstandard_types = False
def customizations(record):
for n in ["author", "editor"]:
if n in record:
a = [i for i in record[n].replace("\n", " ").split(", ")]
b = [i.split(" and ") for i in a]
c = [item for sublist in b for item in sublist]
d = [i.strip() for i in c]
record[n] = getnames(d)
return record
parser.customization = customizations
with open(rc.filename, "r", encoding='utf-8') as f:
bibs = bibtexparser.load(f, parser=parser)
coll = rc.client[rc.db][rc.coll]
for bib in bibs.entries:
bibid = bib.pop("ID")
bib["entrytype"] = bib.pop("ENTRYTYPE")
if "author" in bib:
bib["author"] = [
a.strip() for b in bib["author"] for a in RE_AND.split(b)
]
if "title" in bib:
bib["title"] = RE_SPACE.sub(" ", bib["title"])
rc.client.update_one(rc.db, rc.coll, {"_id": bibid}, bib, upsert=True)
def _determine_ingest_coll(rc):
f = rc.filename
base, ext = os.path.splitext(f)
return INGEST_COLL_LU.get(ext, base)
[docs]def ingest(rc):
"""Ingests a foreign resource into a database."""
if rc.coll is None:
rc.coll = _determine_ingest_coll(rc)
if rc.coll == "citations":
_ingest_citations(rc)
else:
raise ValueError(
"don't know how to ingest collection {0!r}".format(rc.coll)
)
def _run_app(app, rc):
if hasattr(app, "rc"):
raise RuntimeError("cannot assign rc to app")
app.rc = rc
app.debug = rc.debug
print("\nDO NOT type Ctrl-C to close the server!!!")
print("Instead, run the following:")
print("\n$ curl -d '' http://localhost:5000/shutdown\n")
app.run(host="localhost")
del app.rc
[docs]def app(rc):
"""Runs flask app"""
from regolith.app import app
_run_app(app, rc)
[docs]def grade(rc):
"""Runs flask grading app"""
from regolith.grader import app
_run_app(app, rc)
[docs]def build_db_check(rc):
"""Checks which DBs a builder needs"""
dbs = set()
for t in rc.build_targets:
bldr = BUILDERS[t]
needed_colls = getattr(bldr, 'needed_colls', None)
# If the requested builder doesn't state DB deps then it requires
# all dbs!
if not needed_colls:
return None
dbs.update(needed_colls)
return dbs
[docs]def helper_db_check(rc):
"""Checks which DBs a builder needs"""
dbs = set()
bldr = HELPERS[rc.helper_target][0]
needed_colls = getattr(bldr, 'needed_colls', None)
# If the requested builder doesn't state DB deps then it requires
# all dbs!
if not needed_colls:
return None
dbs.update(needed_colls)
return dbs
[docs]def build(rc):
"""Builds all of the build targets"""
for t in rc.build_targets:
bldr = builder(t, rc)
bldr.build()
[docs]def helper(rc):
"""Runs the helper targets"""
hlpr = helpr(rc.helper_target, rc)
hlpr.hlp()
[docs]def deploy(rc):
"""Deploys all of the deployment targets."""
if not hasattr(rc, "deploy") or len(rc.deploy) == 0:
raise RuntimeError("run control has no deployment targets!")
for target in rc.deploy:
dploy(rc, **target)
[docs]def classlist(rc):
"""Sets values for the class list."""
from regolith.classlist import register
register(rc)
[docs]def json_to_yaml(rc):
"""Converts JSON to YAML"""
from regolith import fsclient
for inp in rc.files:
base, ext = os.path.splitext(inp)
out = base + ".yaml"
fsclient.json_to_yaml(inp, out)
[docs]def yaml_to_json(rc):
"""Converts YAML to JSON"""
from regolith import fsclient
for inp in rc.files:
base, ext = os.path.splitext(inp)
out = base + ".json"
fsclient.yaml_to_json(inp, out)
[docs]def fs_to_mongo(rc: RunControl) -> None:
"""Convert database collection from filesystem to mongo db.
Parameters
----------
rc : RunControl
The RunControl. The mongo client will be created according to 'mongodbpath' in it. The databases will
be loaded according to the 'databases' in it.
"""
from regolith.mongoclient import MongoClient
client = MongoClient(rc)
dbs = getattr(rc, 'databases')
for db in dbs:
client.import_database(db)
return
[docs]def mongo_to_fs(rc: RunControl) -> None:
"""Convert database collection from filesystem to mongo db.
Parameters
----------
rc : RunControl
The RunControl. The mongo client will be created according to 'mongodbpath' in it. The databases will
be loaded according to the 'databases' in it.
"""
dbs = getattr(rc, 'databases')
for db in dbs:
rc.client.export_database(db)
return
[docs]def validate(rc):
"""Validate the combined database against the schemas"""
from regolith.schemas import validate
print("=" * 10 + "\nVALIDATING\n")
any_errors = False
if getattr(rc, "collection"):
db = {rc.collection: rc.client.chained_db[rc.collection]}
else:
db = rc.client.chained_db
for name, collection in db.items():
errored_print = False
for doc_id, doc in collection.items():
v = validate(name, doc, rc.schemas)
if v[0] is False:
if errored_print is False:
errored_print = True
any_errors = True
print(f"Errors found in {name}")
print("=" * len(f"Errors found in {name}"))
print(f"ERROR in {doc_id}:")
pprint(v[1])
cap = copy(v[1])
for vv in v[1]:
pprint(doc.get(vv))
print("-" * 15)
print("\n")
if not any_errors:
print("\nNO ERRORS IN DBS\n" + "=" * 15)
else:
# uncomment when debugging scheme errors
#
# sys.exit(f"Validation failed on some records\n {cap}")
sys.exit(f"Validation failed on some records")