timeit

Програмиране с Python

Курс във Факултета по Математика и Информатика към СУ

Решение на Социална мрежа от Андрей Иванов

Обратно към всички решения

Към профила на Андрей Иванов

Резултати

  • 10 точки от тестове
  • 0 бонус точки
  • 10 точки общо
  • 8 успешни тест(а)
  • 0 неуспешни тест(а)

Код

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
import uuid
import datetime
import math
from collections import defaultdict


class User:
    def __init__(self, full_name):
        self.full_name = full_name
        self.uuid = uuid.uuid4()
        self.posts = []

    def add_post(self, post_content):
        if len(self.posts) == 50:
            self.posts.pop(0)
        time = datetime.datetime.now()
        self.posts.append(Post(self.uuid, time, post_content))

    def get_post(self):
        for post in self.posts:
            yield post


class Post:
    def __init__(self, author, published_at, content):
        self.author = author
        self.published_at = published_at
        self.content = content


def sort_posts(posts):
    length = len(posts)
    for i in range(0, length-1):
        max = posts[i]
        max_position = i
        for j in range(i+1, length):
            if posts[j].published_at > max.published_at:
                max = posts[j]
                max_position = j
        posts[i], posts[max_position] = posts[max_position], posts[i]


class SocialGraph:
    def __init__(self):
        self.graph = {}

    def add_user(self, user):
        for u in self.graph:
            if u.uuid == user.uuid:
                raise UserAlreadyExistsError
        self.graph[user] = []

    def get_user(self, user_uuid):
        for user in self.graph:
            if user.uuid == user_uuid:
                return user
        raise UserDoesNotExistError

    def user_in_graph(self, user_id):
        for user in self.graph:
            if user.uuid == user_id:
                return True
        return False

    def delete_user(self, user_uuid):
        if not self.user_in_graph(user_uuid):
            raise UserDoesNotExistError
        for user in self.graph:
            if user.uuid == user_uuid:
                del self.graph[user]
                break
        for user in self.graph:
            for user_child in self.graph[user]:
                if user_child.uuid == user_uuid:
                    self.graph[user].remove(user_child)

    def follow(self, follower, followee):
        user_follower = self.get_user(follower)
        user_followee = self.get_user(followee)
        if follower == followee:
            raise ToNotFollowMyselfError
        for user in self.graph:
            if user.uuid == followee:
                if user not in self.graph[user_follower]:
                    self.graph[user_follower].append(user)
                    break

    def unfollow(self, follower, followee):
        user_follower = self.get_user(follower)
        user_followee = self.get_user(followee)
        for user in self.graph:
            if user.uuid == followee:
                if user in self.graph[user_follower]:
                    self.graph[user_follower].remove(user)
                    break

    def is_following(self, follower, followee):
        user_follower = self.get_user(follower)
        user_followee = self.get_user(followee)
        if user_followee in self.graph[user_follower]:
            return True
        return False

    def followers(self, user_uuid):
        followee = self.get_user(user_uuid)
        followers = []
        for user in self.graph:
            if followee in self.graph[user]:
                followers.append(user)
        return set([follower.uuid for follower in followers])

    def following(self, user_uuid):
        user_follower = self.get_user(user_uuid)
        return set([user.uuid for user in self.graph[user_follower]])

    def friends(self, user_uuid):
        return self.followers(user_uuid) & self.following(user_uuid)

    def max_distance(self, user_uuid):
        start_user = self.get_user(user_uuid)
        if len(self.graph[start_user]) == 0:
            return math.inf
        max_distance = 0
        for user in self.graph:
            try:
                current_distance = self.min_distance(user_uuid, user.uuid)
                if current_distance > max_distance:
                    max_distance = current_distance
            except UsersNotConnectedError:
                continue
        return max_distance

    def min_distance(self, from_user_uuid, to_user_uuid):
        from_user = self.get_user(from_user_uuid)
        to_user = self.get_user(to_user_uuid)
        distance = 0
        queue = [from_user, True]
        used_nodes = [from_user]
        while len(queue) != 0:
            current_node = queue.pop(0)
            used_nodes.append(current_node)
            if current_node is True and len(queue) != 0:
                distance = distance + 1
                queue.append(True)
            elif current_node == to_user:
                return distance
            elif current_node in self.graph:
                for followee in self.graph[current_node]:
                    if followee not in used_nodes:
                        queue.append(followee)
        raise UsersNotConnectedError

    def nth_layer_followings(self, user_uuid, n):
        if not self.user_in_graph(user_uuid):
            raise UserDoesNotExistError
        if n <= 0:
            return set()
        nth_layer_list = []
        for user in self.graph:
            try:
                if self.min_distance(user_uuid, user.uuid) == n:
                    nth_layer_list.append(user.uuid)
            except UsersNotConnectedError:
                continue
        return set(nth_layer_list)

    def generate_feed(self, user_uuid, offset=0, limit=10):
        follower = self.get_user(user_uuid)
        followeds = self.graph[follower]
        followed_posts = []
        for followed in followeds:
            followed_posts += followed.posts
        sort_posts(followed_posts)
        del followed_posts[:offset]
        return followed_posts[:limit]


