Add stats
This commit is contained in:
		
							parent
							
								
									79a4d3411b
								
							
						
					
					
						commit
						93a0301b6e
					
				
							
								
								
									
										191
									
								
								wilty.py
									
									
									
									
									
								
							
							
						
						
									
										191
									
								
								wilty.py
									
									
									
									
									
								
							| @ -11,28 +11,50 @@ class Wilty(): | ||||
|         self.save_dir = ".botany" | ||||
|         self.plant_file = "_plant_data.json" | ||||
|         self.visit_file = "visitors.json" | ||||
|         self.harvest_file = "harvest_file.json" | ||||
|         self.plants = {} | ||||
|         self.loadData() | ||||
|         self.stats_model = { | ||||
|             "total": 0, | ||||
|             "live": 0, | ||||
|             "seed": 0, | ||||
|             "young": 0, | ||||
|             "mature": 0, | ||||
|             "succ": 0, | ||||
|             "tree": 0, | ||||
|             "nonflower": 0, | ||||
|             "flower": 0, | ||||
|             "gen": 0, | ||||
|             "avg_gen": 0, | ||||
|             "harv": 0, | ||||
|             "avg_harv": 0, | ||||
|             } | ||||
|         self.border = "-" * 47 | ||||
| 
 | ||||
|     def convertTS(self, ts): | ||||
|         """Convert a timestamp in seconds to a legible format, e.g. 12d 12h | ||||
|         12m.""" | ||||
|     def formatTime(self, ts): | ||||
|         """Format an integer timestamp (seconds) to a timestamp string (days, | ||||
|         hours and minutes), 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.""" | ||||
|         """Load usernames into the plants dictionary as keys. Only users whose | ||||
|         plant data is peer-readable will be added. | ||||
|         """ | ||||
|         sys_users = pwd.getpwall() | ||||
|         for u in sys_users: | ||||
|             if os.path.exists("/home/" + u.pw_name + "/" + self.save_dir): | ||||
|             sv_dir = "/home/" + u.pw_name + "/" + self.save_dir + "/" | ||||
|             if os.path.exists(sv_dir) and \ | ||||
|                     os.access(sv_dir + u.pw_name + self.plant_file, os.R_OK): | ||||
|                 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.""" | ||||
|         dictionary. | ||||
|         """ | ||||
|         # Get last visitor timestamp and check if later than user's | ||||
|         visitor_ts = 0 | ||||
|         if self.plants[user]["visitors"] != []: | ||||
| @ -40,24 +62,20 @@ class Wilty(): | ||||
|         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 | ||||
|         # >5 days = dead plant | ||||
|         if time_since > 432000: | ||||
|             self.plants[user]["is_dead"] = True | ||||
|         self.plants[user]["time_since_fmt"] = self.convertTS(time_since) | ||||
|         self.plants[user]["time_since_fmt"] = self.formatTime(time_since) | ||||
| 
 | ||||
