def graph(self, x_col, graph_type): """Graph the data wrt to x_col.""" parsed_data = self.data.applymap(en.parse_data) graph_operations = { "line": lambda x_col : parsed_data.groupby(x_col).mean(), "bar" : lambda x_col : parsed_data.groupby(x_col).sum(), "count": lambda x_col, d=self.data[x_col].value_counts() : pd.DataFrame({x_col:d.index,"count":d.values}).groupby(x_col).sum() } try: if graph_type in graph_operations: data = graph_operations[graph_type](x_col) else: return r.Status(False, "\tGraph type \"%s\" invalid\n\tValid graph types: %s" % (graph_type, ", ".join(graph_operations.keys()))) except pd.core.base.DataError as e: return r.Status(False, "\t" + str(e)) x_data = list(data.index) for idx, col in enumerate(data.columns): y_data = list(data[col]) if graph_type == "line": plt.plot(x_data, y_data, label=data[col].name) elif graph_type == "bar" or graph_type == "count": bar_count = len(data.columns) bar_width = 0.7 / bar_count index = np.arange(len(x_data)) plt.bar(index + idx * bar_width, y_data, bar_width, label=data[col].name) x_ticks = index + (bar_count * bar_width - bar_width) / 2 plt.xticks(x_ticks, x_data) if type(x_data[0]) == date: plt.gcf().autofmt_xdate() plt.legend() plt.show() return r.Status(True)
def log(self, parameters): """Add new entries to an item.""" usage = "\tUsage: log <item_name> [insert_index]" if not self.check_num_params(parameters, [1, 2], usage): return r.Status(False) item = parameters[0] if not self.check_item(item): return r.Status(False) item = self.logs[item] columns = item.get_columns() if len(parameters) == 2: index = parameters[1] if not index.isnumeric(): return r.Status(False, "\tIndex must be an integer") index = int(index) else: index = None prompt = "Enter data for columns (%s) (Enter 'quit' when done): " % ", ".join(columns) user_in = input(prompt) while user_in != "quit": data = list(map(en.encode_input, user_in.split())) if len(data) != len(columns): print("\tColumn count mismatch: %d columns entered, needs %d." % (len(data), len(columns))) else: run = item.insert(data, index) if len(parameters) == 2: index += 1 if run.failed(): return run user_in = input(prompt) return r.Status(True)
def delete_col(self, col_name): """Remove a column from the DataFrame.""" if col_name not in self.get_columns(): return r.Status(False, "\tColumn \"%s\" not found\n\tValid columns: %s" % (col_name, ", ".join(self.get_columns()))) self.data = self.data.drop(col_name, axis=1) self.save() return r.Status(True)
def edit_entry(self, index, data): """Edit an entry in the DataFrame.""" dataf = pd.DataFrame([data], columns=self.get_columns()) col_index = self.checktype(dataf) if col_index != -1: return r.Status(False, "\tInvalid type entered for column \"%s\"" % self.get_columns()[col_index]) self.data.loc[int(index)] = data self.save() return r.Status(True)
def delete_entry(self, index): """Delete an entry in the DataFrame.""" if not index.isnumeric(): return r.Status(False, "\tIndex must be numeric") else: index = int(index) if index not in self.data.index: return r.Status(False, "\tEntry number %d does not exist" % index) self.data = self.data.drop(index) self.save() return r.Status(True)
def expand(self, col_name, data): """Add a column with data to the DataFrame.""" if len(data) != len(self.data) and len(data) != 0: return r.Status(False, "\tInvalid number of entries: %d entered, 0 or %d needed" % (len(data), len(self.data))) if len(data) == 0: self.data[col_name] = [0 for x in range(len(self.data))] else: self.data[col_name] = data self.save() return r.Status(True)
def tag(self, parameters): """Tag an item.""" usage = "\tUsage: tag <\"add\", \"remove\", or \"show\"> [item_name] [tag_name]" if not self.check_num_params(parameters, [1,3], usage): return r.Status(False) if parameters[0] == "show": for tag, items in self.tags.items(): print("\t" + tag + ": " + ", ".join(items)) return r.Status(True) item = parameters[1] if not self.check_item(item): return r.Status(False) tag = parameters[2] op = parameters[0] if op == "add": if tag not in self.tags: self.tags[tag] = [] if item in self.tags[tag]: return r.Status(False, "\tItem \"%s\" already has tag \"%s\"" % (item, tag)) self.tags[tag].append(item) elif op == "remove": if tag not in self.tags: return r.Status(False, "\tTag \"%s\" does not exist" % tag) if item not in self.tags[tag]: return r.Status(False, "\tItem \"%s\" does not have tag \"%s\"" % (item, tag)) self.tags[tag].remove(item) if not self.tags[tag]: del self.tags[tag] else: return r.Status(False, "\tMust specify \"add\" or \"remove\"") self.save_tags() return r.Status(True)
def insert(self, data, index=None): """Insert a new entry at index.""" dataf = pd.DataFrame([data], columns=self.get_columns()) col_index = self.checktype(dataf) if col_index != -1: return r.Status(False, "\tInvalid type entered for column \"%s\"" % self.get_columns()[col_index]) if index == None or index >= len(self.data): self.data = self.data.append(dataf, ignore_index=True) else: data_a = self.data.iloc[:index] data_b = self.data.iloc[index:] self.data = data_a.append(dataf).append(data_b).reset_index(drop=True) self.save() return r.Status(True)
def rename(self, name, col=None): """Rename this item, or a column.""" if col == None: self.name = name os.remove(self.directory) self.directory = "logs/" + name + ".txt" self.save() return r.Status(True) else: if col not in self.get_columns(): return r.Status(False, "\tColumn \"%s\" not found\n\tValid columns: %s" % (col, ", ".join(self.get_columns()))) self.data = self.data.rename(columns={col:name}) self.save() return r.Status(True)
def copy(self, parameters): """Copy an item.""" usage = "\tUsage: copy <item_name> <new_name>" if not self.check_num_params(parameters, [2], usage): return r.Status(False) item = parameters[0] if not self.check_item(item): return r.Status(False) item = self.logs[item] new_name = parameters[1] if new_name in self.logs: return r.Status(False, "\tItem \"%s\" already exists" % new_name) new_item = LoggedItem(new_name, copy_from=item) self.logs[new_name] = new_item return r.Status(True)
def expand(self, parameters): """Add a column to an item.""" usage = "\tUsage: expand <item_name> <new_column_name>" if not self.check_num_params(parameters, [2], usage): return r.Status(False) item = parameters[0] if not self.check_item(item): return r.Status(False) item = self.logs[item] new_col = parameters[1] if new_col in item.get_columns(): return r.Status(False, "\tColumn %s already exists" % new_col) prompt = "\nEnter data for %d entries: " % (len(item.get_indices())) data = input(prompt).split() data = list(map(en.encode_input, data)) return item.expand(new_col, data)
def graph(self, parameters): """Graph an item.""" usage = "\tUsage: graph <item_name> <x_column> [graph_type]" if not self.check_num_params(parameters, [2, 3], usage): return r.Status(False) item = parameters[0] if not self.check_item(item): return r.Status(False) item = self.logs[item] x_col = parameters[1] if x_col not in item.get_columns(): return r.Status(False, "\tColumn \"%s\" not found\n\tValid columns: %s" % (x_col, ", ".join(item.get_columns()))) if len(parameters) == 2: return item.graph(x_col, "line") else: graph_type = parameters[2] return item.graph(x_col, graph_type)
def view(self, parameters): """View the log, with parameters given by user.""" usage = "\tUsage: view <item_name> [column(s)] {filter_column} {filter_operation} {filter_value}" if not self.check_num_params(parameters, [1,2,4,5], usage): return r.Status(False) item = parameters[0] if not self.check_item(item): return r.Status(False) logged_item = self.logs[item] if len(parameters) == 5: col, filter_col, filter_op, filter_val = parameters[1:] return logged_item.get_entries(filter_col, filter_op, filter_val, col) elif len(parameters) == 4: filter_col, filter_op, filter_val = parameters[1:] return logged_item.get_entries(filter_col, filter_op, filter_val) elif len(parameters) == 2: col = parameters[1] return logged_item.get_entries(col=col) elif len(parameters) == 1: return logged_item.get_entries()
def edit(self, parameters): """Edit a previous entry.""" usage = "\tUsage: edit <item_name> <entry_number>" if not self.check_num_params(parameters, [2], usage): return r.Status(False) item = parameters[0] if not self.check_item(item): return r.Status(False) item = self.logs[item] index = parameters[1] if not index.isnumeric(): return r.Status(False, "\tIndex must be an integer") columns = item.get_columns() prompt = "\nEnter data for columns (" + ", ".join(columns) + "): " user_in = input(prompt).split() user_in = list(map(en.encode_input, user_in)) for entry in user_in: if type(entry) == r.Status: return entry return item.edit_entry(index, user_in)
def list(self, parameters): """List logged items.""" usage = "\tUsage: list <\"all\" or \"tags\"> [tag1,tag2,...]" if not self.check_num_params(parameters, [1,2], usage): return r.Status(False) op = parameters[0] if op == "all": show_list = list(self.logs.keys()) elif op == "tags": tags = parameters[1].split(",") show_list = [] for tag in tags: if tag not in self.tags: return r.Status(False, "\tTag \"%s\" does not exist" % tag) show_list += self.tags[tag] show_list = set(show_list) else: return r.Status(False, "\t Must specify \"all\" or \"tags\"") print("\tYour items: " + ", ".join(show_list)) return r.Status(True)
def delete(self, parameters): """Delete an entry from the table.""" usage = "\tUsage: delete <item_name> [\"column\" or \"entry\"] [column_name or entry_number]" if not self.check_num_params(parameters, [1, 3], usage): return r.Status(False) item = parameters[0] if not self.check_item(item): return r.Status(False) item = self.logs[item] if len(parameters) == 3: delete_type = parameters[1] if delete_type == "column": col = parameters[2] if self.get_conf_delete(col, "column"): return item.delete_col(col) else: return r.Status(False, "\tInput does not match column name, delete cancelled") if delete_type == "entry": index = parameters[2] return item.delete_entry(index) else: return r.Status(False, "\tMust specify whether to delete column or entry") return item.delete_entry(index) elif len(parameters) == 1: if self.get_conf_delete(item.name, "item"): self.logs.pop(item.name) if self.tags: to_remove = [] for tag, items in self.tags.items(): if item.name in items: items.remove(item.name) if not items: to_remove.append(tag) for tag in to_remove: del self.tags[tag] self.save_tags() return item.delete_item() else: return r.Status(False, "\tInput does not match item name, delete cancelled")
def parse_slice(string): """Convert a string inpout into a list of indices.""" indices = [] slice_in = string.split(':') if len(slice_in) != 2 and len(slice_in) != 3: return r.Status( False, "\tIndex must be in the form start:end or start:end:step") for num in slice_in: if not num.isnumeric(): return r.Status( False, "\tInvalid index \"" + num + "\", index must be an integer") indices.append(int(num)) if len(indices) == 2: indices.append(1) if indices[1] < indices[0]: return r.Status( False, "\tIndex must have a greater ending position than starting position" ) indices[0] -= 1 return indices
def help(self, parameters): """Print out help information.""" print("\tThis program is a manager and visualization tool for CSV (comma separated value) files") print("\tYou can enter commands in order to view, edit, graph, etc.") print("\tTo see how to use a command, enter the command name and press enter.") print("\tRequired arguments are denoted with angle brackets <>") print("\tOptional arguments are denoted with varying brackets, where each set of arguments uses the same symbol") print("\tIf you want to specify a date, make sure your input has a '/' character") print("\tIf you want to specify a a range of columns, use the notation begin:end:step") print("\tThe view command automatically generates an item called 'last'") print("\tYou can use the 'last' item to chain commands, such as viewing and graphing") print("\tTo get started, create a new item by typing \"view <item_name>\"") return r.Status(True)
def rename(self, parameters): """Rename an item.""" usage = "\tUsage: rename <item_name> {column_name} <new_name>" if not self.check_num_params(parameters, [2, 3], usage): return r.Status(False) item = parameters[0] if not self.check_item(item): return r.Status(False) item = self.logs[item] if len(parameters) == 2: new_name = parameters[1] if new_name in self.logs: return r.Status(False, "\tItem \"%s\" already exists" % new_name) if self.tags: for tag, items in self.tags.items(): self.tags[tag] = [new_name if (i == item.name) else i for i in items] self.save_tags() return item.rename(new_name) elif len(parameters) == 3: col = parameters[1] new_name = parameters[2] if new_name in item.get_columns(): return r.Status(False, "\tColumn \"%s\" already exists" % new_name) return item.rename(new_name, col)
def parse_date(string): """Convert a string input into a date object.""" today = date.today() year = today.year month = today.month day = today.day date_in = string.split('/') try: if len(date_in) == 2: if date_in[1]: day = int(date_in[1]) month = int(date_in[0]) elif date_in[0]: day = int(date_in[0]) elif len(date_in) == 3: month, day, year = list(map(int, date_in)) return date(year, month, day) except ValueError: return r.Status(False, "\tInvalid date entered: " + string)
def get_entries(self, filter_col=None, filter_op=None, value=None, col=None): """Pull up entries corresponding to filter.""" filters = { "=" : lambda c, v : c == v, ">" : lambda c, v : c > v, "<" : lambda c, v : c < v, ">=" : lambda c, v : c >= v, "<=" : lambda c, v : c <= v, } if filter_col: if filter_col not in self.get_columns(): return r.Status(False, "\tColumn \"%s\" not found\n\tValid columns: %s" % (filter_col, ", ".join(self.get_columns()))) if filter_op not in filters: return r.Status(False, "\tFilter operation \"%s\" invalid\n\tValid operations: %s" % (filter_op, ", ".join(filters.keys()))) column = self.data[filter_col].map(en.parse_data) value = en.parse_data(value) if type(column) is r.Status: return column if type(value) is r.Status: return value try: mask = filters[filter_op](column, value) except TypeError: return r.Status(False, "\tInvalid type entered (%s) for column type (%s)" % (type(value), type(column[0]))) else: mask = [True for _ in range(len(self.data))] if col: col = en.parse_data(col) if type(col) == str: if col not in self.get_columns(): return r.Status(False, "\tColumn \"%s\" not found\n\tValid columns: %s" % (col, ", ".join(self.get_columns()))) elif type(col) == list: if col[1] > len(self.get_columns()): return r.Status(False, "\tColumn index %d out of bounds, max is %d" % (col[1], len(self.get_columns()))) col = self.get_columns()[col[0]:col[1]:col[2]] elif type(col) == r.Status: return col else: col = self.get_columns() filtered_data = self.data[col][mask] if type(filtered_data) == pd.core.series.Series: filtered_data = filtered_data.to_frame() print(filtered_data) filtered_data.to_csv("logs/last.txt") return r.Status(True)
def delete_item(self): """Delete this item.""" os.remove(self.directory) return r.Status(True)