#!/usr/bin/env python3 import json import os import pwd import time class Wilty(): def __init__(self): self.save_dir = ".botany" self.plant_file = "_plant_data.json" self.visit_file = "visitors.json" self.plants = {} self.loadData() def convertTS(self, ts): """Convert a timestamp in seconds to a legible format, e.g. 12d 12h 12m.""" days = int(ts / 86400) hours = int((ts % 86400) / 3600) mins = round(((ts % 86400) % 3600) / 60) return str(days) + "d " + str(hours) + "h " + str(mins) + "m" def getUsers(self): """Load usernames into the plants dictionary as keys.""" sys_users = pwd.getpwall() for u in sys_users: if os.path.exists("/home/" + u.pw_name + "/" + self.save_dir): self.plants[u.pw_name] = {} def checkPlant(self, user): """Given the username, calculates the last time since the user's plant was watered, estimates whether it is dead and updates the plants dictionary.""" # Get last visitor timestamp and check if later than user's visitor_ts = 0 if self.plants[user]["visitors"] != []: visitor_ts = self.plants[user]["visitors"][-1]["timestamp"] watered = max(self.plants[user]["last_watered"], visitor_ts) time_since = int(time.time()) - watered self.plants[user]["time_since"] = time_since # >432000 (5 days) = dead plant if time_since > 432000: self.plants[user]["is_dead"] = True self.plants[user]["time_since_fmt"] = self.convertTS(time_since) def loadData(self): """Load plant and visitor data into plants dictionary.""" self.getUsers() for u in self.plants: sv_dir = "/home/" + u + "/" + self.save_dir + "/" # Get plant data if os.access(sv_dir + u + self.plant_file, os.R_OK): with open(sv_dir + u + self.plant_file) as plant_fh: plant_json = plant_fh.read() self.plants[u] = json.loads(plant_json) self.plants[u]["allow_query"] = True else: self.plants[u]["allow_query"] = False # Get visitor data if os.access(sv_dir + self.visit_file, os.R_OK) and \ os.access(sv_dir + self.visit_file, os.W_OK): with open(sv_dir + self.visit_file) as visit_fh: visit_json = visit_fh.read() self.plants[u]["visitors"] = json.loads(visit_json) self.plants[u]["allow_visit"] = True else: self.plants[u]["allow_visit"] = False self.plants[u]["visitors"] = [] # Update plant watered state if plant data is readable if self.plants[u]["allow_query"]: self.checkPlant(u) def listLivePlants(self, *args, **kwargs): """List living plants open to visitors.""" sort_l = [] for p in self.plants: if self.plants[p]["allow_query"] and \ self.plants[p]["allow_visit"] and \ not self.plants[p]["is_dead"]: sort_l.append((p, self.plants[p]["description"].split()[-1], \ self.plants[p]["time_since_fmt"], \ self.plants[p]["time_since"])) # Sort by column column = kwargs.get("sort", "") if column == "user": sort_l.sort(key=lambda c: c[0]) elif column == "plant": sort_l.sort(key=lambda c: c[1]) else: sort_l.sort(key=lambda c: c[3], reverse=True) # Format output border = "-" * 47 print(border) print("{:<20s}{:<15s}{:<15s}".format("User", "Plant", "Last Watered")) print(border) for i in sort_l: print("{:<20s}{:<15s}{:<15s}".format(i[0], i[1], i[2])) print(border) instance = Wilty() instance.listLivePlants()