Example #1
0
class Session:
    def __init__(self, name=None, email=None):
        self.ps = None
        self.name = name
        self.email = email
        self.__start()

    def __start(self):
        dt = datetime.today()
        a = dt.time().hour
        greet = None
        if 0 <= a < 4:
            greet = 'Gute Nacht'
        elif 4 <= a < 12:
            greet = 'Guten Morgen'
        elif 12 <= a < 18:
            greet = 'Guten Tag'
        elif 8 <= a < 24:
            greet = 'Guten Abend'
        text = f'{greet}, mein Jarl!\n'
        self.__print(text)
        self.__ask_for_person()
        self.__run()

    def __ask_for_person(self):
        if self.name and self.email:
            self.__print(f'Logging as {self.name} ({self.email})...\n')
        else:
            text = 'Please type your profile name\n'
            self.__print(text)
            self.name = self.__input()
            text = 'Please type your profile email\n'
            self.__print(text)
            self.email = self.__input()

        self.ps = Person(self.name, self.email)
        profile_exists = self.ps.profile_exists()
        if profile_exists:
            text = 'Loading existing profile...\n'
        else:
            text = 'Create new profile...\n'
        self.__print(text)
        self.ps.load_progress()

    def __run(self):
        while True:
            text = "\nWas wollen Sie?\n"
            prompt = OrderedDict((
                ('1', 'start work'),
                ('2', 'log work'),
                ('3', 'show quests'),
                ('4', 'show logs'),
                ('5', 'add quest'),
                ('6', 'edit quest meta'),
                ('7', 'remove quest'),
            ))
            prompt.update({'x': 'exit'})
            text += self.__construct_prompt(prompt)
            self.__print(text)
            i = self.__input(values=prompt)

            if i == '1':
                self.__start_work()
            elif i == '2':
                self.__log_work()
            elif i == '3':
                self.__show_quests()
            elif i == '4':
                self.__show_logs()
            elif i == '5':
                self.__add_quest()
            elif i == '6':
                self.__edit_quest_meta()
            elif i == '7':
                self.__remove_quest()
            elif i == 'x':
                break

        self.__exit()

    def __start_work(self):
        enumeration = enumerate(
            (q for q in self.ps.quests
             if self.ps.quests[q]['start_timestamp'] is None), 1)
        quests_dict = OrderedDict((str(i), name) for i, name in enumeration)
        if quests_dict:
            text = 'Which quest you want to start?\n'
            quests_dict.update({'x': 'exit'})
            text += self.__construct_prompt(quests_dict)
            self.__print(text)
            i = self.__input(values=quests_dict)
            if i == 'x':
                return
            quest_name = quests_dict[i]

            timestamp = None
            while True:
                text = "When you gonna start it?\n"
                prompt = OrderedDict((
                    ('1', 'now'),
                    ('2', 'custom time in the past'),
                ))
                prompt.update({'x': 'exit'})
                text += self.__construct_prompt(prompt)
                self.__print(text)
                i = self.__input(values=prompt)
                if i == '2':
                    timestamp = self.__input_timestamp()
                elif i == 'x':
                    return

                try:
                    self.ps.start_work(quest_name, timestamp=timestamp)
                except PersonError as e:
                    text = f'{e}\n'
                    self.__print(text)
                    continue

                break
        else:
            text = 'No quests that can be started. You should create a quest first\n'
            self.__print(text)

    def __log_work(self):
        enumeration = enumerate(
            (q for q in self.ps.quests
             if self.ps.quests[q]['start_timestamp'] is not None), 1)
        quests_dict = OrderedDict((str(i), name) for i, name in enumeration)
        if quests_dict:
            quests_dict.update({'x': 'exit'})
            text = 'Which quest you want to stop?\n'
            text += self.__construct_prompt(quests_dict)
            self.__print(text)
            i = self.__input(values=quests_dict)
            if i == 'x':
                return
            quest_name = quests_dict[i]
            quest = self.ps.quests[quest_name]

            timestamp = None
            text = "When you gonna stop it?\n"
            prompt = OrderedDict((
                ('1', 'now'),
                ('2', 'custom time in the past'),
            ))
            prompt.update({'x': 'exit'})
            text += self.__construct_prompt(prompt)
            self.__print(text)
            i = self.__input(values=prompt)
            if i == '2':
                timestamp = self.__input_timestamp()
            elif i == 'x':
                return

            bookmark = ''
            while True:
                text = 'Please type a description of that time evaluation\n'
                self.__print(text)
                i = self.__input()
                if not i:
                    continue
                bookmark = i
                break

            points = {}
            for point_name in quest['points']:
                point = quest['points'][point_name]
                typ = point['type']
                if typ == 'int':
                    text = f'How much {point_name} did you earn?\n'
                    prompt = OrderedDict((
                        ('1', 'minutes from start to log'),
                        ('2', f'custom number of {point_name}'),
                    ))
                    prompt.update({'x': 'exit'})
                    text += self.__construct_prompt(prompt)
                    self.__print(text)
                    i = self.__input(values=prompt)
                    if i == '1':
                        timedelta = (
                            (timestamp or parse_timestamp(datetime.today())) -
                            quest['start_timestamp'])
                        i = timedelta.days * 60 * 24 + ceil(
                            timedelta.seconds / 60)
                    elif i == '2':
                        while True:
                            text = f'How much {point_name} did you earn?\n'
                            self.__print(text)
                            i = self.__input()
                            try:
                                i = int(i)
                            except ValueError:
                                self.__print(
                                    f'Please type exact number of {point_name}'
                                )
                                continue
                            break
                    elif i == 'x':
                        return

                elif typ == 'bool':
                    text = f'Have you done {point_name}?\n'
                    prompt = OrderedDict((
                        ('y', 'yes'),
                        ('n', 'no'),
                    ))
                    prompt.update({'x': 'exit'})
                    text += self.__construct_prompt(prompt)
                    self.__print(text)
                    i = self.__input(values=prompt)
                    if i == 'x':
                        return
                    i = i == 'y'
                points[point_name] = i

            self.ps.log_work(quest_name, points, bookmark, timestamp=timestamp)
        else:
            text = 'No started quests.\n'
            self.__print(text)

    def __show_quests(self):
        quests = self.ps.show_quests()
        if quests:
            text = 'Current quests:\n'
            for quest_name, quest in self.__sort_quests(quests):
                started_mark = "not started"
                if quest["start_timestamp"] is not None:
                    started_mark = "STARTED"
                    if quest["start_timestamp"].date() < datetime.today().date(
                    ):
                        started_mark += f' {quest["start_timestamp"].date()}'
                    started_mark += f' at {quest["start_timestamp"].time()} {quest["start_timestamp"].tzinfo}'
                text += f' > {quest_name} [{started_mark}]:\n' \
                        f'   {quest["instruction"]}\n' \
                        f'   points:\n'
                for point_name in quest['points']:
                    point = quest['points'][point_name]
                    text += f'      {round(point["points_to_do"], 3)} {point_name} to do ({point["norm"]} per day)\n'
        else:
            text = 'No quests.\n'
        self.__print(text)

    def __show_logs(self):
        logs = self.ps.show_logs(100)
        self.__print(
            f'Show last 100 logs:\n'
            f'{"timestamp": <28}{"quest_name": <27}{"log": <16}{"points": <27}mark\n'
        )
        for timestamp, quest_id, log, points, mark in logs:
            q_name = 'unknown'
            for q_name, quest in self.ps.quests.items():
                if quest['id'] == int(quest_id):
                    break
            self.__print(
                f'{timestamp: <28}{q_name: <27}{log: <16}{points: <27}{mark}\n'
            )

    def __add_quest(self):
        while True:
            text = '\nPlease type a new quest name\n'
            self.__print(text)
            quest_name = self.__input()
            if not quest_name:
                continue
            break

        while True:
            text = '\nPlease type a new quest start instruction\n'
            self.__print(text)
            instruction = self.__input()
            if not instruction:
                continue
            break

        points = self.__input_new_points()
        if points:
            self.ps.add_quest(quest_name, points, instruction)

    def __edit_quest_meta(self):
        quests_dict = OrderedDict(
            (str(i), name) for i, name in enumerate(self.ps.quests, 1))
        if quests_dict:
            quests_dict.update({'x': 'exit'})
            text = 'Which quest you want to edit?\n'
            text += self.__construct_prompt(quests_dict)
            self.__print(text)
            i = self.__input(values=quests_dict)
            if i == 'x':
                return
            quest_name = quests_dict[i]
            quest = self.ps.quests[quest_name]

            instruction = None
            text = f'Do you want to change this quest instruction?\n' \
                   f'(current: {quest["instruction"]})\n'
            prompt = OrderedDict((
                ('y', 'yes'),
                ('n', 'no'),
            ))
            prompt.update({'x': 'exit'})
            text += self.__construct_prompt(prompt)
            self.__print(text)
            i = self.__input(values=prompt)
            if i == 'x':
                return
            if i == 'y':
                text = 'Please type a new instruction\n'
                self.__print(text)
                i = self.__input()
                instruction = i

            self.ps.edit_quest_meta(quest_name, instruction=instruction)
            self.__print('The quest meta updated.\n')
        else:
            text = 'No quests. You should create a quest first\n'
            self.__print(text)

    def __remove_quest(self):
        quests_dict = OrderedDict(
            (str(i), name) for i, name in enumerate(self.ps.quests, 1))
        if quests_dict:
            quests_dict.update({'x': 'exit'})
            text = '\nWhich quest you want to remove?\n'
            text += self.__construct_prompt(quests_dict)
            self.__print(text)
            i = self.__input(values=quests_dict)
            if i == 'x':
                return
            quest_name = quests_dict[i]

            text = f'\nRemove quest {quest_name}. Are you sure?\n'
            prompt = OrderedDict((
                ('y', 'yes'),
                ('n', 'no'),
            ))
            text += self.__construct_prompt(prompt)
            self.__print(text)
            i = self.__input(values=prompt)
            if i == 'y':
                self.ps.remove_quest(quest_name)
                self.__print('Quest removed.\n')
        else:
            text = 'No quests. You should create a quest first\n'
            self.__print(text)

    def __exit(self):
        text = 'Auf wiedersehen, mein Jarl\n'
        self.__print(text)
        exit(88)

    def __input(self, values=None):
        while True:
            i = input()
            if values is not None and i not in values:
                text = f'Please type one of the following:\n'
                text += self.__construct_prompt(values)
                self.__print(text)
                continue
            break
        return i

    def __input_timestamp(self):
        text = 'Which date?\n'
        prompt = OrderedDict((
            ('1', 'today'),
            ('2', 'other date'),
        ))
        text += self.__construct_prompt(prompt)
        self.__print(text)
        i = self.__input(values=prompt)
        str_date = ''
        if i == '1':
            str_date = str(datetime.today().date())
        elif i == '2':
            while True:
                text = 'Please type exact date in format dd-mm-yyyy\n'
                self.__print(text)
                i = self.__input()
                try:
                    parse_timestamp(f'{i} 00:00')
                except ValueError:
                    text = 'Incorrect date format.\n'
                    self.__print(text)
                    continue
                str_date = i
                break

        str_time = ''
        while True:
            text = 'Please type exact time in format HH:MM or HH:MM:SS\n'
            self.__print(text)
            i = self.__input()
            try:
                parse_timestamp(f'15-10-1994 {i}')
            except ValueError:
                text = 'Incorrect time format.\n'
                self.__print(text)
                continue
            str_time = i
            break

        str_tz = ''
        if config.ASK_FOR_TIMEZONE:
            while True:
                text = 'Which timezone?\n'
                prompt = OrderedDict((
                    ('1', 'use computer tz'),
                    ('2', 'custom tz'),
                ))
                text += self.__construct_prompt(prompt)
                self.__print(text)
                i = self.__input(values=prompt)
                if i == '2':
                    text = 'Please type timezone in format +(-)H or +(-)HH or +(-)HHMM'
                    self.__print(text)
                    i = self.__input()
                    if i == '0':
                        i = '+0'
                    try:
                        parse_timestamp(f'15-10-1994 21:00:00{i}')
                    except ValueError:
                        text = 'Incorrect timezone format.\n'
                        self.__print(text)
                        continue
                    str_tz = i
                break
        timestamp = parse_timestamp(f'{str_date} {str_time}{str_tz}')

        return timestamp

    def __input_new_points(self):
        points = {}
        while True:
            while True:
                text = '\n' \
                       'Please type a new point name with the following rules:\n' \
                       ' * a numeric point should be called like "minutes" or "sentences"\n' \
                       ' * a boolean point should be called like "learn grammar" or "special exercise"\n'
                self.__print(text)
                point_name = self.__input()
                if not point_name:
                    self.__print('Empty point name given.\n')
                    continue
                if point_name in points:
                    self.__print('A point with that name already exists.\n')
                    continue
                break

            while True:
                text = '\n' \
                       'Please enter a daily rate of this point with the following rules:\n' \
                       ' * the daily rate number means number of some points that you earn, like minutes or sentences\n' \
                       ' * a boolean point should have daily rate about 1-3 depending on how much times in a day you gonna do that action"\n'
                self.__print(text)
                norm = self.__input()
                if not norm:
                    self.__print('Empty daily rate given.\n')
                    continue
                try:
                    norm = float(norm.strip().replace(',', '.'))
                except ValueError:
                    text = 'Daily rate must be numeric.\n'
                    self.__print(text)
                    continue
                break

            text = '\n' \
                   'Please choose type of the new point\n'
            prompt = OrderedDict((
                ('1', 'numeric, integer'),
                ('2', 'boolean, a fact of an action'),
            ))
            text += self.__construct_prompt(prompt)
            self.__print(text)
            typ = self.__input(values=prompt)
            if typ == '1':
                typ = 'int'
            elif typ == '2':
                typ = 'bool'

            text = f'\n' \
                   f'Add point "{point_name}" [{typ}] with daily rate {norm}. Are you sure?\n'
            prompt = OrderedDict((
                ('y', 'yes, add that point'),
                ('n', 'no, skip that point'),
            ))
            text += self.__construct_prompt(prompt)
            self.__print(text)
            i = self.__input(values=prompt)
            if i == 'y':
                points[point_name] = {'norm': norm, 'type': typ}
                text = 'Added that point.\n'
                self.__print(text)

            text = "\nWould you like to add another point?\n"
            prompt = OrderedDict((
                ('y', 'yes'),
                ('n', 'no'),
            ))
            text += self.__construct_prompt(prompt)
            self.__print(text)
            i = self.__input(values=prompt)
            if i == 'n':
                break

        return points

    @staticmethod
    def __sort_quests(quests):
        for quest in quests.values():
            quest['_priority'] = 0.
            for point in quest['points'].values():
                quest['_priority'] += point['points_to_do'] / max(
                    1, point['norm'])
            if quest['points']:
                quest['_priority'] /= len(quest['points'])
        res = sorted(((n, q) for n, q in quests.items()),
                     key=lambda x: x[1]['_priority'],
                     reverse=True)
        for n, q in res:
            del q['_priority']
        return res

    @staticmethod
    def __print(text):
        print(text, end='')

    @staticmethod
    def __construct_prompt(dictionary):
        text = []
        for i, t in dictionary.items():
            text.append(f'  [{i}] {t}\n')
        text = ''.join(text)
        return text