Skip to content

Commit 3f7b2d5

Browse files
authored
Merge pull request #33 from hardaker/pets-commands
Pets commands
2 parents 3cdfd04 + ce75464 commit 3f7b2d5

File tree

2 files changed

+198
-0
lines changed

2 files changed

+198
-0
lines changed

habitipy/api.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,11 @@ def _prepare_request(self, backend=requests, **kwargs):
252252
raise ValueError('{} is not an endpoint!'.format(uri))
253253
method = self._node.method
254254
headers = self._make_headers()
255+
256+
# allow a caller to force URI parameters when API document is incorrect
257+
if 'uri_params' in kwargs:
258+
uri += '?' + '&'.join([str(x) + '=' + str(y) for x, y in kwargs['uri_params'].items()])
259+
255260
query = {}
256261
if 'query' in self._node.params:
257262
for name, param in self._node.params['query'].items():

habitipy/cli.py

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
import os
1010
import json
1111
import uuid
12+
import time
1213
from bisect import bisect
1314
from collections.abc import Mapping
15+
from collections import defaultdict
1416
from itertools import chain
1517
from textwrap import dedent
1618
from typing import List, Union, Dict, Any # pylint: disable=unused-import
@@ -363,6 +365,197 @@ def main(self):
363365
print(res)
364366

365367

