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
|
# ==============================================================================
# Copyright (C) 2019 - Philip Paquette, Steven Bocco
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <https://www.gnu.org/licenses/>.
# ==============================================================================
""" Abstract Jsonable class to create data intended to be exchanged on network.
Used for requests, responses and notifications.
To write a sub-class, you must first write a base class for data category (e.g. notifications):
- Define header model for network data.
- Define ID field for data category (e.g. "notification_id"). This will be used to create unique
identifier for every data exchanged on network.
- Then every sub-class from base class must define parameters (params) model. Params and header
must not share any field.
"""
import uuid
from diplomacy.utils import strings, exceptions
from diplomacy.utils.common import assert_no_common_keys, camel_case_to_snake_case
from diplomacy.utils.jsonable import Jsonable
class NetworkData(Jsonable):
""" Abstract class for network-exchanged data. """
__slots__ = ['name']
# NB: header must have a `name` field and a field named `id_field`.
header = {}
params = {}
id_field = None
def __init__(self, **kwargs):
self.name = None # type: str
# Setting default values
kwargs[strings.NAME] = kwargs.get(strings.NAME, None) or self.get_class_name()
kwargs[self.id_field] = kwargs.get(self.id_field, None) or str(uuid.uuid4())
if kwargs[strings.NAME] != self.get_class_name():
raise exceptions.DiplomacyException('Expected request name %s, got %s' %
(self.get_class_name(), kwargs[strings.NAME]))
# Building
super(NetworkData, self).__init__(**kwargs)
@classmethod
def get_class_name(cls):
""" Returns the class name in snake_case. """
return camel_case_to_snake_case(cls.__name__)
@classmethod
def validate_params(cls):
""" Called when getting model to validate parameters. Called once per class. """
@classmethod
def build_model(cls):
""" Return model associated to current class. You can either define model class field
or override this function.
"""
# Validating model parameters (header and params must have different keys)
assert_no_common_keys(cls.header, cls.params)
cls.validate_params()
# Building model.
model = cls.header.copy()
model.update(cls.params.copy())
model[strings.NAME] = (cls.get_class_name(),)
return model
|