class UserDoesNotExistError(Exception):
    pass


class UserAlreadyExistsError(Exception):
    pass


class UsersNotConnectedError(Exception):
    pass


class ToNotFollowMyselfError(Exception):
    pass

Лог от изпълнението

........
----------------------------------------------------------------------
Ran 8 tests in 0.066s

OK

История (3 версии и 1 коментар)

Андрей обнови решението на 16.04.2016 17:55 (преди над 1 година)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
import uuid
import datetime
import math
from collections import defaultdict


class User:
    def __init__(self, full_name):
        self.full_name = full_name
        self.uuid = uuid.uuid4()
        self.posts = []

    def add_post(self, post_content):
        if len(self.posts) == 50:
            self.posts.pop(0)
        time = datetime.datetime.now()
        self.posts.append(Post(self.uuid, time, post_content))

    def get_post(self):
        for post in self.posts:
            yield post


class Post:
    def __init__(self, author, published_at, content):
        self.author = author
        self.published_at = published_at
        self.content = content


def sort_posts(posts):
    length = len(posts)
    for i in range(0, length-1):
        max = posts[i]
        max_position = i
        for j in range(i+1, length):
            if posts[j].published_at > max.published_at:
                max = posts[j]
                max_position = j
        posts[i], posts[max_position] = posts[max_position], posts[i]


def user_in_graph(graph, user_id):
    for user in graph:
        if user.uuid == user_id:
            return True
    return False


def get_user_help(graph, user_id):
    for user in graph:
        if user.uuid == user_id:
            return user