368+
@HabiticaCli.subcommand('pets')
369+
class Pets(ApplicationWithApi):
370+
"""Core inheritable class for dealing with actions on pets."""
371+
DESCRIPTION = _('List pets and their status')
372+
pet_specifier = cli.SwitchAttr(
373+
['-P', '--pet'],
374+
help=_('Only show information about a particular pet')) # noqa: Q000
375+
color_specifier = cli.SwitchAttr(
376+
['-C', '--color'],
377+
help=_('Only show information about a particular color')) # noqa: Q000
378+
379+
def get_full_percent(self, amount: int):
380+
"""Return the percentage of "fullness" for a pet."""
381+
if amount == -1:
382+
amount = 100
383+
else:
384+
amount *= 2
385+
return str(amount)
386+
387+
def get_food_needed(self, pet_fullness: int, amount_per_food: int = 5) -> int:
388+
"""Return the amount of food needed to feed a pet till full."""
389+
if pet_fullness == -1:
390+
return 0
391+
return int((50 - int(pet_fullness)) / amount_per_food)
392+
393+
def is_hatchable(self, user: dict, pet: str, color: str) -> bool:
394+
"""Return true when a pat of a particular type and color can be hatched."""
395+
combined = pet + '-' + color
396+
397+
# check if pet exists or name is wrong
398+
if user['items']['pets'].get(combined, 100) != -1:
399+
return False
400+
401+
if color not in user['items']['hatchingPotions'] or pet not in user['items']['eggs']:
402+
return False
403+
if user['items']['hatchingPotions'][color] > 0 and user['items']['eggs'][pet] > 0:
404+
return True
405+
return False
406+
407+
408+
@Pets.subcommand('list')
409+
class ListPets(Pets):
410+
"""Lists all pets from the inventory."""
411+
def main(self): # pylint: disable=too-many-branches
412+
super().main()
413+
user = self.api.user.get()
414+
print(_('Pets:'))
415+
416+
color_specifier = self.color_specifier
417+
if color_specifier:
418+
color_specifier = color_specifier[0].capitalize() + color_specifier[1:]
419+
pet_specifier = self.pet_specifier
420+
if pet_specifier:
421+
pet_specifier = pet_specifier[0].capitalize() + pet_specifier[1:]
422+
423+
# split pets into type and color
424+
pet_summaries = defaultdict(dict)
425+
for pet in user['items']['pets']:
426+
(pettype, color) = pet.split('-')
427+
pet_summaries[pettype][color] = user['items']['pets'][pet]
428+
429+
for pet in pet_summaries:
430+
if pet_specifier and pet != pet_specifier:
431+
continue
432+
pet_printed = False
433+
for color in pet_summaries[pet]:
434+
if color_specifier and color != color_specifier:
435+
continue
436+
437+
if not pet_printed:
438+
print(f' {pet}:')
439+
pet_printed = True
440+
441+
pet_full_level = pet_summaries[pet][color]
442+
if pet_full_level == -1:
443+
full_percentage = colors.red | _('No Pet')
444+
if self.is_hatchable(user, pet, color):
445+
full_percentage += ' ' + (colors.green | _('(hatchable)'))
446+
elif pet + '-' + color in user['items']['mounts']:
447+
full_percentage = colors.green | '100%'
448+
else:
449+
full_percentage = self.get_full_percent(pet_full_level) + '%'
450+
if full_percentage == '100%':
451+
full_percentage = colors.green | full_percentage
452+
else:
453+
full_percentage = colors.yellow | full_percentage
454+
print(f' {color:<30} {full_percentage}')
455+
456+
457+
@Pets.subcommand('feed')
458+
class FeedPet(Pets):
459+
"""Feeds a pet or pets with specified food."""
460+
sleep_time = cli.SwitchAttr(
461+
['-S', '--sleep-time'], argtype=int, default=1,
462+
help=_('Time to wait between feeding each pet to avoid overloading the server')) # pylint: disable=line-too-long
463+
maximum_food = cli.SwitchAttr(
464+
['-M', '--maxmimum-food'], argtype=int, default=10,
465+
help=_('Maximum amount of food to feed a pet')
466+
)
467+
468+
def main(self, *food):
469+
super().main()
470+
if len(food) != 1:
471+
self.log.error(_('error: must specify one food to feed.')) # noqa: Q000
472+
return
473+
474+
food = food[0]
475+
user = self.api.user.get()
476+
pets = user['items']['pets']
477+
mounts = user['items']['mounts']
478+
479+
color_specifier = self.color_specifier
480+
if color_specifier:
481+
color_specifier = color_specifier[0].capitalize() + color_specifier[1:]
482+
pet_specifier = self.pet_specifier
483+
if pet_specifier:
484+
pet_specifier = pet_specifier[0].capitalize() + pet_specifier[1:]
485+
486+
for pet in pets:
487+
(pettype, color) = pet.split('-')
488+
if pet_specifier and pettype != pet_specifier:
489+
continue
490+
if color_specifier and color != color_specifier:
491+
continue
492+
493+
food_needed = self.get_food_needed(pets[pet])
494+
if food_needed > 0 and pet not in mounts:
495+
food_amount = min(food_needed, self.maximum_food)
496+
print(_(f'feeding {food_amount} {food} to {color} {pettype}'))
497+
response = self.api.user.feed[pet][food].post(uri_params={
498+
'amount': food_amount,
499+
})
500+
print(_(f' new fullness: {self.get_full_percent(response)}%'))
501+
time.sleep(self.sleep_time)
502+
else:
503+
print(_(f'NOT feeding {color} {pettype}'))
504+
505+
506+
@Pets.subcommand('hatch')
507+
class HatchPet(Pets):
508+
"""Hatches pets with eggs when possible."""
509+
sleep_time = cli.SwitchAttr(
510+
['-S', '--sleep-time'], argtype=int, default=1,
511+
help=_('Time to wait between feeding each pet to avoid overloading the server')) # pylint: disable=line-too-long
512+
maximum_food = cli.SwitchAttr(
513+
['-M', '--maxmimum-food'], argtype=int, default=10,
514+
help=_('Maximum amount of food to feed a pet')
515+
)
516+
517+
def main(self):
518+
super().main()
519+
user = self.api.user.get()
520+
pets = user['items']['pets']
521+
522+
color_specifier = self.color_specifier
523+
if color_specifier:
524+
color_specifier = color_specifier[0].capitalize() + color_specifier[1:]
525+
pet_specifier = self.pet_specifier
526+
if pet_specifier:
527+
pet_specifier = pet_specifier[0].capitalize() + pet_specifier[1:]
528+
529+
for pet in pets:
530+
(pettype, color) = pet.split('-')
531+
532+
if pet_specifier and pettype != pet_specifier:
533+
continue
534+
if color_specifier and color != color_specifier:
535+
continue
536+
537+
if self.is_hatchable(user, pettype, color):
538+
print(_(f'hatching {color} {pettype}'))
539+
self.api.user.hatch[pettype][color].post()
540+
time.sleep(self.sleep_time)
541+
else:
542+
print(_(f'NOT hatching {color} {pettype}'))
543+
544+
545+
@HabiticaCli.subcommand('food')
546+
class Food(ApplicationWithApi):
547+
"""Lists food from the inventory."""
548+
DESCRIPTION = _('List inventory food and their quantities available')
549+
550+
def main(self):
551+
super().main()
552+
user = self.api.user.get()
553+
food_list = user['items']['food']
554+
food_list_keys = sorted(food_list, key=lambda x: food_list[x])
555+
for food in food_list_keys:
556+
print(f'{food:<30}: {food_list[food]}')
557+
558+
366559
@HabiticaCli.subcommand('habits')
367560
class Habits(TasksPrint): # pylint: disable=missing-class-docstring
368561
DESCRIPTION = _("List, up and down habit tasks") # noqa: Q000

0 commit comments

Comments
 (0)