def edit(args: Dict[str, str], config: Dict[str, str]): """ Edit the content of an artifact. Arguments: args: - a dictionary containing the following fields: id -> the IDs (the one you see with kb list) associated to the artifact we want to edit title -> the title assigned to the artifact(s) category -> the category assigned to the artifact(s) config: - a configuration dictionary containing at least the following keys: PATH_KB_DB - the database path of KB PATH_KB_DATA - the data directory of KB PATH_KB_HIST - the history menu path of KB EDITOR - the editor program to call """ initializer.init(config) # if an ID is specified, load artifact with that ID if args["id"]: edit_by_id(args["id"], config) # else if a title is specified elif args["title"]: edit_by_name(args["title"], args["category"], config) # else try to guess elif args["nameid"]: if args["nameid"].isdigit(): edit_by_id(args["nameid"], config) else: edit_by_name(args["nameid"], args["category"], config)
def apply_on_set(args: Dict[str, str], config: Dict[str, str]): """ Apply the specified template to all the filtered artifacts """ # Check initialization initializer.init(config) tags_list = None if args["tags"] and args["tags"] != "": tags_list = args["tags"].split(';') conn = db.create_connection(config["PATH_KB_DB"]) is_query_strict = not args["extended_match"] rows = db.get_artifacts_by_filter(conn, title=args["title"], category=args["category"], tags=tags_list, status=args["status"], author=args["author"], is_strict=is_query_strict) for artifact in rows: updated_artifact = Artifact(id=artifact.id, title=artifact.title, category=artifact.category, tags=artifact.tags, author=artifact.author, status=artifact.status, template=args["template"]) db.update_artifact_by_id(conn, artifact.id, updated_artifact)
def delete(args: Dict[str, str], config: Dict[str, str]): """ Delete a list of artifacts from the kb knowledge base. Arguments: args: - a dictionary containing the following fields: id -> a list of IDs (the ones you see with kb list) associated to the artifacts we want to delete title -> the title assigned to the artifact(s) category -> the category assigned to the artifact(s) config: - a configuration dictionary containing at least the following keys: PATH_KB_DB - the database path of KB PATH_KB_DATA - the data directory of KB PATH_KB_HIST - the history menu path of KB db_id: - True if this is a raw DB id, False if this is a viewed artifact IDs """ initializer.init(config) response = delete_artifacts(args, config, False) if response == -200: print("Artifact removed.") if response == -301: print("There is more than one artifact with that title, please specify a category") if response == -302: print("There is no artifact with that name, please specify a correct artifact name")
def template(args: Dict[str, str], config: Dict[str, str]): """ Manage templates for kb. Arguments: args: - a dictionary containing the following fields: template_command -> the sub-command to execute for templates that can be: "add", "delete", "edit", "list" or "new". file -> used if the command is add, representing the template file to add to kb template -> used if the command is "delete", "edit" or "new" to represent the name of the template query -> used if the command is "list" config: - a configuration dictionary containing at least the following keys: PATH_KB_DEFAULT_TEMPLATE - the path to the kb default template PATH_KB_TEMPLATES - the path to kb templates EDITOR - the editor program to call """ # Check initialization initializer.init(config) COMMANDS[args["template_command"]](args, config)
def add(args: Dict[str, str], config: Dict[str, str]): """ Adds a list of artifacts to the knowledge base of kb. Arguments: args: - a dictionary containing the following fields: file -> a list of files to add to kb title -> the title assigned to the artifact(s) category -> the category assigned to the artifact(s) tags -> the tags assigned to the artifact(s) author -> the author to assign to the artifact status -> the status to assign to the artifact config: - a configuration dictionary containing at least the following keys: PATH_KB_DB - the database path of KB PATH_KB_DATA - the data directory of KB EDITOR - the editor program to call """ # Check if the add command has proper arguments/options is_valid_add = args["file"] or args["title"] if not is_valid_add: print("Please, either specify a file or a title for the new artifact") sys.exit(1) # Check initialization initializer.init(config) conn = db.create_connection(config["PATH_KB_DB"]) if args["file"]: for fname in args["file"]: if fs.is_directory(fname): continue add_file_to_kb(conn, args, config, fname) else: # Get title for the new artifact title = args["title"] # Assign a "default" category if not provided category = args["category"] or "default" # Create "category" directory if it does not exist category_path = Path(config["PATH_KB_DATA"], category) category_path.mkdir(parents=True, exist_ok=True) if not db.is_artifact_existing(conn, title, category): # If a file is provided, copy the file to kb directory # otherwise open up the editor and create some content shell_cmd = shlex.split( config["EDITOR"]) + [str(Path(category_path, title))] call(shell_cmd) new_artifact = Artifact(id=None, title=title, category=category, path="{category}/{title}".format( category=category, title=title), tags=args["tags"], status=args["status"], author=args["author"]) db.insert_artifact(conn, new_artifact)
def view(args: Dict[str, str], config: Dict[str, str]): """ View an artifact contained in the knowledge base of kb. Arguments: args: - a dictionary containing the following fields: id -> the IDs (the one you see with kb list) associated to the artifact to view title -> the title of the artifact to view category -> the category of the artifact to view editor -> a boolean, if True the file will be opened in a text editor as a temporary file hence the original will not be affected config: - a configuration dictionary containing at least the following keys: PATH_KB_DB - the database path of KB PATH_KB_DATA - the data directory of KB PATH_KB_HIST - the history menu path of KB PATH_KB_MARKERS - the file associated to the markers EDITOR - the editor program to call """ # Check initialization initializer.init(config) color_mode = not args["no_color"] if args["id"]: view_by_id(args["id"], config, args["editor"], color_mode) elif args["title"]: view_by_name(args["title"], args["category"], config, args["editor"], color_mode)
def update_artifact(conn, old_artifact: Artifact, args: Dict[str, str], config: Dict[str, str], attachment): """ Update artifact properties within the knowledge base of kb. Arguments: old_artifact: - an object of type Artifact containing the old artifact details args: - a dictionary containing the following fields: id -> an id of an artifact - note - the ACTUAL db_id title -> the title to be assigned to the artifact to update category -> the category to be assigned to the artifact to update tags -> the tags to be assigned to the artifact to update author -> the author to be assigned to the artifact to update status -> the status to be assigned to the artifact to update template -> the template to be assigned to the artifact to update config: - a configuration dictionary containing at least the following keys: PATH_KB_DB - the database path of KB PATH_KB_DATA - the data directory of KB PATH_KB_HIST - the history menu path of KB attachment: - new file content """ initializer.init(config) template_name = args.get("template", "") updated_artifact = Artifact( id=None, title=args.get("title", old_artifact.title), category=args.get("category", old_artifact.category), tags=args.get("tags", old_artifact.tags), author=args.get("author", old_artifact.author), status=args.get("status", old_artifact.status), template=args.get("template", old_artifact.template), path=args.get("category", old_artifact.category) + '/' + args.get("title", old_artifact.title) ) db.update_artifact_by_id(conn, old_artifact.id, updated_artifact) # If either title or category has been changed, we must move the file if args["category"] or args["title"]: old_category_path = Path( config["PATH_KB_DATA"], old_artifact.category) new_category_path = Path( config["PATH_KB_DATA"], args["category"] or old_artifact.category) fs.create_directory(new_category_path) fs.move_file(Path(old_category_path, old_artifact.title), Path( new_category_path, args["title"] or old_artifact.title)) return -200
def edit(args: Dict[str, str], config: Dict[str, str]): """ Edit the content of an artifact. Arguments: args: - a dictionary containing the following fields: id -> the IDs (the one you see with kb list) associated to the artifact we want to edit title -> the title assigned to the artifact(s) category -> the category assigned to the artifact(s) config: - a configuration dictionary containing at least the following keys: PATH_KB_DB - the database path of KB PATH_KB_DATA - the data directory of KB PATH_KB_HIST - the history menu path of KB EDITOR - the editor program to call """ initializer.init(config) conn = db.create_connection(config["PATH_KB_DB"]) # if an ID is specified, load artifact with that ID if args["id"]: artifact = history.get_artifact(conn, config["PATH_KB_HIST"], args["id"]) category_path = Path(config["PATH_KB_DATA"], artifact.category) shell_cmd = config["EDITOR"].split() + [ Path(category_path, artifact.title) ] call(shell_cmd) # else if a title is specified elif args["title"]: artifacts = db.get_artifacts_by_filter(conn, title=args["title"], category=args["category"], is_strict=True) if len(artifacts) == 1: artifact = artifacts.pop() category_path = Path(config["PATH_KB_DATA"], artifact.category) shell_cmd = config["EDITOR"].split() + [ Path(category_path, artifact.title) ] call(shell_cmd) elif len(artifacts) > 1: print( "There is more than one artifact with that title, please specify a category" ) else: print( "There is no artifact with that name, please specify a correct artifact name" )
def search_kb(args: Dict[str, str], config: Dict[str, str]): """ Search artifacts within the knowledge base of kb. Arguments: args: - a dictionary containing the following fields: query -> filter for the title field of the artifact category -> filter for the category field of the artifact tags -> filter for the tags field of the artifact author -> filter for the author field of the artifact status -> filter for the status field of the artifact config: - a configuration dictionary containing at least the following keys: PATH_KB_DB - the database path of KB PATH_KB_DATA - the data directory of KB PATH_KB_HIST - the history menu path of KB EDITOR - the editor program to call """ # Check initialization initializer.init(config) conn = db.create_connection(config["PATH_KB_DB"]) categories = ls.list_categories(config) print(categories) # List all categories if args.get("all_categories", False) is True: categories = ls.list_categories(config) return categories # List all categories if args.get("all_tags", False) is True: all_tags = ls.list_tags(conn, config) return all_tags tags_list = None if args.get("tags",False) is True: if args["tags"] and args["tags"] != "": tags_list = args["tags"].split(';') rows = db.get_artifacts_by_filter( conn, title=args.get("query",''), category=args.get("category",''), tags=tags_list, status=args.get("status",''), author=args.get("author",'')) artifacts = sorted(rows, key=lambda x: x.title) return artifacts
def delete(args: Dict[str, str], config: Dict[str, str]): """ Delete a list of artifacts from the kb knowledge base. Arguments: args: - a dictionary containing the following fields: id -> a list of IDs (the ones you see with kb list) associated to the artifacts we want to delete title -> the title assigned to the artifact(s) category -> the category assigned to the artifact(s) config: - a configuration dictionary containing at least the following keys: PATH_KB_DB - the database path of KB PATH_KB_DATA - the data directory of KB PATH_KB_HIST - the history menu path of KB """ initializer.init(config) conn = db.create_connection(config["PATH_KB_DB"]) if args["id"]: for i in args["id"]: artifact_id = history.get_artifact_id(config["PATH_KB_HIST"], i) artifact = db.get_artifact_by_id(conn, artifact_id) if not artifact: continue db.delete_artifact_by_id(conn, artifact_id) category_path = Path(config["PATH_KB_DATA"], artifact.category) Path(category_path, artifact.title).unlink() if fs.count_files(category_path) == 0: fs.remove_directory(category_path) print("Artifact {category}/{title} removed!".format( category=artifact.category, title=artifact.title)) sys.exit(0) # else if a title is specified elif args["title"]: artifacts = db.get_artifacts_by_filter(conn, title=args["title"], category=args["category"], is_strict=True) if len(artifacts) == 1: artifact = artifacts.pop() db.delete_artifact_by_id(conn, artifact.id) print("Artifact {}/{} removed!".format(artifact.category, artifact.title)) else: print( "There is more than one artifact with that title, please specify a category")
def search(args: Dict[str, str], config: Dict[str, str]): """ Search artifacts within the knowledge base of kb. Arguments: args: - a dictionary containing the following fields: query -> filter for the title field of the artifact category -> filter for the category field of the artifact tags -> filter for the tags field of the artifact author -> filter for the author field of the artifact status -> filter for the status field of the artifact config: - a configuration dictionary containing at least the following keys: PATH_KB_DB - the database path of KB PATH_KB_DATA - the data directory of KB PATH_KB_HIST - the history menu path of KB EDITOR - the editor program to call """ # Check initialization initializer.init(config) tags_list = None if args["tags"] and args["tags"] != "": tags_list = args["tags"].split(';') conn = db.create_connection(config["PATH_KB_DB"]) rows = db.get_artifacts_by_filter( conn, title=args["query"], category=args["category"], tags=tags_list, status=args["status"], author=args["author"]) # rows.sort(key=lambda x: x[1]) artifacts = sorted(rows, key=lambda x: x.title) # Write to history file history.write(config["PATH_KB_HIST"], artifacts) # Is full_identifier mode enabled? if args["full_identifier"]: printer.print_search_result_full_mode(artifacts) return # Print resulting list color_mode = not args["no_color"] if args["verbose"]: printer.print_search_result_verbose(artifacts, color_mode) else: printer.print_search_result(artifacts, color_mode)
def add(args: Dict[str, str], config: Dict[str, str]): """ Adds a list of artifacts to the knowledge base of kb. Arguments: args: - a dictionary containing the following fields: file -> a list of files to add to kb title -> the title assigned to the artifact(s) category -> the category assigned to the artifact(s) tags -> the tags assigned to the artifact(s) author -> the author to assign to the artifact status -> the status to assign to the artifact config: - a configuration dictionary containing at least the following keys: PATH_KB_DB - the database path of KB PATH_KB_DATA - the data directory of KB EDITOR - the editor program to call """ # Check if the add command has proper arguments/options is_valid_add = args["file"] or args["title"] if not is_valid_add: print("Please, either specify a file or a title for the new artifact") sys.exit(1) # Check initialization initializer.init(config) conn = db.create_connection(config["PATH_KB_DB"]) if args["file"]: for fname in args["file"]: if fs.is_directory(fname): continue add_file_to_kb(conn, args, config, fname) else: if not db.is_artifact_existing(conn, args["title"], args["category"]): pass else: with tempfile.NamedTemporaryFile(delete=True) as f: shell_cmd = shlex.split(config["EDITOR"]) + [f] call(shell_cmd) args["temp_file"] = f result = add_artifact(conn, args, config) return (result)
def delete(args: Dict[str, str], config: Dict[str, str]): """ Delete a list of artifacts from the kb knowledge base. Arguments: args: - a dictionary containing the following fields: id -> a list of IDs (the ones you see with kb list) associated to the artifacts we want to delete title -> the title assigned to the artifact(s) category -> the category assigned to the artifact(s) config: - a configuration dictionary containing at least the following keys: PATH_KB_DB - the database path of KB PATH_KB_DATA - the data directory of KB PATH_KB_HIST - the history menu path of KB """ initializer.init(config) if args["id"]: for i in args["id"]: delete_by_id(i, config) elif args["title"]: delete_by_name(args["title"], args["category"], config)
def grep(args: Dict[str, str], config: Dict[str, str]): """ Grep through the list of artifacts of the knowledge base of kb. Arguments: args: - a dictionary containing the following fields: regex -> the regex to search for case_insensitive -> a boolean, if true, the search will be case insensitive matches -> a boolean, if true, only the raw matches will be shown verbose -> a boolean, if true, a verbose output is produced on screen config: - a configuration dictionary containing at least the following keys: PATH_KB_DB - the database path of KB PATH_KB_DATA - the data directory of KB PATH_KB_HIST - the history menu path of KB """ initializer.init(config) conn = db.create_connection(config["PATH_KB_DB"]) # Get all artifacts rows = db.get_artifacts_by_filter(conn, title="") # Get all the file paths related to the artifacts in the database file_list = [Path(config["PATH_KB_DATA"], r.category, r.title) for r in rows] # Grep in the files results = fs.grep_in_files( file_list, args["regex"], args["case_insensitive"]) # Get the list of artifact tuples in the form (category,title) artifact_names = [fs.get_filename_parts_wo_prefix( res[0], config["PATH_KB_DATA"]) for res in results] # If user specied --matches -> just show matching lines and exit if args["matches"]: printer.print_grep_matches(artifact_names) sys.exit(0) # Get the set of uniq artifacts uniq_artifact_names = set(artifact_names) # Get the number of matches (hits) for each path found filecounts = get_hits_per_artifact_name(artifact_names) grep_result = list() for art in uniq_artifact_names: artifact = db.get_artifacts_by_filter( conn, category=art[0], title=art[1])[0] if artifact: no_of_hits = filecounts[art] grep_result.append((artifact, no_of_hits)) # Sort by number of hits, the largest -> the first grep_result.sort(key=lambda x: x[1], reverse=True) grep_artifacts = [r[0] for r in grep_result] grep_hits = [r[1] for r in grep_result] # Write to history file history.write(config["PATH_KB_HIST"], grep_artifacts) color_mode = not args["no_color"] if args["verbose"]: printer.print_grep_result_verbose( grep_artifacts, grep_hits, color_mode) else: printer.print_grep_result(grep_artifacts, grep_hits, color_mode)
def update(args: Dict[str, str], config: Dict[str, str]): """ Update artifact properties within the knowledge base of kb. Arguments: args: - a dictionary containing the following fields: id -> a list of IDs (the ones you see with kb list) associated to the artifact to update title -> the title to be assigned to the artifact to update category -> the category to be assigned to the artifact to update tags -> the tags to be assigned to the artifact to update author -> the author to be assigned to the artifact to update status -> the status to be assigned to the artifact to update template -> the template to be assigned to the artifact to update edit_content -> a boolean, if True -> also open the artifact to edit the content config: - a configuration dictionary containing at least the following keys: PATH_KB_DB - the database path of KB PATH_KB_DATA - the data directory of KB PATH_KB_HIST - the history menu path of KB EDITOR - the editor program to call """ initializer.init(config) conn = db.create_connection(config["PATH_KB_DB"]) # if an ID is specified, load artifact with that ID if args["id"]: old_artifact = history.get_artifact(conn, config["PATH_KB_HIST"], args["id"]) if not old_artifact: print("The artifact you are trying to update does not exist! " "Please insert a valid ID...") return None updated_artifact = Artifact(id=None, title=args["title"], category=args["category"], tags=args["tags"], author=args["author"], status=args["status"], template=args["template"]) db.update_artifact_by_id(conn, old_artifact.id, updated_artifact) # If either title or category has been changed, we must move the file if args["category"] or args["title"]: old_category_path = Path(config["PATH_KB_DATA"], old_artifact.category) new_category_path = Path(config["PATH_KB_DATA"], args["category"] or old_artifact.category) fs.create_directory(new_category_path) fs.move_file( Path(old_category_path, old_artifact.title), Path(new_category_path, args["title"] or old_artifact.title)) # else if a title is specified elif args["title"]: artifact = db.get_uniq_artifact_by_filter(conn, title=args["title"], category=args["category"], author=args["author"], status=args["status"], is_strict=True) if artifact: category_path = Path(config["PATH_KB_DATA"], artifact.category) else: print( "There is none or more than one artifact with that title, please specify a category" ) if args["edit_content"] or args["body"]: if args["title"]: artifact_path = str(Path(category_path, artifact.title)) shell_cmd = shlex.split(config["EDITOR"]) + [artifact_path] elif args["id"]: artifact_path = str( Path(config["PATH_KB_DATA"]) / old_artifact.category / old_artifact.title) shell_cmd = shlex.split(config["EDITOR"]) + [artifact_path] if args["body"]: args["body"] = args["body"].replace("\\n", "\n") with open(artifact_path, 'w') as art_file: art_file.write(args["body"]) else: call(shell_cmd)
def view(args: Dict[str, str], config: Dict[str, str]): """ View an artifact contained in the knowledge base of kb. Arguments: args: - a dictionary containing the following fields: id -> the IDs (the one you see with kb list) associated to the artifact to view title -> the title of the artifact to view category -> the category of the artifact to view editor -> a boolean, if True the file will be opened in a text editor as a temporary file hence the original will not be affected config: - a configuration dictionary containing at least the following keys: PATH_KB_DB - the database path of KB PATH_KB_DATA - the data directory of KB PATH_KB_HIST - the history menu path of KB PATH_KB_MARKERS - the file associated to the markers EDITOR - the editor program to call """ # Check initialization initializer.init(config) conn = db.create_connection(config["PATH_KB_DB"]) if args["id"]: artifact_id = history.get_artifact_id( config["PATH_KB_HIST"], args["id"]) artifact = db.get_artifact_by_id(conn, artifact_id) if not artifact: sys.exit(1) category_path = Path(config["PATH_KB_DATA"], artifact.category) artifact_path = Path(category_path, artifact.title) if args["editor"]: with tempfile.NamedTemporaryFile() as tmpfname: fs.copy_file(artifact_path, tmpfname.name) call([config["EDITOR"], tmpfname.name]) sys.exit(0) # View File if fs.is_text_file(artifact_path): markers = get_markers(config["PATH_KB_MARKERS"]) color_mode = not args["no_color"] viewer.view(artifact_path, markers, color=color_mode) else: opener.open_non_text_file(artifact_path) elif args["title"]: artifact = db.get_uniq_artifact_by_filter(conn, title=args["title"], category=args["category"], is_strict=True) if artifact: category_path = Path(config["PATH_KB_DATA"], artifact.category) artifact_path = Path(category_path, artifact.title) content = "" if args["editor"]: call([config["EDITOR"], artifact_path]) sys.exit(0) # View File if fs.is_text_file(artifact_path): markers = get_markers(config["PATH_KB_MARKERS"]) color_mode = not args["no_color"] viewer.view(artifact_path, markers, color=color_mode) else: opener.open_non_text_file(artifact_path) else: print( "There is no artifact with that title, please specify a category")