class SocialGraph:
    def __init__(self):
        self.graph = {}

    def add_user(self, user):
        for u in self.graph:
            if u.uuid == user.uuid:
                raise UserAlreadyExistsError
        self.graph[user] = []

    def get_user(self, user_uuid):
        for user in self.graph:
            if user.uuid == user_uuid:
                return user
        raise UserDoesNotExistError

    def delete_user(self, user_uuid):
        if not user_in_graph(self.graph, user_uuid):
            raise UserDoesNotExistError
        for user in self.graph:
            if user.uuid == user_uuid:
                del self.graph[user]
                break
        for user in self.graph:
            for user_child in self.graph[user]:
                if user_child.uuid == user_uuid:
                    self.graph[user].remove(user_child)

    def follow(self, follower, followee):
        exist_follower = user_in_graph(self.graph, follower)
        exist_followee = user_in_graph(self.graph, followee)
        if not exist_follower or not exist_followee:
            raise UserDoesNotExistError
        if follower == followee:
            raise ToNotFollowMyselfError
        user_follower = get_user_help(self.graph, follower)
        for user in self.graph:
            if user.uuid == followee:
                if user not in self.graph[user_follower]:
                    self.graph[user_follower].append(user)
                    break

    def unfollow(self, follower, followee):
        exist_follower = user_in_graph(self.graph, follower)
        exist_followee = user_in_graph(self.graph, followee)
        if not exist_follower or not exist_followee:
            raise UserDoesNotExistError
        user_follower = get_user_help(self.graph, follower)
        for user in self.graph:
            if user.uuid == followee:
                if user in self.graph[user_follower]:
                    self.graph[user_follower].remove(user)
                    break

    def is_following(self, follower, followee):
        exist_follower = user_in_graph(self.graph, follower)
        exist_followee = user_in_graph(self.graph, followee)
        if not exist_follower or not exist_followee:
            raise UserDoesNotExistError
        for user in self.graph:
            if user.uuid == follower:
                for user_followee in self.graph[user]:
                    if user_followee.uuid == followee:
                        return True
        return False

    def followers(self, user_uuid):
        if not user_in_graph(self.graph, user_uuid):
            raise UserDoesNotExistError
        followee = get_user_help(self.graph, user_uuid)
        followers = []
        for user in self.graph:
            if followee in self.graph[user]:
                followers.append(user)
        return set([follower.uuid for follower in followers])

    def following(self, user_uuid):
        if not user_in_graph(self.graph, user_uuid):
            raise UserDoesNotExistError
        user_follower = get_user_help(self.graph, user_uuid)
        return set([user.uuid for user in self.graph[user_follower]])

    def friends(self, user_uuid):
        if not user_in_graph(self.graph, user_uuid):
            raise UserDoesNotExistError
        return self.followers(user_uuid) & self.following(user_uuid)

    def max_distance(self, user_uuid):
        if not user_in_graph(self.graph, user_uuid):
            raise UserDoesNotExistError
        start_user = get_user_help(self.graph, user_uuid)
        if len(self.graph[start_user]) == 0:
            return math.inf
        max_distance = 0
        for user in self.graph:
            try:
                current_distance = self.min_distance(user_uuid, user.uuid)
                if current_distance > max_distance:
                    max_distance = current_distance
            except UsersNotConnectedError:
                continue
        return max_distance

    def min_distance(self, from_user_uuid, to_user_uuid):
        exist_from = user_in_graph(self.graph, from_user_uuid)
        exist_to = user_in_graph(self.graph, to_user_uuid)
        if not exist_from or not exist_to:
            raise UserDoesNotExistError
        from_user = get_user_help(self.graph, from_user_uuid)
        to_user = get_user_help(self.graph, to_user_uuid)
        distance = 0
        queue = [from_user, True]
        used_nodes = [from_user]
        while len(queue) != 0:
            current_node = queue.pop(0)
            used_nodes.append(current_node)
            if current_node is True and len(queue) != 0:
                distance = distance + 1
                queue.append(True)
            elif current_node == to_user:
                return distance
            elif current_node in self.graph:
                for followee in self.graph[current_node]:
                    if followee not in used_nodes:
                        queue.append(followee)
        raise UsersNotConnectedError

    def nth_layer_followings(self, user_uuid, n):
        if not user_in_graph(self.graph, user_uuid):
            raise UserDoesNotExistError
        start_user = get_user_help(self.graph, user_uuid)
        if n <= 0:
            return set()
        if n == 1:
            return set([node.uuid for node in self.graph[start_user]])
        queue = [start_user, True]
        used_nodes = defaultdict(list)
        count = 0
        while len(queue) != 0:
            node = queue.pop(0)
            if node is True:
                count = count + 1
                if count == n:
                    return set([node.uuid for node in queue])
                queue.append(True)
            else:
                for followee in self.graph[node]:
                    if followee not in used_nodes[node]:
                        used_nodes[followee] += used_nodes[node]
                        used_nodes[followee].append(node)
                        queue.append(followee)

    def generate_feed(self, user_uuid, offset=0, limit=10):
        if not user_in_graph(self.graph, user_uuid):
            raise UserDoesNotExistError
        follower = get_user_help(self.graph, user_uuid)
        followeds = self.graph[follower]
        followed_posts = []
        for followed in followeds:
            followed_posts += followed.posts
        sort_posts(followed_posts)
        del followed_posts[:offset]
        return followed_posts[:limit]


class UserDoesNotExistError(Exception):
    pass


class UserAlreadyExistsError(Exception):
    pass


class UsersNotConnectedError(Exception):
    pass