|     def loadData(self): | ||||
|         """Load plant and visitor data into plants dictionary.""" | ||||
|     def getRawData(self): | ||||
|         """Load plant data into the 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 | ||||
|             with open(sv_dir + u + self.plant_file) as plant_fh: | ||||
|                 plant_json = plant_fh.read() | ||||
|             self.plants[u] = json.loads(plant_json) | ||||
|             # 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): | ||||
| @ -68,37 +86,146 @@ class Wilty(): | ||||
|             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) | ||||
|             # Get harvest data | ||||
|             if os.access(sv_dir + self.harvest_file, os.R_OK): | ||||
|                 with open(sv_dir + self.harvest_file) as harvest_fh: | ||||
|                     harvest_json = harvest_fh.read() | ||||
|                 self.plants[u]["harvests"] = json.loads(harvest_json) | ||||
|             else: | ||||
|                 self.plants[u]["harvests"] = {} | ||||
|             # Update plant watered state | ||||
|             self.checkPlant(u) | ||||
| 
 | ||||
|     def listLivePlants(self, *args, **kwargs): | ||||
|         """List living plants open to visitors.""" | ||||
|         """Prints a list of living plants open to visitors. Optionally specify | ||||
|         a sort flag to sort the list alphabetically by username or plant type, | ||||
|         e.g.  sort=\"plant\". By default, the list is sorted by the longest | ||||
|         interval since the last watered time descending. | ||||
|         """ | ||||
|         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"]: | ||||
|             if 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": | ||||
|         if kwargs.get("sort", "") == "user": | ||||
|             sort_l.sort(key=lambda c: c[0]) | ||||
|         elif column == "plant": | ||||
|         elif kwargs.get("sort", "") == "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(self.border) | ||||
|         print("{:<20s}{:<15s}{:<15s}".format("User", "Plant", "Last Watered")) | ||||
|         print(border) | ||||
|         print(self.border) | ||||
|         for i in sort_l: | ||||
|             print("{:<20s}{:<15s}{:<15s}".format(i[0], i[1], i[2])) | ||||
|         print(border) | ||||
|         print(self.border) | ||||
| 
 | ||||
|     def countPlantStages(self, key, count): | ||||
|         """Count the plants in different stages (seedling, young, mature). Take | ||||
|         as arguments a key for plants dictionary lookup and a dictionary to | ||||
|         append the results.""" | ||||
|         if "stage" in self.plants[key]: | ||||
|             if self.plants[key]["stage"] == ("mature" or "flowering" or \ | ||||
|                     "seed-bearing"): | ||||
|                 count["mature"] += 1 | ||||
|             elif self.plants[key]["stage"] == "young": | ||||
|                 count["young"] += 1 | ||||
|             else: | ||||
|                 count["seed"] += 1 | ||||
|         else: | ||||
|             count["seed"] += 1 | ||||
| 
 | ||||
|     def countPlantGroups(self, key, count): | ||||
|         """Count plants in type groups.""" | ||||
|         succulents = ["agave", "aloe", "cactus", "lithops", "pachypodium"] | ||||
|         trees = ["baobab", "ficus", "palm"] | ||||
|         noflowers = ["fern", "moss"] | ||||
|         plant_type = self.plants[key]["description"].split()[-1] | ||||
|         if plant_type in succulents: | ||||
|             count["succ"] += 1 | ||||
|         elif plant_type in trees: | ||||
|             count["tree"] += 1 | ||||
|         elif plant_type in noflowers: | ||||
|             count["nonflower"] += 1 | ||||
|         elif plant_type != ("seed" or "seedling"): | ||||
|             count["flower"] += 1 | ||||
| 
 | ||||
|     def countGenRate(self, key, count): | ||||
|         """Count total generation rates.""" | ||||
|         if "generation" in self.plants[key]: | ||||
|             count["gen"] += self.plants[key]["generation"] | ||||
|         else: | ||||
|             count["gen"] += 1 | ||||
| 
 | ||||
|     def avgGenRate(self, count): | ||||
|         """Calculate the average plant generation.""" | ||||
|         count["avg_gen"] = round(count["gen"] / count["total"], \ | ||||
|                 3) | ||||
|          | ||||
|     def countHarvests(self, key, count): | ||||
|         """Count the total number of harvests.""" | ||||
|         if self.plants[key]["harvests"] != {}: | ||||
|             count["harv"] += len(self.plants[key]["harvests"]) | ||||
|         else: | ||||
|             count["harv"] += 0 | ||||
| 
 | ||||
|     def avgHarvests(self, count): | ||||
|         """Calculate the average number of harvests per user.""" | ||||
|         count["avg_harv"] = round(count["harv"] / count["total"], 3) | ||||
| 
 | ||||
|     def genStats(self, subset): | ||||
|         """Generate garden stats of all plants or only live plants.""" | ||||
|         stats = dict(self.stats_model) | ||||
|         if subset == "live": | ||||
|             for p in self.plants: | ||||
|                 if not self.plants[p]["is_dead"]: | ||||
|                     stats["live"] += 1 | ||||
|                     self.countPlantStages(p, stats) | ||||
|                     self.countPlantGroups(p, stats) | ||||
|                     self.countGenRate(p, stats) | ||||
|                     self.countHarvests(p, stats) | ||||
|             stats["total"] = stats["live"] | ||||
|         else: | ||||
|             for p in self.plants: | ||||
|                 self.countPlantStages(p, stats) | ||||
|                 self.countPlantGroups(p, stats) | ||||
|                 self.countGenRate(p, stats) | ||||
|                 self.countHarvests(p, stats) | ||||
|             stats["total"] = len(self.plants) | ||||
|         self.avgGenRate(stats) | ||||
|         self.avgHarvests(stats) | ||||
|         return stats | ||||
| 
 | ||||
|     def listStats(self, *args, **kwargs): | ||||
|         """Print plant stats. Optionally use a subset flag to get stats on live | ||||
|         plants only, e.g. subset=\"live\". By default, show stats based on the | ||||
|         total number of plants. | ||||
|         """ | ||||
|         if kwargs.get("subset", "all") == "live": | ||||
|             stats = self.genStats("live") | ||||
|         else: | ||||
|             stats = self.genStats("all") | ||||
|         print("Garden Stats (" + kwargs.get("subset", "all") + " plants)") | ||||
|         print(self.border) | ||||
|         print( | ||||
|             "Avg. generation: " +  str(stats["avg_gen"]) + | ||||
|             "\nAvg. harvests per user: " +  str(stats["avg_harv"]) + | ||||
|             "\nSeedlings: " + str(stats["seed"]) + | ||||
|             "\nYoung plants: " + str(stats["young"]) +  | ||||
|             "\nMature plants: " + str(stats["mature"]) +   | ||||
|             "\nFlowering shrubs: " + str(stats["flower"]) +  | ||||
|             "\nNon-flowering plants: " + str(stats["nonflower"]) +  | ||||
|             "\nSucculents: " + str(stats["succ"]) + | ||||
|             "\nTrees: " + str(stats["tree"]) +  | ||||
|             "\nTotal plants: " + str(stats["total"]) | ||||
|             ) | ||||
|         print(self.border) | ||||
| 
 | ||||
| 
 | ||||
| instance = Wilty() | ||||
| instance.getRawData() | ||||
| instance.listLivePlants() | ||||
| instance.listStats() | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user