class ToNotFollowMyselfError(Exception):
    pass

Андрей обнови решението на 17.04.2016 20:09 (преди над 1 година)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
import uuid
import datetime
import math
from collections import defaultdict


class User:
    def __init__(self, full_name):
        self.full_name = full_name
        self.uuid = uuid.uuid4()
        self.posts = []

    def add_post(self, post_content):
        if len(self.posts) == 50:
            self.posts.pop(0)
        time = datetime.datetime.now()
        self.posts.append(Post(self.uuid, time, post_content))

    def get_post(self):
        for post in self.posts:
            yield post


class Post:
    def __init__(self, author, published_at, content):
        self.author = author
        self.published_at = published_at
        self.content = content


def sort_posts(posts):
    length = len(posts)
    for i in range(0, length-1):
        max = posts[i]
        max_position = i
        for j in range(i+1, length):
            if posts[j].published_at > max.published_at:
                max = posts[j]
                max_position = j
        posts[i], posts[max_position] = posts[max_position], posts[i]


def user_in_graph(graph, user_id):
    for user in graph:
        if user.uuid == user_id:
            return True
    return False


def get_user_help(graph, user_id):
    for user in graph:
        if user.uuid == user_id:
            return user


class SocialGraph:
    def __init__(self):
        self.graph = {}

    def add_user(self, user):
        for u in self.graph:
            if u.uuid == user.uuid:
                raise UserAlreadyExistsError
        self.graph[user] = []

    def get_user(self, user_uuid):
        for user in self.graph:
            if user.uuid == user_uuid:
                return user
        raise UserDoesNotExistError

    def delete_user(self, user_uuid):
        if not user_in_graph(self.graph, user_uuid):
            raise UserDoesNotExistError
        for user in self.graph:
            if user.uuid == user_uuid:
                del self.graph[user]
                break
        for user in self.graph:
            for user_child in self.graph[user]:
                if user_child.uuid == user_uuid:
                    self.graph[user].remove(user_child)

    def follow(self, follower, followee):
        exist_follower = user_in_graph(self.graph, follower)
        exist_followee = user_in_graph(self.graph, followee)
        if not exist_follower or not exist_followee:
            raise UserDoesNotExistError
        if follower == followee:
            raise ToNotFollowMyselfError
        user_follower = get_user_help(self.graph, follower)
        for user in self.graph:
            if user.uuid == followee:
                if user not in self.graph[user_follower]:
                    self.graph[user_follower].append(user)
                    break

    def unfollow(self, follower, followee):
        exist_follower = user_in_graph(self.graph, follower)
        exist_followee = user_in_graph(self.graph, followee)
        if not exist_follower or not exist_followee:
            raise UserDoesNotExistError
        user_follower = get_user_help(self.graph, follower)
        for user in self.graph:
            if user.uuid == followee:
                if user in self.graph[user_follower]:
                    self.graph[user_follower].remove(user)
                    break

    def is_following(self, follower, followee):
        exist_follower = user_in_graph(self.graph, follower)
        exist_followee = user_in_graph(self.graph, followee)
        if not exist_follower or not exist_followee:
            raise UserDoesNotExistError
        for user in self.graph:
            if user.uuid == follower:
                for user_followee in self.graph[user]:
                    if user_followee.uuid == followee:
                        return True
        return False

    def followers(self, user_uuid):
        if not user_in_graph(self.graph, user_uuid):
            raise UserDoesNotExistError
        followee = get_user_help(self.graph, user_uuid)
        followers = []
        for user in self.graph:
            if followee in self.graph[user]:
                followers.append(user)
        return set([follower.uuid for follower in followers])

    def following(self, user_uuid):
        if not user_in_graph(self.graph, user_uuid):
            raise UserDoesNotExistError
        user_follower = get_user_help(self.graph, user_uuid)
        return set([user.uuid for user in self.graph[user_follower]])

    def friends(self, user_uuid):
        if not user_in_graph(self.graph, user_uuid):
            raise UserDoesNotExistError
        return self.followers(user_uuid) & self.following(user_uuid)

    def max_distance(self, user_uuid):
        if not user_in_graph(self.graph, user_uuid):
            raise UserDoesNotExistError
        start_user = get_user_help(self.graph, user_uuid)
        if len(self.graph[start_user]) == 0:
            return math.inf
        max_distance = 0
        for user in self.graph:
            try:
                current_distance = self.min_distance(user_uuid, user.uuid)
                if current_distance > max_distance:
                    max_distance = current_distance
            except UsersNotConnectedError:
                continue
        return max_distance

    def min_distance(self, from_user_uuid, to_user_uuid):
        exist_from = user_in_graph(self.graph, from_user_uuid)
        exist_to = user_in_graph(self.graph, to_user_uuid)
        if not exist_from or not exist_to:
            raise UserDoesNotExistError
        from_user = get_user_help(self.graph, from_user_uuid)
        to_user = get_user_help(self.graph, to_user_uuid)
        distance = 0
        queue = [from_user, True]
        used_nodes = [from_user]
        while len(queue) != 0:
            current_node = queue.pop(0)
            used_nodes.append(current_node)
            if current_node is True and len(queue) != 0:
                distance = distance + 1
                queue.append(True)
            elif current_node == to_user:
                return distance
            elif current_node in self.graph:
                for followee in self.graph[current_node]:
                    if followee not in used_nodes:
                        queue.append(followee)
        raise UsersNotConnectedError

    def nth_layer_followings(self, user_uuid, n):
        if not user_in_graph(self.graph, user_uuid):
            raise UserDoesNotExistError
        if n <= 0:
            return set()
        nth_layer_list = []
        for user in self.graph:
            try:
                if self.min_distance(user_uuid, user.uuid) == n:
                    nth_layer_list.append(user.uuid)
            except UsersNotConnectedError:
                continue
        return set(nth_layer_list)

    def generate_feed(self, user_uuid, offset=0, limit=10):
        if not user_in_graph(self.graph, user_uuid):
            raise UserDoesNotExistError
        follower = get_user_help(self.graph, user_uuid)
        followeds = self.graph[follower]
        followed_posts = []
        for followed in followeds:
            followed_posts += followed.posts
        sort_posts(followed_posts)
        del followed_posts[:offset]
        return followed_posts[:limit]


class UserDoesNotExistError(Exception):
    pass


class UserAlreadyExistsError(Exception):
    pass


class UsersNotConnectedError(Exception):
    pass


class ToNotFollowMyselfError(Exception):
    pass
  • user_in_graph и get_user_help(което е малко гадно име) работят върху граф, което логично ги прави доста подходящи за методи на SocialGraph.
  • Ключовете в graph атрибута са User инстанции, но User класа ти няма __hash__, вместо това правиш проверки за съвпадане на uuid-тата. Това е доста кофти дизайн, труден за проследяване. Освен това те кара на много места да правиш линейни проверки по ключовете на dict, което не би трябвало дати се налага често. Заради многото обхождания стигаш и до доста нива на влагане, които правят кода ти много сложен за четене.
  • Методът, който прави проверка за съществуване на потребител може директно да хвърля изключението, вместо да имаш още един if извън него.

Андрей обнови решението на 18.04.2016 13:36 (преди над 1 година)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
import uuid
import datetime
import math
from collections import defaultdict


class User:
    def __init__(self, full_name):
        self.full_name = full_name
        self.uuid = uuid.uuid4()
        self.posts = []

    def add_post(self, post_content):
        if len(self.posts) == 50:
            self.posts.pop(0)
        time = datetime.datetime.now()
        self.posts.append(Post(self.uuid, time, post_content))

    def get_post(self):
        for post in self.posts:
            yield post


class Post:
    def __init__(self, author, published_at, content):
        self.author = author
        self.published_at = published_at
        self.content = content


def sort_posts(posts):
    length = len(posts)
    for i in range(0, length-1):
        max = posts[i]
        max_position = i
        for j in range(i+1, length):
            if posts[j].published_at > max.published_at:
                max = posts[j]
                max_position = j
        posts[i], posts[max_position] = posts[max_position], posts[i]


class SocialGraph:
    def __init__(self):
        self.graph = {}

    def add_user(self, user):
        for u in self.graph:
            if u.uuid == user.uuid:
                raise UserAlreadyExistsError
        self.graph[user] = []

    def get_user(self, user_uuid):
        for user in self.graph:
            if user.uuid == user_uuid:
                return user
        raise UserDoesNotExistError

    def user_in_graph(self, user_id):
        for user in self.graph:
            if user.uuid == user_id:
                return True
        return False

    def delete_user(self, user_uuid):
        if not self.user_in_graph(user_uuid):
            raise UserDoesNotExistError
        for user in self.graph:
            if user.uuid == user_uuid:
                del self.graph[user]
                break
        for user in self.graph:
            for user_child in self.graph[user]:
                if user_child.uuid == user_uuid:
                    self.graph[user].remove(user_child)

    def follow(self, follower, followee):
        user_follower = self.get_user(follower)
        user_followee = self.get_user(followee)
        if follower == followee:
            raise ToNotFollowMyselfError
        for user in self.graph:
            if user.uuid == followee:
                if user not in self.graph[user_follower]:
                    self.graph[user_follower].append(user)
                    break

    def unfollow(self, follower, followee):
        user_follower = self.get_user(follower)
        user_followee = self.get_user(followee)
        for user in self.graph:
            if user.uuid == followee:
                if user in self.graph[user_follower]:
                    self.graph[user_follower].remove(user)
                    break

    def is_following(self, follower, followee):
        user_follower = self.get_user(follower)
        user_followee = self.get_user(followee)
        if user_followee in self.graph[user_follower]:
            return True
        return False

    def followers(self, user_uuid):
        followee = self.get_user(user_uuid)
        followers = []
        for user in self.graph:
            if followee in self.graph[user]:
                followers.append(user)
        return set([follower.uuid for follower in followers])

    def following(self, user_uuid):
        user_follower = self.get_user(user_uuid)
        return set([user.uuid for user in self.graph[user_follower]])

    def friends(self, user_uuid):
        return self.followers(user_uuid) & self.following(user_uuid)

    def max_distance(self, user_uuid):
        start_user = self.get_user(user_uuid)
        if len(self.graph[start_user]) == 0:
            return math.inf
        max_distance = 0
        for user in self.graph:
            try:
                current_distance = self.min_distance(user_uuid, user.uuid)
                if current_distance > max_distance:
                    max_distance = current_distance
            except UsersNotConnectedError:
                continue
        return max_distance

    def min_distance(self, from_user_uuid, to_user_uuid):
        from_user = self.get_user(from_user_uuid)
        to_user = self.get_user(to_user_uuid)
        distance = 0
        queue = [from_user, True]
        used_nodes = [from_user]
        while len(queue) != 0:
            current_node = queue.pop(0)
            used_nodes.append(current_node)
            if current_node is True and len(queue) != 0:
                distance = distance + 1
                queue.append(True)
            elif current_node == to_user:
                return distance
            elif current_node in self.graph:
                for followee in self.graph[current_node]:
                    if followee not in used_nodes:
                        queue.append(followee)
        raise UsersNotConnectedError

    def nth_layer_followings(self, user_uuid, n):
        if not self.user_in_graph(user_uuid):
            raise UserDoesNotExistError
        if n <= 0:
            return set()
        nth_layer_list = []
        for user in self.graph:
            try:
                if self.min_distance(user_uuid, user.uuid) == n:
                    nth_layer_list.append(user.uuid)
            except UsersNotConnectedError:
                continue
        return set(nth_layer_list)

    def generate_feed(self, user_uuid, offset=0, limit=10):
        follower = self.get_user(user_uuid)
        followeds = self.graph[follower]
        followed_posts = []
        for followed in followeds:
            followed_posts += followed.posts
        sort_posts(followed_posts)
        del followed_posts[:offset]
        return followed_posts[:limit]


class UserDoesNotExistError(Exception):
    pass


class UserAlreadyExistsError(Exception):
    pass


class UsersNotConnectedError(Exception):
    pass


class ToNotFollowMyselfError(Exception):
    pass