diff options
| author | Simeon Simeonov | 2023-01-18 21:12:31 +0100 |
|---|---|---|
| committer | Simeon Simeonov | 2023-01-18 21:12:31 +0100 |
| commit | 6dcd1727eb9d5dbfcb8f6e9599ada10d33063bac (patch) | |
| tree | 57d6aa95763fd57835316cca7ce1ae33f3f19ec2 | |
| parent | a33898d9db2f0e0e9b3da6f1b329b4045b65af11 (diff) | |
Remove some old presentations
| -rw-r--r-- | notebooks/python/python_intro.ipynb | 2 | ||||
| -rw-r--r-- | notebooks/python/python_oo.ipynb | 107 | ||||
| -rwxr-xr-x | reveal.js/dataaccess.html | 360 | ||||
| -rw-r--r-- | reveal.js/dataporten.html | 118 | ||||
| -rw-r--r-- | reveal.js/demo.html | 4 | ||||
| -rw-r--r-- | reveal.js/evalg.html | 274 | ||||
| -rwxr-xr-x | reveal.js/flow.html | 194 | ||||
| -rw-r--r-- | reveal.js/gateway.html | 152 | ||||
| -rw-r--r-- | reveal.js/mqprod.html | 438 | ||||
| -rw-r--r-- | reveal.js/postgresql_tuning.html | 233 | ||||
| -rw-r--r-- | reveal.js/python-oo.html | 145 | ||||
| -rw-r--r-- | reveal.js/python.html | 680 | ||||
| -rw-r--r-- | reveal.js/rabbitmq.html | 528 | ||||
| -rw-r--r--[-rwxr-xr-x] | reveal.js/sqlalchemy.html | 285 | ||||
| -rw-r--r-- | reveal.js/timetravel.html | 214 |
15 files changed, 1067 insertions, 2667 deletions
diff --git a/notebooks/python/python_intro.ipynb b/notebooks/python/python_intro.ipynb index 3c49a14..0168839 100644 --- a/notebooks/python/python_intro.ipynb +++ b/notebooks/python/python_intro.ipynb | |||
| @@ -1253,7 +1253,7 @@ | |||
| 1253 | "name": "python", | 1253 | "name": "python", |
| 1254 | "nbconvert_exporter": "python", | 1254 | "nbconvert_exporter": "python", |
| 1255 | "pygments_lexer": "ipython3", | 1255 | "pygments_lexer": "ipython3", |
| 1256 | "version": "3.10.8" | 1256 | "version": "3.11.1" |
| 1257 | } | 1257 | } |
| 1258 | }, | 1258 | }, |
| 1259 | "nbformat": 4, | 1259 | "nbformat": 4, |
diff --git a/notebooks/python/python_oo.ipynb b/notebooks/python/python_oo.ipynb index b56aa26..1c655d0 100644 --- a/notebooks/python/python_oo.ipynb +++ b/notebooks/python/python_oo.ipynb | |||
| @@ -179,7 +179,8 @@ | |||
| 179 | "\"The Zen of Python\" (import this) is still relevant and should be followed :)\n", | 179 | "\"The Zen of Python\" (import this) is still relevant and should be followed :)\n", |
| 180 | "\n", | 180 | "\n", |
| 181 | "\"Python will remain a dynamically typed language, and the authors have no desire to ever make type hints mandatory, even by convention.\" - PEP484\n", | 181 | "\"Python will remain a dynamically typed language, and the authors have no desire to ever make type hints mandatory, even by convention.\" - PEP484\n", |
| 182 | "\n" | 182 | "\n", |
| 183 | "Class (type) names should normally use the *CapWords* convention - CapitalizedWords (or *CapWords*, or *CamelCase*). This is also sometimes known as *StudlyCaps*. ASCII characters and English names only!" | ||
| 183 | ] | 184 | ] |
| 184 | }, | 185 | }, |
| 185 | { | 186 | { |
| @@ -225,17 +226,17 @@ | |||
| 225 | "name": "stdout", | 226 | "name": "stdout", |
| 226 | "output_type": "stream", | 227 | "output_type": "stream", |
| 227 | "text": [ | 228 | "text": [ |
| 228 | "car1.get_obj_info_str() = \"I am <__main__.Car object at 0x7f5630542810> with id 140008154736656 from <class '__main__.Car'> with id 94904583728688\"\n", | 229 | "car1.get_obj_info_str() = \"I am <__main__.Car object at 0x7fc7add96f10> with id 140495591927568 from <class '__main__.Car'> with id 94628241379728\"\n", |
| 229 | "car2.get_obj_info_str() = \"I am <__main__.Car object at 0x7f5633ec8190> with id 140008215052688 from <class '__main__.Car'> with id 94904583728688\"\n", | 230 | "car2.get_obj_info_str() = \"I am <__main__.Car object at 0x7fc7aef459d0> with id 140495610468816 from <class '__main__.Car'> with id 94628241379728\"\n", |
| 230 | "car1.model = 'BMW', car1.reg_nr = 'EC76183', car1.extras = ['GPSnav', 'Sound system'], id(car1.cls_extras) = 140008154734272, id(car1.__init__) = 140008154735680\n", | 231 | "car1.model = 'BMW', car1.reg_nr = 'EC76183', car1.extras = ['GPSnav', 'Sound system'], id(car1.cls_extras) = 140495591909184, id(car1.get_obj_info_str) = 140495591927808\n", |
| 231 | "car2.model = 'Scoda', car2.reg_nr = 'BD77655', car2.extras = ['GPSnav'], id(car2.cls_extras) = 140008154734272, id(car2.__init__) = 140008154736768\n", | 232 | "car2.model = 'Scoda', car2.reg_nr = 'BD77655', car2.extras = ['GPSnav'], id(car2.cls_extras) = 140495591909184, id(car2.get_obj_info_str) = 140495591927936\n", |
| 232 | "id(Car.cls_extras) = 140008154734272, id(Car.__init__) = 140008154484864\n", | 233 | "id(Car.cls_extras) = 140495591909184, id(Car.get_obj_info_str) = 140495591682944\n", |
| 233 | "car1.cls_extras = ['GPSnav', 'Sound system'], id(car1.cls_extras) = 140008154734272\n", | 234 | "car1.cls_extras = ['GPSnav', 'Sound system'], id(car1.cls_extras) = 140495591909184\n", |
| 234 | "car2.cls_extras = ['GPSnav', 'Sound system'], id(car2.cls_extras) = 140008154734272\n", | 235 | "car2.cls_extras = ['GPSnav', 'Sound system'], id(car2.cls_extras) = 140495591909184\n", |
| 235 | "True\n", | 236 | "True\n", |
| 236 | "hasattr(car1, 'import_tax_paid') = True\n", | 237 | "hasattr(car1, 'import_tax_paid') = True\n", |
| 237 | "hasattr(car2, 'import_tax_paid') = False\n", | 238 | "hasattr(car2, 'import_tax_paid') = False\n", |
| 238 | "id(car1.__class__) = 94904583728688, id(car2.__class__) = 94904583728688, id(Car) = 94904583728688\n" | 239 | "id(car1.__class__) = 94628241379728, id(car2.__class__) = 94628241379728, id(Car) = 94628241379728\n" |
| 239 | ] | 240 | ] |
| 240 | } | 241 | } |
| 241 | ], | 242 | ], |
| @@ -244,6 +245,7 @@ | |||
| 244 | "\n", | 245 | "\n", |
| 245 | " cls_extras = [] # class attribute - shared by all instances\n", | 246 | " cls_extras = [] # class attribute - shared by all instances\n", |
| 246 | " # cls_extras: list = [] is also possible\n", | 247 | " # cls_extras: list = [] is also possible\n", |
| 248 | " car_count = 0\n", | ||
| 247 | "\n", | 249 | "\n", |
| 248 | " def __init__(self, model: str, reg_nr: str, extras: list):\n", | 250 | " def __init__(self, model: str, reg_nr: str, extras: list):\n", |
| 249 | " \"\"\"\n", | 251 | " \"\"\"\n", |
| @@ -262,24 +264,40 @@ | |||
| 262 | "\n", | 264 | "\n", |
| 263 | " # self.cls_extras = extras # will create a *NEW instance attribute* called 'cls_extras'\n", | 265 | " # self.cls_extras = extras # will create a *NEW instance attribute* called 'cls_extras'\n", |
| 264 | " \n", | 266 | " \n", |
| 267 | " Car.car_count += 1 # increase Car.car_count by 1 every time a car is created \n", | ||
| 268 | " \n", | ||
| 269 | " def __del__(self):\n", | ||
| 270 | " \"\"\"\n", | ||
| 271 | " Called when the instance is about to be destroyed.\n", | ||
| 272 | "\n", | ||
| 273 | " This is also called a finalizer or (improperly) a destructor.\n", | ||
| 274 | " \"\"\"\n", | ||
| 275 | " Car.car_count -= 1 # decrease by 1 every time a Car object is destroyed\n", | ||
| 276 | "\n", | ||
| 265 | " def get_obj_info_str(self): # regular method that becomes instance attribute\n", | 277 | " def get_obj_info_str(self): # regular method that becomes instance attribute\n", |
| 266 | " return f\"I am {self} with id {id(self)} from {self.__class__} with id {id(self.__class__)}\"\n", | 278 | " return f\"I am {self} with id {id(self)} from {self.__class__} with id {id(self.__class__)}\"\n", |
| 267 | "\n", | 279 | "\n", |
| 268 | "car1 = Car(\"BMW\", \"EC76183\", [\"GPSnav\", \"Sound system\"])\n", | 280 | "car1 = Car(\"BMW\", \"EC76183\", [\"GPSnav\", \"Sound system\"])\n", |
| 269 | "car2 = Car(\"Scoda\", \"BD77655\", [\"GPSnav\"])\n", | 281 | "car2 = Car(\"Scoda\", \"BD77655\", [\"GPSnav\"])\n", |
| 270 | "\n", | 282 | "\n", |
| 271 | "print(f\"{car1.get_obj_info_str() = }\")\n", | 283 | "print(f\"{car1.get_obj_info_str() = }\") # same as calling Car.get_object_info_str(car1)\n", |
| 272 | "print(f\"{car2.get_obj_info_str() = }\")\n", | 284 | "print(f\"{car2.get_obj_info_str() = }\")\n", |
| 273 | "\n", | 285 | "\n", |
| 274 | "# default behavior for `object.attr`:\n", | 286 | "# default behavior for `object.attr`:\n", |
| 275 | "# getter:\n", | 287 | "# getter:\n", |
| 276 | "# - checks if 'attr' is an instance attribute\n", | 288 | "# - checks if 'attr' is an instance attribute\n", |
| 277 | "# - checks if 'attr' is a class attribute\n", | 289 | "# - checks if 'attr' is a class attribute (through the method resolution order - MRO)\n", |
| 278 | "# - raises AttributeError\n", | 290 | "# - raises AttributeError\n", |
| 279 | "# setter: (re)defines an instance attribute\n", | 291 | "# setter:\n", |
| 280 | "print(f\"{car1.model = }, {car1.reg_nr = }, {car1.extras = }, {id(car1.cls_extras) = }, {id(car1.__init__) = }\")\n", | 292 | "# - (re)defines an instance attribute\n", |
| 281 | "print(f\"{car2.model = }, {car2.reg_nr = }, {car2.extras = }, {id(car2.cls_extras) = }, {id(car2.__init__) = }\")\n", | 293 | "\n", |
| 282 | "print(f\"{id(Car.cls_extras) = }, {id(Car.__init__) = }\")\n", | 294 | "# Details not covered in this course:\n", |
| 295 | "# Car.x is translated to Car.__dict__[\"x\"] (...through the MRO)\n", | ||
| 296 | "# car1.x is translated to car1.__dict__[\"x\"] (if not found ... Car.x (see above), if not found car1.__getattr__(\"x\") is called)\n", | ||
| 297 | "\n", | ||
| 298 | "print(f\"{car1.model = }, {car1.reg_nr = }, {car1.extras = }, {id(car1.cls_extras) = }, {id(car1.get_obj_info_str) = }\")\n", | ||
| 299 | "print(f\"{car2.model = }, {car2.reg_nr = }, {car2.extras = }, {id(car2.cls_extras) = }, {id(car2.get_obj_info_str) = }\")\n", | ||
| 300 | "print(f\"{id(Car.cls_extras) = }, {id(Car.get_obj_info_str) = }\")\n", | ||
| 283 | "car1.cls_extras.extend(car1.extras)\n", | 301 | "car1.cls_extras.extend(car1.extras)\n", |
| 284 | "# car1.cls_extras = car1.extras # N.B. This will create a *NEW instance attribute* called 'cls_extras'\n", | 302 | "# car1.cls_extras = car1.extras # N.B. This will create a *NEW instance attribute* called 'cls_extras'\n", |
| 285 | "print(f\"{car1.cls_extras = }, {id(car1.cls_extras) = }\")\n", | 303 | "print(f\"{car1.cls_extras = }, {id(car1.cls_extras) = }\")\n", |
| @@ -289,7 +307,7 @@ | |||
| 289 | "car1.import_tax_paid = True # 'import_tax_paid' will only be present in car1\n", | 307 | "car1.import_tax_paid = True # 'import_tax_paid' will only be present in car1\n", |
| 290 | "print(car1.import_tax_paid)\n", | 308 | "print(car1.import_tax_paid)\n", |
| 291 | "print(f\"{hasattr(car1, 'import_tax_paid') = }\") # Out: True\n", | 309 | "print(f\"{hasattr(car1, 'import_tax_paid') = }\") # Out: True\n", |
| 292 | "print(f\"{hasattr(car2, 'import_tax_paid') = }\") # Out: False\n", | 310 | "print(f\"{hasattr(car2, 'import_tax_paid') = }\") # Out: False - no 'import_tax_paid' attribute in object car2\n", |
| 293 | "\n", | 311 | "\n", |
| 294 | "# .__class__ instance attribute pointing to its class (type) will be created\n", | 312 | "# .__class__ instance attribute pointing to its class (type) will be created\n", |
| 295 | "print(f\"{id(car1.__class__) = }, {id(car2.__class__) = }, {id(Car) = }\")" | 313 | "print(f\"{id(car1.__class__) = }, {id(car2.__class__) = }, {id(Car) = }\")" |
| @@ -705,6 +723,7 @@ | |||
| 705 | "name": "stdout", | 723 | "name": "stdout", |
| 706 | "output_type": "stream", | 724 | "output_type": "stream", |
| 707 | "text": [ | 725 | "text": [ |
| 726 | "2:2\n", | ||
| 708 | "__mul__ called\n", | 727 | "__mul__ called\n", |
| 709 | "vector * 3 = Vector(start=Point(x=2, y=2), end=Point(x=12, y=18))\n", | 728 | "vector * 3 = Vector(start=Point(x=2, y=2), end=Point(x=12, y=18))\n", |
| 710 | "__rmul__ called\n", | 729 | "__rmul__ called\n", |
| @@ -771,6 +790,7 @@ | |||
| 771 | " # __rmul__ = __mul__\n", | 790 | " # __rmul__ = __mul__\n", |
| 772 | "\n", | 791 | "\n", |
| 773 | "vector = Vector(Point(2, 2), Point(4, 6))\n", | 792 | "vector = Vector(Point(2, 2), Point(4, 6))\n", |
| 793 | "print(f\"{vector['start']}\")\n", | ||
| 774 | "print(f\"{vector * 3 = }\")\n", | 794 | "print(f\"{vector * 3 = }\")\n", |
| 775 | "print(f\"{3 * vector = }\")" | 795 | "print(f\"{3 * vector = }\")" |
| 776 | ] | 796 | ] |
| @@ -863,7 +883,7 @@ | |||
| 863 | "id": "d1d17f26-de6e-4510-b0f0-3ae02761ae24", | 883 | "id": "d1d17f26-de6e-4510-b0f0-3ae02761ae24", |
| 864 | "metadata": {}, | 884 | "metadata": {}, |
| 865 | "source": [ | 885 | "source": [ |
| 866 | "# Example 2\n", | 886 | "# Example 3\n", |
| 867 | "\n", | 887 | "\n", |
| 868 | "We will create a small and incomplete Animal class hierarchy" | 888 | "We will create a small and incomplete Animal class hierarchy" |
| 869 | ] | 889 | ] |
| @@ -973,7 +993,7 @@ | |||
| 973 | " super().__init__(\n", | 993 | " super().__init__(\n", |
| 974 | " weight=weight, tooth_replacement=True, alive=alive, **kwargs\n", | 994 | " weight=weight, tooth_replacement=True, alive=alive, **kwargs\n", |
| 975 | " )\n", | 995 | " )\n", |
| 976 | " self_conservation_status = conservation_status\n", | 996 | " self._conservation_status = conservation_status\n", |
| 977 | "\n", | 997 | "\n", |
| 978 | "\n", | 998 | "\n", |
| 979 | "class Cow(Mammal):\n", | 999 | "class Cow(Mammal):\n", |
| @@ -1104,6 +1124,7 @@ | |||
| 1104 | " \"\"\"\n", | 1124 | " \"\"\"\n", |
| 1105 | "\n", | 1125 | "\n", |
| 1106 | "\n", | 1126 | "\n", |
| 1127 | "# not good! The user of JSONMixin should not have to explicitly know about / use DictMixin\n", | ||
| 1107 | "class JSONConvertableTigerShark(TigerShark, DictMixin, JSONMixin):\n", | 1128 | "class JSONConvertableTigerShark(TigerShark, DictMixin, JSONMixin):\n", |
| 1108 | " \"\"\"A tiger shark class that provides JSON conversion functionality\"\"\"\n", | 1129 | " \"\"\"A tiger shark class that provides JSON conversion functionality\"\"\"\n", |
| 1109 | "\n", | 1130 | "\n", |
| @@ -1143,14 +1164,7 @@ | |||
| 1143 | "MRO is monotonic when the following is true: if C1 precedes C2 in the linearization of C, then C1 precedes C2 in the linearization of any subclass of C.\n", | 1164 | "MRO is monotonic when the following is true: if C1 precedes C2 in the linearization of C, then C1 precedes C2 in the linearization of any subclass of C.\n", |
| 1144 | "\n", | 1165 | "\n", |
| 1145 | "Not all classes admit a linearization. There are cases, in complicated hierarchies, where it is not possible to derive a class such that its linearization respects all the desired properties. \n", | 1166 | "Not all classes admit a linearization. There are cases, in complicated hierarchies, where it is not possible to derive a class such that its linearization respects all the desired properties. \n", |
| 1146 | "`TypeError: Cannot create a consistent method resolution order (MRO) for bases X, Y` is raised then.\n", | 1167 | "`TypeError: Cannot create a consistent method resolution order (MRO) for bases X, Y` is raised then." |
| 1147 | "\n", | ||
| 1148 | "\n", | ||
| 1149 | "## The Liskov substitution principle (LSP):\n", | ||
| 1150 | "\n", | ||
| 1151 | "Functions that use references to base class objects must be able to use objects of derived classes without knowing it.\n", | ||
| 1152 | "\n", | ||
| 1153 | "In other words: an object (such as a class) may be replaced by a sub-object (such as a class that extends the first class) without breaking the program.\n" | ||
| 1154 | ] | 1168 | ] |
| 1155 | }, | 1169 | }, |
| 1156 | { | 1170 | { |
| @@ -1233,6 +1247,14 @@ | |||
| 1233 | ] | 1247 | ] |
| 1234 | }, | 1248 | }, |
| 1235 | { | 1249 | { |
| 1250 | "cell_type": "markdown", | ||
| 1251 | "id": "875e7ffd-4feb-4ef0-b912-0a939c6c2dd1", | ||
| 1252 | "metadata": {}, | ||
| 1253 | "source": [ | ||
| 1254 | "... **N.B.** The most important thing to note here is that if `super()` is used in `Grandfather1` for this MRO (f.i. object of type `Simeon`) , none of its parents or siblings is called, but `Father` that was probably \"not even born\" when `Grandfather1` was created. :)" | ||
| 1255 | ] | ||
| 1256 | }, | ||
| 1257 | { | ||
| 1236 | "cell_type": "code", | 1258 | "cell_type": "code", |
| 1237 | "execution_count": 12, | 1259 | "execution_count": 12, |
| 1238 | "id": "95a1d0cc-0006-4ddc-b08d-d0b8b02acfba", | 1260 | "id": "95a1d0cc-0006-4ddc-b08d-d0b8b02acfba", |
| @@ -1274,7 +1296,6 @@ | |||
| 1274 | " \"\"\"Produces JSON content for production plans\"\"\"\n", | 1296 | " \"\"\"Produces JSON content for production plans\"\"\"\n", |
| 1275 | " \n", | 1297 | " \n", |
| 1276 | " def to_json(self, **kwargs):\n", | 1298 | " def to_json(self, **kwargs):\n", |
| 1277 | " # super().get_data(**kwargs)\n", | ||
| 1278 | " self.get_data(**kwargs)\n", | 1299 | " self.get_data(**kwargs)\n", |
| 1279 | " prod_plans = self.extract_production_plans(**kwargs)\n", | 1300 | " prod_plans = self.extract_production_plans(**kwargs)\n", |
| 1280 | " return json.dumps(self.__do_some_magic(prod_plans), indent=kwargs.get(\"indent\", 2))\n", | 1301 | " return json.dumps(self.__do_some_magic(prod_plans), indent=kwargs.get(\"indent\", 2))\n", |
| @@ -1295,7 +1316,10 @@ | |||
| 1295 | "\n", | 1316 | "\n", |
| 1296 | "\n", | 1317 | "\n", |
| 1297 | "cool_ts_proprocessor = CoolTSPreprocessor()\n", | 1318 | "cool_ts_proprocessor = CoolTSPreprocessor()\n", |
| 1298 | "print(cool_ts_proprocessor.to_json(indent=4))" | 1319 | "print(cool_ts_proprocessor.to_json(indent=4))\n", |
| 1320 | "\n", | ||
| 1321 | "# but we want to test CoolTSPreprocessor without the annoying ConnectionManager.get_data method being used\n", | ||
| 1322 | "# simply (temporary or canditionally) replacing ConnectionManager with MockConnectionManager will go against the O in SOLID" | ||
| 1299 | ] | 1323 | ] |
| 1300 | }, | 1324 | }, |
| 1301 | { | 1325 | { |
| @@ -1311,6 +1335,7 @@ | |||
| 1311 | "Using mock data\n", | 1335 | "Using mock data\n", |
| 1312 | "Extracting production plans from data\n", | 1336 | "Extracting production plans from data\n", |
| 1313 | "some magic is happening here...\n", | 1337 | "some magic is happening here...\n", |
| 1338 | "{}\n", | ||
| 1314 | "(<class '__main__.MockedButStillCoolTSPreprocessor'>, <class '__main__.CoolTSPreprocessor'>, <class '__main__.MockConnectionManager'>, <class '__main__.ConnectionManager'>, <class 'object'>)\n" | 1339 | "(<class '__main__.MockedButStillCoolTSPreprocessor'>, <class '__main__.CoolTSPreprocessor'>, <class '__main__.MockConnectionManager'>, <class '__main__.ConnectionManager'>, <class 'object'>)\n" |
| 1315 | ] | 1340 | ] |
| 1316 | } | 1341 | } |
| @@ -1320,12 +1345,36 @@ | |||
| 1320 | " pass\n", | 1345 | " pass\n", |
| 1321 | "\n", | 1346 | "\n", |
| 1322 | "mocked_but_still_cool_ts_preprocessor = MockedButStillCoolTSPreprocessor()\n", | 1347 | "mocked_but_still_cool_ts_preprocessor = MockedButStillCoolTSPreprocessor()\n", |
| 1323 | "mocked_but_still_cool_ts_preprocessor.to_json(indent=4)\n", | 1348 | "print(mocked_but_still_cool_ts_preprocessor.to_json(indent=4))\n", |
| 1324 | "print(MockedButStillCoolTSPreprocessor.__mro__)" | 1349 | "print(MockedButStillCoolTSPreprocessor.__mro__)" |
| 1325 | ] | 1350 | ] |
| 1326 | }, | 1351 | }, |
| 1327 | { | 1352 | { |
| 1328 | "cell_type": "markdown", | 1353 | "cell_type": "markdown", |
| 1354 | "id": "738b9084-27b9-4837-a927-e2087267d483", | ||
| 1355 | "metadata": {}, | ||
| 1356 | "source": [ | ||
| 1357 | "# Quick tips and final words\n", | ||
| 1358 | "\n", | ||
| 1359 | "- Avoid the classical pitfall: \"I am solving simple problems by simple means (the way I am used to) and could probably use OO when solving more complex problems\"! Solving simple problems is the best way to get used to the OO way of thinking / designing code.\n", | ||
| 1360 | "\n", | ||
| 1361 | "- Take some time to analyze and design your solution and discuss it with other team members and more experienced programmers. Do not rely only on QA (merge requests)!\n", | ||
| 1362 | "\n", | ||
| 1363 | "- Learn to think in terms of objects, like in real world objects (f.i. Car, Animal, TimeSeries, ProductionPlan, DataSource...)!\n", | ||
| 1364 | "\n", | ||
| 1365 | "- Learn to think in terms of abstract classes (types): Classes that will only serve as a foundation to their child classes, but will themselves never be directly instanciated!\n", | ||
| 1366 | "\n", | ||
| 1367 | "- Do not create \"thin\" classes / objects that simply containin some relevant attributes, protected by corresponding properties! The entire functionality (business logic) of the object should be encapsulated there.\n", | ||
| 1368 | "\n", | ||
| 1369 | "- Always make sure that you are not violating some of the SOLID principles (and others described above)! Run \"mental tests\" to test your current design!\n", | ||
| 1370 | "\n", | ||
| 1371 | "- Consider composition (\"has-a\" relationship) before considering inhiritance (\"is-a\" relationship)!\n", | ||
| 1372 | "\n", | ||
| 1373 | "- Read and learn from free software projects written in Python (f.i. on github.com)" | ||
| 1374 | ] | ||
| 1375 | }, | ||
| 1376 | { | ||
| 1377 | "cell_type": "markdown", | ||
| 1329 | "id": "540de317-ba77-4f5c-97ba-f1c0bec071fb", | 1378 | "id": "540de317-ba77-4f5c-97ba-f1c0bec071fb", |
| 1330 | "metadata": {}, | 1379 | "metadata": {}, |
| 1331 | "source": [ | 1380 | "source": [ |
diff --git a/reveal.js/dataaccess.html b/reveal.js/dataaccess.html deleted file mode 100755 index 4bafccd..0000000 --- a/reveal.js/dataaccess.html +++ /dev/null | |||
| @@ -1,360 +0,0 @@ | |||
| 1 | <!doctype html> | ||
| 2 | <html lang="en"> | ||
| 3 | <head> | ||
| 4 | <meta charset="utf-8"> | ||
| 5 | <title>SQLAlchemy & Odin Data Access</title> | ||
| 6 | <meta name="author" content="Simeon Simeonov"> | ||
| 7 | <meta name="apple-mobile-web-app-capable" content="yes"> | ||
| 8 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | ||
| 9 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| 10 | |||
| 11 | <link rel="stylesheet" href="dist/reset.css"> | ||
| 12 | <link rel="stylesheet" href="dist/reveal.css"> | ||
| 13 | |||
| 14 | <link rel="stylesheet" href="dist/theme/fifty.css" id="theme"> | ||
| 15 | |||
| 16 | <!-- Theme used for syntax highlighting of code --> | ||
| 17 | <link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme"> | ||
| 18 | <!-- <link rel="stylesheet" href="plugin/highlight/zenburn.css" id="highlight-theme"> --> | ||
| 19 | </head> | ||
| 20 | <body> | ||
| 21 | <div class="reveal"> | ||
| 22 | |||
| 23 | <!-- Any section element inside of this container is displayed as a slide --> | ||
| 24 | <div class="slides"> | ||
| 25 | |||
| 26 | <section> | ||
| 27 | <h2>SQLAlchemy & Odin Data Access</h2> | ||
| 28 | <h4>Fifty Data Science</h4> | ||
| 29 | </br> | ||
| 30 | <p><small>Simeon Simeonov</small></p> | ||
| 31 | </section> | ||
| 32 | |||
| 33 | <section> | ||
| 34 | |||
| 35 | <section id="fragments"> | ||
| 36 | <h2>Agenda</h2> | ||
| 37 | </br> | ||
| 38 | <ul> | ||
| 39 | <span class="fragment"><li>SQLAlchemy - Design & overview</li></span> | ||
| 40 | <span class="fragment"><li>SQLAlchemy - A small practical example</li></span> | ||
| 41 | <span class="fragment"><li>Odin Data Access - Overview and examples</li></span> | ||
| 42 | <span class="fragment"><li>Q & A</li></span> | ||
| 43 | </ul> | ||
| 44 | </section> | ||
| 45 | |||
| 46 | </section> | ||
| 47 | |||
| 48 | <section> | ||
| 49 | <h2>What is SQLAlchemy?</h2> | ||
| 50 | </br> | ||
| 51 | <p><em>SQLAlchemy</em> is a Python library created by Mike Bayer to provide a high-level Pythonic interface to RDBMS such as <em>PostgreSQL</em>, <em>SQLite</em>, <em>MySQL</em>, <em>Oracle</em>, <em>DB2</em>.</p> | ||
| 52 | <p><em>SQLAlchemy</em> includes RDBMS-independent SQL expression language and an <em>object-relational mapper (ORM)</em>.</p> | ||
| 53 | </section> | ||
| 54 | |||
| 55 | <section> | ||
| 56 | <h2>Why use SQLAlchemy?</h2> | ||
| 57 | </br> | ||
| 58 | <ul> | ||
| 59 | <li><p>portability - the programming interface is independent of the type of RDBMS and connector used</p></li> | ||
| 60 | <li><p>security - no more SQL injections</p></li> | ||
| 61 | <li><p>abstraction - no need to bother with complex JOINs</p></li> | ||
| 62 | <li><p>object-orientation - you work with objects and NOT tables and rows</p></li> | ||
| 63 | <li><p>performance - exploits the likehood of reusing a particular query</p></li> | ||
| 64 | <li><p>flexibility - you can override almost anything</p></li> | ||
| 65 | </ul> | ||
| 66 | </section> | ||
| 67 | |||
| 68 | <section> | ||
| 69 | <h2>Basic architecture</h2> | ||
| 70 | <p><em>SQLAlchemy</em> consists of several components, including the the <em>ORM</em>.</p> | ||
| 71 | <ul> | ||
| 72 | <li><em>Engine</em>- manages the connection pool and the RDBMS-independent SQL dialect layer</li> | ||
| 73 | <li><em>MetaData</em> - used to collect and organize information about your table layout (schema)</li> | ||
| 74 | <li><em>Session</em> - establishes all conversations with the RDBMS and represents a "holding zone" for all the objects which you've loaded or associated with it during its lifespan</li> | ||
| 75 | <li><em>SQL expression language</em> - provides an API to execute your queries and updates against your tables, all from Python, and all in a database-independent way (low-level interface)</li> | ||
| 76 | <li><em>ORM</em> - provides a convenient way to add database persistence to your Python objects withoutrequiring you to design your objects around the database, or the database around the objects (high-level interface)</li> | ||
| 77 | </ul> | ||
| 78 | <img src="images/sqlalchemy/sqla_arch.png"></img> | ||
| 79 | </section> | ||
| 80 | |||
| 81 | <section data-auto-animate> | ||
| 82 | <h2>Example</h2> | ||
| 83 | <p><em>SQLAlchemy</em> gives us the choice between <em>classical mapping</em> and the newer <em>declarative mapping</em></p> | ||
| 84 | <pre data-id="code-animation"> | ||
| 85 | <code class="python" data-trim data-line-numbers type="text/template"> | ||
| 86 | import sqlalchemy | ||
| 87 | sqlalchemy.__version__ | ||
| 88 | |||
| 89 | from sqlalchemy import create_engine | ||
| 90 | |||
| 91 | # engine = create_engine("postgresql+psycopg2://user:zipassword@localhost/mydb" , echo=True) | ||
| 92 | # The string form of the URL is dialect+driver://user:password@host/dbname[?key=value..], | ||
| 93 | # where dialect is a database name such as mysql, oracle, postgresql, etc., | ||
| 94 | # and driver the name of a DBAPI, such as psycopg2, pyodbc, cx_oracle | ||
| 95 | # The echo flag is a shortcut to setting up SQLAlchemy logging, | ||
| 96 | # which is accomplished via Python’s standard logging module. | ||
| 97 | # With it enabled, we’ll see all the generated SQL produced. | ||
| 98 | # engine = create_engine("sqlite:///library.db", echo=True) | ||
| 99 | engine = create_engine("sqlite:///:memory:", echo=True) | ||
| 100 | |||
| 101 | metadata = MetaData() | ||
| 102 | |||
| 103 | from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey | ||
| 104 | |||
| 105 | authors_table = Table( | ||
| 106 | "authors", | ||
| 107 | metadata, | ||
| 108 | Column("id", Integer, primary_key=True), | ||
| 109 | Column("name", String), | ||
| 110 | ) # Column("name", String(50)) is possible | ||
| 111 | |||
| 112 | books_table = Table( | ||
| 113 | "books", | ||
| 114 | metadata, | ||
| 115 | Column("id", Integer, primary_key=True), | ||
| 116 | Column("title", String), | ||
| 117 | Column("description", String), | ||
| 118 | Column("author_id", ForeignKey('authors.id')), | ||
| 119 | ) | ||
| 120 | |||
| 121 | metadata.create_all(engine) # creates the tables | ||
| 122 | </code> | ||
| 123 | </pre> | ||
| 124 | </section> | ||
| 125 | |||
| 126 | <section data-auto-animate> | ||
| 127 | <h2>Example (cont...)</h2> | ||
| 128 | <h4 data-id="code-title">Use of <em>SQL expression language</em></h4> | ||
| 129 | <pre data-id="code-animation"> | ||
| 130 | <code class="python" data-trim data-line-numbers type="text/template"> | ||
| 131 | insert_stmt = authors_table.insert(bind=engine) | ||
| 132 | type(insert_stmt) | ||
| 133 | # Out: <class 'sqlalchemy.sql.expression.Insert'> | ||
| 134 | print(insert_stmt) | ||
| 135 | # Out: INSERT INTO authors (id, name) VALUES (:id,:name) | ||
| 136 | |||
| 137 | compiled_stmt = insert_stmt.compile() | ||
| 138 | print(compiled_stmt.params) | ||
| 139 | # Out: {'id': None, 'name': None} | ||
| 140 | |||
| 141 | insert_stmt.execute(name="Alexandre Dumas") # insert a single entry | ||
| 142 | insert_stmt.execute([{"name": 'Mr X'},{'name': 'Mr Y'}]) # a list of entries | ||
| 143 | |||
| 144 | metadata.bind = engine # no need to explicitly bind the engine from now on | ||
| 145 | select_stmt = authors_table.select(authors_table.c.id==2) | ||
| 146 | result = select_stmt.execute() | ||
| 147 | result.fetchall() | ||
| 148 | # Out: [(1, u'Mr X')] | ||
| 149 | |||
| 150 | del_stmt = authors_table.delete() | ||
| 151 | del_stmt.execute(whereclause=text("name='Mr Y'")) | ||
| 152 | del_stmt.execute() # delete all | ||
| 153 | </code> | ||
| 154 | </pre> | ||
| 155 | </section> | ||
| 156 | |||
| 157 | <section data-auto-animate> | ||
| 158 | <h2>Example (cont...)</h2> | ||
| 159 | <h4 data-id="code-title">Use of <em>classical mapping</em></h4> | ||
| 160 | <pre data-id="code-animation"> | ||
| 161 | <code class="python" data-trim data-line-numbers type="text/template"> | ||
| 162 | from sqlalchemy.orm import mapper | ||
| 163 | from sqlalchemy.orm import relationship, backref | ||
| 164 | |||
| 165 | class Author: | ||
| 166 | def __init__(self, name): | ||
| 167 | self.name = name | ||
| 168 | |||
| 169 | def __str__(self): | ||
| 170 | return self.name | ||
| 171 | |||
| 172 | |||
| 173 | class Book: | ||
| 174 | def __init__(self, title, description, author): | ||
| 175 | self.title = title | ||
| 176 | self.description = description | ||
| 177 | self.author = author | ||
| 178 | |||
| 179 | def __str__(self): | ||
| 180 | return self.title | ||
| 181 | |||
| 182 | mapper(Book, books_table) | ||
| 183 | mapper(Author, authors_table, properties = {"books": relation(Book, backref="author")}) | ||
| 184 | </code> | ||
| 185 | </pre> | ||
| 186 | </section> | ||
| 187 | |||
| 188 | <section data-auto-animate> | ||
| 189 | <h2>Example (cont...)</h2> | ||
| 190 | <h4 data-id="code-title">Doing the same thing the easy way with <em>declarative mapping</em></h4> | ||
| 191 | <pre data-id="code-animation"> | ||
| 192 | <code class="python" data-trim data-line-numbers type="text/template"> | ||
| 193 | from sqlalchemy.ext.declarative import declarative_base | ||
| 194 | from sqlalchemy.orm import relationship, backref | ||
| 195 | |||
| 196 | Base = declarative_base() | ||
| 197 | |||
| 198 | class Author(Base): | ||
| 199 | __tablename__ = "authors" | ||
| 200 | |||
| 201 | id = Column(Integer, primary_key=True) | ||
| 202 | name = Column(String) | ||
| 203 | |||
| 204 | def __init__(self, name): | ||
| 205 | self.name = name | ||
| 206 | |||
| 207 | def __str__(self): | ||
| 208 | return self.name | ||
| 209 | |||
| 210 | |||
| 211 | class Book(Base): | ||
| 212 | __tablename__ = "books" # self.__table__ will be available for our objects | ||
| 213 | |||
| 214 | id = Column(Integer, primary_key=True) | ||
| 215 | title = Column(String) | ||
| 216 | description = Column(String) | ||
| 217 | author_id = Column(Integer, ForeignKey("authors.id")) | ||
| 218 | author = relationship(Author, backref=backref("books", order_by=title)) | ||
| 219 | |||
| 220 | def __init__(self, title, description, author): | ||
| 221 | self.title = title | ||
| 222 | self.description = description | ||
| 223 | self.author = author | ||
| 224 | |||
| 225 | def __str__(self): | ||
| 226 | return self.title | ||
| 227 | |||
| 228 | Base.metadata.create_all(engine)# create tables | ||
| 229 | </code> | ||
| 230 | </pre> | ||
| 231 | </section> | ||
| 232 | |||
| 233 | <section data-auto-animate> | ||
| 234 | <h2>Example (cont...)</h2> | ||
| 235 | <h4 data-id="code-title">Creating instances</h4> | ||
| 236 | <pre data-id="code-animation"> | ||
| 237 | <code class="python" data-trim data-line-numbers type="text/template"> | ||
| 238 | from sqlalchemy.orm import sessionmaker | ||
| 239 | |||
| 240 | Session = sessionmaker(bind=engine) # bound session | ||
| 241 | session = Session() | ||
| 242 | |||
| 243 | author_1 = Author("Richard Dawkins") | ||
| 244 | author_2 = Author("Matt Ridley") | ||
| 245 | |||
| 246 | book_1 = Book("The Red Queen", "A popular science book", author_2) | ||
| 247 | book_2 = Book("The Selfish Gene", "A popular science book", author_1) | ||
| 248 | book_3 = Book("The Blind Watchmaker", "The theory of evolutio", author_1) # typo | ||
| 249 | |||
| 250 | session.add(author_1) | ||
| 251 | session.add(author_2) | ||
| 252 | session.add(book_1) | ||
| 253 | session.add(book_2) | ||
| 254 | session.add(book_3) # or simply session.add_all([author_1, author_2, book_1, book_2, book_3]) | ||
| 255 | |||
| 256 | # session.flush() | ||
| 257 | session.commit() # flushes (issues the statements and sends them to the RDBMS) and commits | ||
| 258 | |||
| 259 | book_3.description = "The theory of evolution" # update the object | ||
| 260 | book_3 in session # check whether the object is in the session | ||
| 261 | # Out: True | ||
| 262 | |||
| 263 | session.commit() | ||
| 264 | </code> | ||
| 265 | </pre> | ||
| 266 | </section> | ||
| 267 | |||
| 268 | <section data-auto-animate> | ||
| 269 | <h2>Example (cont...)</h2> | ||
| 270 | <h4 data-id="code-title">Queries</h4> | ||
| 271 | <pre data-id="code-animation"> | ||
| 272 | <code class="python" data-trim data-line-numbers type="text/template"> | ||
| 273 | session.query(Book).order_by(Book.id) # returns a Query instance with a .statement attribute | ||
| 274 | session.query(Book).order_by(Book.id).all() # returns an object-list | ||
| 275 | |||
| 276 | # return all book objects where title == "The Selfish Gene" | ||
| 277 | session.query(Book).filter(Book.title == "The Selfish Gene").order_by(Book.id).all() | ||
| 278 | |||
| 279 | # using LIKE | ||
| 280 | session.query(Book).filter(Book.title.like("The%")).order_by(Book.id).all() | ||
| 281 | |||
| 282 | query = session.query(Book).filter(Book.id == 9).order_by(Book.id) | ||
| 283 | query.count() # returns 0L | ||
| 284 | query.all() # returns an empty list | ||
| 285 | query.first() # returns None | ||
| 286 | query.one() # raises NoResultFound exception | ||
| 287 | |||
| 288 | query = session.query(Book).filter(Book.id == 1).order_by(Book.id) | ||
| 289 | book_1 = query.one() | ||
| 290 | book_1.description # returns "A popular science book" | ||
| 291 | book_1.author.books # returns a list of Book-objects representing all the books from the same author. | ||
| 292 | |||
| 293 | # get a list of all Book-instances where the author"s name is "Richard Dawkins" | ||
| 294 | session.query(Book).filter(Book.author_id == Author.id).filter(Author.name == "Richard Dawkins").all() | ||
| 295 | session.query(Book).join(Author).filter(Author.name == "Richard Dawkins").all() | ||
| 296 | session.query(Book).\ | ||
| 297 | from_statement("SELECT b.* FROM books b, authors a WHERE b.author_id = a.id AND a.name=:name").\ | ||
| 298 | params(name="Richard Dawkins").all() | ||
| 299 | </code> | ||
| 300 | </pre> | ||
| 301 | </section> | ||
| 302 | |||
| 303 | <section data-background="http://i.giphy.com/90F8aUepslB84.gif"> | ||
| 304 | <h2>... simply amazing!</h2> | ||
| 305 | </section> | ||
| 306 | |||
| 307 | <section> | ||
| 308 | <h2>Odin Data Access</h2> | ||
| 309 | <p><a href="https://gitlab.fifty.eu/odin/data-science/odin-data-access">https://gitlab.fifty.eu/odin/data-science/odin-data-access</a></p> | ||
| 310 | <p>Provides the <em>odin_data_access</em> module with some of the following entities:</p> | ||
| 311 | <ul> | ||
| 312 | <li><em>session</em> module for creating Session objects in a Fifty environment via <em>get_session</em> and <em>get_session_from_env</em></li> | ||
| 313 | <li><em>DBBase</em> - a tiny base-class that can be used as a mixin for mapping</li> | ||
| 314 | <li><em>DBTableAccessor</em> - tiny wrapper for <em>Session</em> and <em>Table</em> as well a "facade" for <em>Query</em> and <em>pandas</em></li> | ||
| 315 | </ul> | ||
| 316 | <p>The <em>DBTableAccessor</em>:</p> | ||
| 317 | <ul> | ||
| 318 | <li>represents a single RDBMS-table that is introspected during instance initialization (<em>__init__</em>)</li> | ||
| 319 | <li>uses a mix of <em>ORM</em> and <em>SQL expression language</em> functionality</li> | ||
| 320 | <li>contins a mapper for mapping custom classes to its corresponding <em>Table</em></li> | ||
| 321 | <span class="fragment"><li>used by Batman and Chuck Norris (source: incomplete Google search)</li></span> | ||
| 322 | </ul> | ||
| 323 | </section> | ||
| 324 | |||
| 325 | <section> | ||
| 326 | <h1>Demo</h1> | ||
| 327 | <p>Now watch this well-rehearsed demo...</p> | ||
| 328 | </section> | ||
| 329 | |||
| 330 | <section> | ||
| 331 | <h1>Q & A</h1> | ||
| 332 | </section> | ||
| 333 | |||
| 334 | </div> | ||
| 335 | </div> | ||
| 336 | |||
| 337 | <script src="dist/reveal.js"></script> | ||
| 338 | <script src="plugin/zoom/zoom.js"></script> | ||
| 339 | <script src="plugin/notes/notes.js"></script> | ||
| 340 | <script src="plugin/search/search.js"></script> | ||
| 341 | <script src="plugin/markdown/markdown.js"></script> | ||
| 342 | <script src="plugin/highlight/highlight.js"></script> | ||
| 343 | <script> | ||
| 344 | |||
| 345 | // Also available as an ES module, see: | ||
| 346 | // https://revealjs.netlify.app/initialization/ | ||
| 347 | Reveal.initialize({ | ||
| 348 | controls: true, | ||
| 349 | progress: true, | ||
| 350 | center: true, | ||
| 351 | hash: true, | ||
| 352 | |||
| 353 | // Learn about plugins: https://revealjs.netlify.app/plugins/ | ||
| 354 | plugins: [ RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight ] | ||
| 355 | }); | ||
| 356 | |||
| 357 | </script> | ||
| 358 | |||
| 359 | </body> | ||
| 360 | </html> | ||
diff --git a/reveal.js/dataporten.html b/reveal.js/dataporten.html deleted file mode 100644 index 6c9f941..0000000 --- a/reveal.js/dataporten.html +++ /dev/null | |||
| @@ -1,118 +0,0 @@ | |||
| 1 | <!doctype html> | ||
| 2 | <html lang="en"> | ||
| 3 | <head> | ||
| 4 | <meta charset="utf-8"> | ||
| 5 | <title>Utviklerforum: Dataporten & eValg 3.0</title> | ||
| 6 | <meta name="author" content="Simeon Simeonov"> | ||
| 7 | <meta name="apple-mobile-web-app-capable" content="yes"> | ||
| 8 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | ||
| 9 | |||
| 10 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| 11 | |||
| 12 | <link rel="stylesheet" href="dist/reset.css"> | ||
| 13 | <link rel="stylesheet" href="dist/reveal.css"> | ||
| 14 | <link rel="stylesheet" href="dist/theme/serif.css" id="theme"> | ||
| 15 | |||
| 16 | <!-- Theme used for syntax highlighting of code --> | ||
| 17 | <link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme"> | ||
| 18 | </head> | ||
| 19 | <body> | ||
| 20 | <div class="reveal"> | ||
| 21 | |||
| 22 | <!-- Any section element inside of this container is displayed as a slide --> | ||
| 23 | <div class="slides"> | ||
| 24 | <section> | ||
| 25 | <h1>Dataporten & eValg 3.0</h1> | ||
| 26 | <h3>Utviklerforum (31.08.2017)</h3> | ||
| 27 | <br/> | ||
| 28 | <p> | ||
| 29 | <small>Simeon Simeonov</small> | ||
| 30 | </p> | ||
| 31 | </section> | ||
| 32 | |||
| 33 | <section> | ||
| 34 | <h2>Agenda</h2> | ||
| 35 | <br/> | ||
| 36 | <ul> | ||
| 37 | <li><p>Intro</p></li> | ||
| 38 | <li><p>The big picture</p></li> | ||
| 39 | <li><p>Flow</p></li> | ||
| 40 | <li><p>Q & A</p></li> | ||
| 41 | </ul> | ||
| 42 | </section> | ||
| 43 | |||
| 44 | <section> | ||
| 45 | <section> | ||
| 46 | <h2>Introduction</h2> | ||
| 47 | <p><em>Evalg 3.0</em></p> | ||
| 48 | <ul> | ||
| 49 | <li><p>Development started in 2017</p></li> | ||
| 50 | <li><p>Dataporten / Feide (OIDC with discovery) ... perhaps others in the future?</p></li> | ||
| 51 | <li><p>Python 3.6 / Flask / React / pyoic / pyJWT ...</p></li> | ||
| 52 | <li><p>Using fnr, feide-id, names...</p></li> | ||
| 53 | </ul> | ||
| 54 | </section> | ||
| 55 | <section> | ||
| 56 | <h3>Challenges with Dataporten</h3> | ||
| 57 | <ul> | ||
| 58 | <li><p>React</p></li> | ||
| 59 | <li><p>Flexibility (using other providers (OP))</p></li> | ||
| 60 | <li><p>Testing (users, firewall(s))</p></li> | ||
| 61 | </ul> | ||
| 62 | </section> | ||
| 63 | </section> | ||
| 64 | |||
| 65 | <section> | ||
| 66 | <h2>The big picture</h2> | ||
| 67 | <img src="images/dataporten/dataporten_the_big_picture1.png"></img> | ||
| 68 | </section> | ||
| 69 | |||
| 70 | <section> | ||
| 71 | <section> | ||
| 72 | <h2>Flow</h2> | ||
| 73 | <img src="images/dataporten/flow1.png"></img> | ||
| 74 | </section> | ||
| 75 | <section> | ||
| 76 | <h3>Challenges</h3> | ||
| 77 | <ul> | ||
| 78 | <li><p>Not distributing client secrets</p></li> | ||
| 79 | <li><p>Checking state and verifying signatures</p></li> | ||
| 80 | <li><p>Flexibility (using other providers (OP))</p></li> | ||
| 81 | <li><p>Testing (users, firewall(s))</p></li> | ||
| 82 | </ul> | ||
| 83 | </section> | ||
| 84 | </section> | ||
| 85 | |||
| 86 | <section> | ||
| 87 | <h2>Q & A</h2> | ||
| 88 | <h3>???</h3> | ||
| 89 | <p><em>“Judge a man by his questions rather than by his answers.”</em> - Voltaire</p> | ||
| 90 | </section> | ||
| 91 | |||
| 92 | </div> | ||
| 93 | </div> | ||
| 94 | |||
| 95 | <script src="dist/reveal.js"></script> | ||
| 96 | <script src="plugin/zoom/zoom.js"></script> | ||
| 97 | <script src="plugin/notes/notes.js"></script> | ||
| 98 | <script src="plugin/search/search.js"></script> | ||
| 99 | <script src="plugin/markdown/markdown.js"></script> | ||
| 100 | <script src="plugin/highlight/highlight.js"></script> | ||
| 101 | <script> | ||
| 102 | |||
| 103 | // Also available as an ES module, see: | ||
| 104 | // https://revealjs.netlify.app/initialization/ | ||
| 105 | Reveal.initialize({ | ||
| 106 | controls: true, | ||
| 107 | progress: true, | ||
| 108 | center: true, | ||
| 109 | hash: true, | ||
| 110 | |||
| 111 | // Learn about plugins: https://revealjs.netlify.app/plugins/ | ||
| 112 | plugins: [ RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight ] | ||
| 113 | }); | ||
| 114 | |||
| 115 | </script> | ||
| 116 | |||
| 117 | </body> | ||
| 118 | </html> | ||
diff --git a/reveal.js/demo.html b/reveal.js/demo.html index ab11f77..39b014d 100644 --- a/reveal.js/demo.html +++ b/reveal.js/demo.html | |||
| @@ -86,7 +86,7 @@ | |||
| 86 | 86 | ||
| 87 | <section data-auto-animate> | 87 | <section data-auto-animate> |
| 88 | <h2 data-id="code-title">Pretty Code</h2> | 88 | <h2 data-id="code-title">Pretty Code</h2> |
| 89 | <pre data-id="code-animation"><code class="hljs" data-trim data-line-numbers> | 89 | <pre data-id="code-animation"><code class="hljs javascript" data-trim data-line-numbers> |
| 90 | import React, { useState } from 'react'; | 90 | import React, { useState } from 'react'; |
| 91 | 91 | ||
| 92 | function Example() { | 92 | function Example() { |
| @@ -102,7 +102,7 @@ | |||
| 102 | 102 | ||
| 103 | <section data-auto-animate> | 103 | <section data-auto-animate> |
| 104 | <h2 data-id="code-title">With animations</h2> | 104 | <h2 data-id="code-title">With animations</h2> |
| 105 | <pre data-id="code-animation"><code class="hljs" data-trim data-line-numbers="|4,8-11|17|22-24"><script type="text/template"> | 105 | <pre data-id="code-animation"><code class="hljs javascript" data-trim data-line-numbers="|4,8-11|17|22-24"><script type="text/template"> |
| 106 | import React, { useState } from 'react'; | 106 | import React, { useState } from 'react'; |
| 107 | 107 | ||
| 108 | function Example() { | 108 | function Example() { |
diff --git a/reveal.js/evalg.html b/reveal.js/evalg.html index e163950..d68a345 100644 --- a/reveal.js/evalg.html +++ b/reveal.js/evalg.html | |||
| @@ -1,160 +1,162 @@ | |||
| 1 | <!doctype html> | 1 | <!doctype html> |
| 2 | <html lang="en"> | 2 | <html lang="en"> |
| 3 | <head> | 3 | <head> |
| 4 | <meta charset="utf-8"> | 4 | <meta charset="utf-8"> |
| 5 | <title>Utviklerforum: eValg 3</title> | 5 | <title>Utviklerforum: eValg 3</title> |
| 6 | <meta name="author" content="Simeon Simeonov"> | 6 | <meta name="author" content="Simeon Simeonov"> |
| 7 | <meta name="apple-mobile-web-app-capable" content="yes"> | 7 | <meta name="apple-mobile-web-app-capable" content="yes"> |
| 8 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | 8 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> |
| 9 | <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> | 9 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 10 | <link rel="stylesheet" href="css/reveal.css"> | ||
| 11 | <link rel="stylesheet" href="css/theme/serif.css" id="theme"> | ||
| 12 | 10 | ||
| 13 | <!-- Theme used for syntax highlighting of code --> | 11 | <link rel="stylesheet" href="dist/reset.css"> |
| 14 | <link rel="stylesheet" href="lib/css/zenburn.css"> | 12 | <link rel="stylesheet" href="dist/reveal.css"> |
| 15 | </head> | ||
| 16 | <body> | ||
| 17 | <div class="reveal"> | ||
| 18 | 13 | ||
| 19 | <!-- Any section element inside of this container is displayed as a slide --> | 14 | <link rel="stylesheet" href="dist/theme/serif.css" id="theme"> |
| 20 | <div class="slides"> | ||
| 21 | <section> | ||
| 22 | <h1>eValg 3</h1> | ||
| 23 | <h3>Utviklerforum (28.11.2019)</h3> | ||
| 24 | <br/> | ||
| 25 | <p>Simeon Simeonov (<em>sgs</em> @ USITINT)</p> | ||
| 26 | </section> | ||
| 27 | 15 | ||
| 28 | <section> | 16 | <!-- Theme used for syntax highlighting of code --> |
| 29 | <h2>Agenda</h2> | 17 | <link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme"> |
| 30 | <br/> | 18 | </head> |
| 31 | <ul> | 19 | <body> |
| 32 | <li><p>Introduction</p></li> | 20 | <div class="reveal"> |
| 33 | <li><p>Components</p></li> | ||
| 34 | <li><p>Development</p></li> | ||
| 35 | <li><p>Q & A</p></li> | ||
| 36 | </ul> | ||
| 37 | </section> | ||
| 38 | 21 | ||
| 39 | <section> | 22 | <!-- Any section element inside of this container is displayed as a slide --> |
| 40 | <h2>Introduction</h2> | 23 | <div class="slides"> |
| 41 | <p><em>eValg 3</em></p> | 24 | <section> |
| 42 | <ul> | 25 | <h1>eValg 3</h1> |
| 43 | <li><p>Development started in 2017 and then again in 2018</p></li> | 26 | <h3>Utviklerforum (28.11.2019)</h3> |
| 44 | <li><p>The last trial election (https://valg-pilot.uio.no) ended 15.11.2019</p></li> | 27 | <br/> |
| 45 | <li><p>Currently the eValg team consists of two developers (<em>skh</em>, <em>sgs</em>)</p></li> | 28 | <p>Simeon Simeonov (<em>sgs</em> @ USITINT)</p> |
| 46 | </ul> | 29 | </section> |
| 47 | </section> | ||
| 48 | 30 | ||
| 31 | <section> | ||
| 32 | <h2>Agenda</h2> | ||
| 33 | <br/> | ||
| 34 | <ul> | ||
| 35 | <li><p>Introduction</p></li> | ||
| 36 | <li><p>Components</p></li> | ||
| 37 | <li><p>Development</p></li> | ||
| 38 | <li><p>Q & A</p></li> | ||
| 39 | </ul> | ||
| 40 | </section> | ||
| 49 | 41 | ||
| 50 | <section> | 42 | <section> |
| 51 | <section> | 43 | <h2>Introduction</h2> |
| 52 | <h2>Components</h2> | 44 | <p><em>eValg 3</em></p> |
| 53 | <br/> | 45 | <ul> |
| 54 | <ul> | 46 | <li><p>Development started in 2017 and then again in 2018</p></li> |
| 55 | <li><p>Backend</p></li> | 47 | <li><p>The last trial election (https://valg-pilot.uio.no) ended 15.11.2019</p></li> |
| 56 | <li><p>Dataporten</p></li> | 48 | <li><p>Currently the eValg team consists of two developers (<em>skh</em>, <em>sgs</em>)</p></li> |
| 57 | <li><p>Frontend</p></li> | 49 | </ul> |
| 58 | </ul> | 50 | </section> |
| 59 | </section> | ||
| 60 | 51 | ||
| 61 | <section> | ||
| 62 | <h3>Backend</h3> | ||
| 63 | <ul> | ||
| 64 | <li><p>General: Python >= 3.6, Flask, SQLAlchemy</p></li> | ||
| 65 | <li><p>DB: Alembic, SQLAlchemy-continuum, Flask-migrate</p></li> | ||
| 66 | <li><p>Signing & encryption: pynacl</p></li> | ||
| 67 | <li><p>Querying (GraphQL): graphene, graphene-sqlalchemy</p></li> | ||
| 68 | <li><p>Others: pytz, sentry_sdk, python-json-logger and many more</p></li> | ||
| 69 | </ul> | ||
| 70 | </section> | ||
| 71 | 52 | ||
| 72 | <section> | 53 | <section> |
| 73 | <h3>Dataporten</h3> | 54 | <section> |
| 74 | <ul> | 55 | <h2>Components</h2> |
| 75 | <li><p>OpenID Connect: Moved from Authorization code flow to Implicit flow (id_token token) in 2018</p></li> | 56 | <br/> |
| 76 | </ul> | 57 | <ul> |
| 77 | </section> | 58 | <li><p>Backend</p></li> |
| 59 | <li><p>Dataporten</p></li> | ||
| 60 | <li><p>Frontend</p></li> | ||
| 61 | </ul> | ||
| 62 | </section> | ||
| 78 | 63 | ||
| 79 | <section> | 64 | <section> |
| 80 | <h3>Frontend</h3> | 65 | <h3>Backend</h3> |
| 81 | <ul> | 66 | <ul> |
| 82 | <li><p>General: React, react-oidc, GraphQL</p></li> | 67 | <li><p>General: Python >= 3.6, Flask, SQLAlchemy</p></li> |
| 83 | <li><p>Encryption: TweetNaCl - a crypto library in 100 tweets</p></li> | 68 | <li><p>DB: Alembic, SQLAlchemy-continuum, Flask-migrate</p></li> |
| 84 | <li><p>Many, many more libraries and packages</p></li> | 69 | <li><p>Signing & encryption: pynacl</p></li> |
| 85 | </ul> | 70 | <li><p>Querying (GraphQL): graphene, graphene-sqlalchemy</p></li> |
| 86 | </section> | 71 | <li><p>Others: pytz, sentry_sdk, python-json-logger and many more</p></li> |
| 72 | </ul> | ||
| 73 | </section> | ||
| 87 | 74 | ||
| 88 | </section> | 75 | <section> |
| 76 | <h3>Dataporten</h3> | ||
| 77 | <ul> | ||
| 78 | <li><p>OpenID Connect: Moved from Authorization code flow to Implicit flow (id_token token) in 2018</p></li> | ||
| 79 | </ul> | ||
| 80 | </section> | ||
| 89 | 81 | ||
| 82 | <section> | ||
| 83 | <h3>Frontend</h3> | ||
| 84 | <ul> | ||
| 85 | <li><p>General: React, react-oidc, GraphQL</p></li> | ||
| 86 | <li><p>Encryption: TweetNaCl - a crypto library in 100 tweets</p></li> | ||
| 87 | <li><p>Many, many more libraries and packages</p></li> | ||
| 88 | </ul> | ||
| 89 | </section> | ||
| 90 | 90 | ||
| 91 | <section> | 91 | </section> |
| 92 | 92 | ||
| 93 | <section> | ||
| 94 | <h2>Development</h2> | ||
| 95 | <br/> | ||
| 96 | <ul> | ||
| 97 | <li><p>Improvements</p></li> | ||
| 98 | <li><p>Goals & challenges</p></li> | ||
| 99 | </ul> | ||
| 100 | </section> | ||
| 101 | 93 | ||
| 102 | <section> | 94 | <section> |
| 103 | <h3>Improvements</h3> | ||
| 104 | <ul> | ||
| 105 | <li><p>Testing (<em>pytest</em>) & CI (<em>Jenkins</em>) - the best in USITINT so far?!? :)</p></li> | ||
| 106 | <li><p>No more FNR* and cross domain access. #GDPR</p></li> | ||
| 107 | <li><p>No more external programs (<em>GnuPG</em>, <em>evalg.jar</em>) and envelopes ...</p></li> | ||
| 108 | <li><p>Insight in all possible election paths in case of drawing</p></li> | ||
| 109 | |||
| 110 | </ul> | ||
| 111 | <p><small><em>* - work in progress</em></small></p> | ||
| 112 | </section> | ||
| 113 | 95 | ||
| 114 | <section> | 96 | <section> |
| 115 | <h3>Goals & challenges</h3> | 97 | <h2>Development</h2> |
| 116 | <ul> | 98 | <br/> |
| 117 | <li><p>Anyone should be able to create an election</p></li> | 99 | <ul> |
| 118 | <li><p>No private key shall ever live in the backend</p></li> | 100 | <li><p>Improvements</p></li> |
| 119 | <li><p>Log & audit all changes</p></li> | 101 | <li><p>Goals & challenges</p></li> |
| 120 | <li><p>It is never too late to change a pollbook</p></li> | 102 | </ul> |
| 121 | <li><p>It is never too late to master public-key cryptography ... :)</p></li> | 103 | </section> |
| 122 | </ul> | ||
| 123 | </section> | ||
| 124 | 104 | ||
| 125 | </section> | 105 | <section> |
| 106 | <h3>Improvements</h3> | ||
| 107 | <ul> | ||
| 108 | <li><p>Testing (<em>pytest</em>) & CI (<em>Jenkins</em>) - the best in USITINT so far?!? :)</p></li> | ||
| 109 | <li><p>No more FNR* and cross domain access. #GDPR</p></li> | ||
| 110 | <li><p>No more external programs (<em>GnuPG</em>, <em>evalg.jar</em>) and envelopes ...</p></li> | ||
| 111 | <li><p>Insight in all possible election paths in case of drawing</p></li> | ||
| 112 | |||
| 113 | </ul> | ||
| 114 | <p><small><em>* - work in progress</em></small></p> | ||
| 115 | </section> | ||
| 126 | 116 | ||
| 117 | <section> | ||
| 118 | <h3>Goals & challenges</h3> | ||
| 119 | <ul> | ||
| 120 | <li><p>Anyone should be able to create an election</p></li> | ||
| 121 | <li><p>No private key shall ever live in the backend</p></li> | ||
| 122 | <li><p>Log & audit all changes</p></li> | ||
| 123 | <li><p>It is never too late to change a pollbook</p></li> | ||
| 124 | <li><p>It is never too late to master public-key cryptography ... :)</p></li> | ||
| 125 | </ul> | ||
| 126 | </section> | ||
| 127 | 127 | ||
| 128 | <section> | 128 | </section> |
| 129 | <h2>Q & A</h2> | ||
| 130 | <h3>???</h3> | ||
| 131 | <p><em>“Old questions are not answered—they only go out of fashion”</em> - Donald Schön</p> | ||
| 132 | </section> | ||
| 133 | </div> | ||
| 134 | </div> | ||
| 135 | 129 | ||
| 136 | <script src="lib/js/head.min.js"></script> | ||
| 137 | <script src="js/reveal.js"></script> | ||
| 138 | <script> | ||
| 139 | 130 | ||
| 140 | // More info https://github.com/hakimel/reveal.js#configuration | 131 | <section> |
| 141 | Reveal.initialize({ | 132 | <h2>Q & A</h2> |
| 142 | controls: true, | 133 | <h3>???</h3> |
| 143 | progress: true, | 134 | <p><em>“Old questions are not answered—they only go out of fashion”</em> - Donald Schön</p> |
| 144 | history: true, | 135 | </section> |
| 145 | center: true, | 136 | </div> |
| 146 | transition: 'slide', // none/fade/slide/convex/concave/zoom | 137 | </div> |
| 147 | // More info https://github.com/hakimel/reveal.js#dependencies | ||
| 148 | dependencies: [ | ||
| 149 | { src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } }, | ||
| 150 | { src: 'plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }, | ||
| 151 | { src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }, | ||
| 152 | { src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }, | ||
| 153 | { src: 'plugin/zoom-js/zoom.js', async: true }, | ||
| 154 | { src: 'plugin/notes/notes.js', async: true } | ||
| 155 | ] | ||
| 156 | }); | ||
| 157 | </script> | ||
| 158 | 138 | ||
| 159 | </body> | 139 | <script src="dist/reveal.js"></script> |
| 140 | <script src="plugin/zoom/zoom.js"></script> | ||
| 141 | <script src="plugin/notes/notes.js"></script> | ||
| 142 | <script src="plugin/search/search.js"></script> | ||
| 143 | <script src="plugin/markdown/markdown.js"></script> | ||
| 144 | <script src="plugin/highlight/highlight.js"></script> | ||
| 145 | <script> | ||
| 146 | |||
| 147 | // Also available as an ES module, see: | ||
| 148 | // https://revealjs.com/initialization/ | ||
| 149 | Reveal.initialize({ | ||
| 150 | controls: true, | ||
| 151 | progress: true, | ||
| 152 | center: true, | ||
| 153 | hash: true, | ||
| 154 | |||
| 155 | // Learn about plugins: https://revealjs.com/plugins/ | ||
| 156 | plugins: [ RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight ] | ||
| 157 | }); | ||
| 158 | |||
| 159 | </script> | ||
| 160 | |||
| 161 | </body> | ||
| 160 | </html> | 162 | </html> |
diff --git a/reveal.js/flow.html b/reveal.js/flow.html deleted file mode 100755 index c5b2ef3..0000000 --- a/reveal.js/flow.html +++ /dev/null | |||
| @@ -1,194 +0,0 @@ | |||
| 1 | <!doctype html> | ||
| 2 | <html lang="en"> | ||
| 3 | <head> | ||
| 4 | <meta charset="utf-8"> | ||
| 5 | <title>QA flow</title> | ||
| 6 | <meta name="author" content="Simeon Simeonov"> | ||
| 7 | <meta name="apple-mobile-web-app-capable" content="yes"> | ||
| 8 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | ||
| 9 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| 10 | |||
| 11 | <link rel="stylesheet" href="dist/reset.css"> | ||
| 12 | <link rel="stylesheet" href="dist/reveal.css"> | ||
| 13 | |||
| 14 | <link rel="stylesheet" href="dist/theme/serif.css" id="theme"> | ||
| 15 | |||
| 16 | <!-- Theme used for syntax highlighting of code --> | ||
| 17 | <link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme"> | ||
| 18 | </head> | ||
| 19 | <body> | ||
| 20 | <div class="reveal"> | ||
| 21 | |||
| 22 | <!-- Any section element inside of this container is displayed as a slide --> | ||
| 23 | <div class="slides"> | ||
| 24 | <section> | ||
| 25 | <h1>QA flow</h1> | ||
| 26 | <h3>Idas and thoughts</h3> | ||
| 27 | </br> | ||
| 28 | <p> | ||
| 29 | <small>Simeon Simeonov</small> | ||
| 30 | </p> | ||
| 31 | </section> | ||
| 32 | |||
| 33 | <section> | ||
| 34 | <section id="fragments"> | ||
| 35 | <h2>Agenda</h2> | ||
| 36 | </br> | ||
| 37 | <ul> | ||
| 38 | <span class="fragment"><li>The problem with the current QA flow</li></span> | ||
| 39 | <span class="fragment"><li>Alternatives</li></span> | ||
| 40 | <span class="fragment"><li>Suggestions</li></span> | ||
| 41 | <span class="fragment"><li>Q&A and discussion</li></span> | ||
| 42 | </ul> | ||
| 43 | </section> | ||
| 44 | </section> | ||
| 45 | |||
| 46 | |||
| 47 | <section> | ||
| 48 | <section> | ||
| 49 | <h4>The problem with the current QA flow</h4> | ||
| 50 | <p><em>“Technical debt is a concept in software development that reflects the implied cost of additional rework caused by choosing an easy (limited) solution now instead of using a better approach that would take longer.”</em> - Wikipedia</p> | ||
| 51 | </br> | ||
| 52 | <p><em>“Accumulation of technical debt is not always a choice.”</em> - Simeon Simeonov, 21st century developer</p> | ||
| 53 | </section> | ||
| 54 | |||
| 55 | <section> | ||
| 56 | <h4>The problem with the current QA flow (2)</h4> | ||
| 57 | </br> | ||
| 58 | <ul> | ||
| 59 | <li>Based on pull-requests (tactical)</li> | ||
| 60 | <li>Poor or lack of technical (strategic) design</li> | ||
| 61 | <li>Lack of supervision</li> | ||
| 62 | </ul> | ||
| 63 | </section> | ||
| 64 | |||
| 65 | <section> | ||
| 66 | <h4>The problem with the current QA flow (3)</h4> | ||
| 67 | </br> | ||
| 68 | <p>Typical challenges</p> | ||
| 69 | <ul> | ||
| 70 | <li>What entities do we need and how will we name them? (module, package, a script...)</li> | ||
| 71 | <li>Where will the code reside?</li> | ||
| 72 | <li>What is the best way to interract? (Object-oriented design in the case of Python)</li> | ||
| 73 | <li>Is this the best way (even in pragmatic terms)?</li> | ||
| 74 | <li>Any known best practices?</li> | ||
| 75 | </ul> | ||
| 76 | </section> | ||
| 77 | |||
| 78 | </section> | ||
| 79 | |||
| 80 | |||
| 81 | <section> | ||
| 82 | <section> | ||
| 83 | <h3>Alternatives</h3> | ||
| 84 | </br> | ||
| 85 | <p>What do others do?</p> | ||
| 86 | </section> | ||
| 87 | |||
| 88 | <section> | ||
| 89 | <h3>Alternatives (2)</h3> | ||
| 90 | </br> | ||
| 91 | <p>Google, Microsoft...</p> | ||
| 92 | <ul> | ||
| 93 | <li>Detailed technical design is expensive and requires hierarchial structure</li> | ||
| 94 | <li>They do not exploit our logistical advantage of being in one place</li> | ||
| 95 | </ul> | ||
| 96 | </section> | ||
| 97 | |||
| 98 | <section> | ||
| 99 | <h3>Alternatives (3)</h3> | ||
| 100 | </br> | ||
| 101 | <p>Linux...</p> | ||
| 102 | <ul> | ||
| 103 | <li>Brutal and hierarchial structure with a lot of overhead</li> | ||
| 104 | <li>They do not exploit our logistical advantage of being in one place</li> | ||
| 105 | </ul> | ||
| 106 | </section> | ||
| 107 | |||
| 108 | </section> | ||
| 109 | |||
| 110 | |||
| 111 | <section> | ||
| 112 | |||
| 113 | <section> | ||
| 114 | <h3>Suggestions</h3> | ||
| 115 | </br> | ||
| 116 | <ul> | ||
| 117 | <li>Automate most of the current QA tasks through better test coverage, static checking and linting</li> | ||
| 118 | <li>Modify the QA flow</li> | ||
| 119 | <li>Consider a more detailed technical specification(s)</li> | ||
| 120 | </ul> | ||
| 121 | </section> | ||
| 122 | |||
| 123 | <section> | ||
| 124 | <h3>Automation</h3> | ||
| 125 | </br> | ||
| 126 | <ul> | ||
| 127 | <li>better test coverage - as much as possible, something is better than nothing</li> | ||
| 128 | <li>own flake / pylint modules, as well as disabling the annoying ones as part of CI</li> | ||
| 129 | </ul> | ||
| 130 | </section> | ||
| 131 | |||
| 132 | <section> | ||
| 133 | <h3>New QA flow</h3> | ||
| 134 | </br> | ||
| 135 | <ol> | ||
| 136 | <li>Decide whether the task is "strategical" during planning and mark it accordingly in Jira</li> | ||
| 137 | <li>Assign supervising developer once the task is picked</li> | ||
| 138 | <li>Commence the design phase(*)</li> | ||
| 139 | <li>Approval by the supervising developer should be required</li> | ||
| 140 | </ol> | ||
| 141 | </section> | ||
| 142 | |||
| 143 | <section> | ||
| 144 | <h3>New QA flow (2)</h3> | ||
| 145 | </br> | ||
| 146 | <p>Design phase</p> | ||
| 147 | <ol> | ||
| 148 | <li>Identify all entities, entry points and parameters (in case of CLI)</li> | ||
| 149 | <li>Decide on names, naming conventions and where different enteties will reside</li> | ||
| 150 | <li>Decide on code entities and define the public interface (OO design)</li> | ||
| 151 | </ol> | ||
| 152 | </section> | ||
| 153 | |||
| 154 | <section> | ||
| 155 | <h3>A more detailed technical specification?</h3> | ||
| 156 | </br> | ||
| 157 | <p>May be feasible when developing new systems and micro-services</p> | ||
| 158 | </section> | ||
| 159 | |||
| 160 | </section> | ||
| 161 | |||
| 162 | |||
| 163 | <section> | ||
| 164 | <h1>Q&A</h1> | ||
| 165 | </section> | ||
| 166 | |||
| 167 | |||
| 168 | </div> | ||
| 169 | </div> | ||
| 170 | |||
| 171 | <script src="dist/reveal.js"></script> | ||
| 172 | <script src="plugin/zoom/zoom.js"></script> | ||
| 173 | <script src="plugin/notes/notes.js"></script> | ||
| 174 | <script src="plugin/search/search.js"></script> | ||
| 175 | <script src="plugin/markdown/markdown.js"></script> | ||
| 176 | <script src="plugin/highlight/highlight.js"></script> | ||
| 177 | <script> | ||
| 178 | |||
| 179 | // Also available as an ES module, see: | ||
| 180 | // https://revealjs.netlify.app/initialization/ | ||
| 181 | Reveal.initialize({ | ||
| 182 | controls: true, | ||
| 183 | progress: true, | ||
| 184 | center: true, | ||
| 185 | hash: true, | ||
| 186 | |||
| 187 | // Learn about plugins: https://revealjs.netlify.app/plugins/ | ||
| 188 | plugins: [ RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight ] | ||
| 189 | }); | ||
| 190 | |||
| 191 | </script> | ||
| 192 | |||
| 193 | </body> | ||
| 194 | </html> | ||
diff --git a/reveal.js/gateway.html b/reveal.js/gateway.html deleted file mode 100644 index 4996a9c..0000000 --- a/reveal.js/gateway.html +++ /dev/null | |||
| @@ -1,152 +0,0 @@ | |||
| 1 | <!doctype html> | ||
| 2 | <html lang="en"> | ||
| 3 | <head> | ||
| 4 | <meta charset="utf-8"> | ||
| 5 | <title>API gateway</title> | ||
| 6 | <meta name="author" content="Simeon Simeonov"> | ||
| 7 | <meta name="apple-mobile-web-app-capable" content="yes"> | ||
| 8 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | ||
| 9 | |||
| 10 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| 11 | |||
| 12 | <link rel="stylesheet" href="dist/reset.css"> | ||
| 13 | <link rel="stylesheet" href="dist/reveal.css"> | ||
| 14 | <link rel="stylesheet" href="dist/theme/serif.css" id="theme"> | ||
| 15 | |||
| 16 | <!-- Theme used for syntax highlighting of code --> | ||
| 17 | <link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme"> | ||
| 18 | <!-- Printing and PDF exports --> | ||
| 19 | <script> | ||
| 20 | var link = document.createElement( 'link' ); | ||
| 21 | link.rel = 'stylesheet'; | ||
| 22 | link.type = 'text/css'; | ||
| 23 | link.href = window.location.search.match( /print-pdf/gi ) ? 'css/print/pdf.css' : 'css/print/paper.css'; | ||
| 24 | document.getElementsByTagName( 'head' )[0].appendChild( link ); | ||
| 25 | </script> | ||
| 26 | <!--[if lt IE 9]> | ||
| 27 | <script src="lib/js/html5shiv.js"></script> | ||
| 28 | <![endif]--> | ||
| 29 | </head> | ||
| 30 | <body> | ||
| 31 | <div class="reveal"> | ||
| 32 | |||
| 33 | <!-- Any section element inside of this container is displayed as a slide --> | ||
| 34 | <div class="slides"> | ||
| 35 | <section> | ||
| 36 | <h1>API gateway</h1> | ||
| 37 | <h3>Cerebrum-seminar 2016</h3> | ||
| 38 | </br> | ||
| 39 | <p> | ||
| 40 | <small>Simeon Simeonov</small> | ||
| 41 | </p> | ||
| 42 | </section> | ||
| 43 | |||
| 44 | <section> | ||
| 45 | <h2>Simeon Simeonov</h2> | ||
| 46 | </br> | ||
| 47 | <ul> | ||
| 48 | <li> | ||
| 49 | <p>... also known as <em>sgs</em> (<a href="http://folk.uio.no/sgs/">http://folk.uio.no/sgs/</a>) | ||
| 50 | </p> | ||
| 51 | </li> | ||
| 52 | <li><p>Cerebrum developer since February 2015</p></li> | ||
| 53 | </ul> | ||
| 54 | </section> | ||
| 55 | |||
| 56 | |||
| 57 | <section> | ||
| 58 | <h2>API gateway vs. API proxy</h2> | ||
| 59 | </br> | ||
| 60 | <p>The integration project at UiO (<em>INT</em>) defines the role <em>API Manager</em>. Why use API gateway instead of API proxy?</p> | ||
| 61 | <ul> | ||
| 62 | <li><p>Unified access for all our APIs</p></li> | ||
| 63 | <li><p>Unified documentation and catalogue</p></li> | ||
| 64 | <li><p>Unified caching for all APIs</p></li> | ||
| 65 | <li><p>Unified logging / statistics generation for all APIs</p></li> | ||
| 66 | <li><p>Access control / rate limiting for all our APIs</p></li> | ||
| 67 | <li><p>(Re)mapping and (re)writing</p></li> | ||
| 68 | </ul> | ||
| 69 | </section> | ||
| 70 | |||
| 71 | |||
| 72 | <section> | ||
| 73 | <section> | ||
| 74 | <h2>API gateway & Cerebrum</h2> | ||
| 75 | </br> | ||
| 76 | <p>For the time being we have settled on <em>Tyk API gateway</em>.</p> | ||
| 77 | <p>It uses an internal user-database and consists of the following logical components:</p> | ||
| 78 | <ul> | ||
| 79 | <li><p>Dashboard</p></li> | ||
| 80 | <li><p>Developer portal</p></li> | ||
| 81 | <li><p>Gateway</p></li> | ||
| 82 | </ul> | ||
| 83 | </section> | ||
| 84 | |||
| 85 | <section> | ||
| 86 | <h2>Dashboard</h2> | ||
| 87 | <p>Provides a convenient web-interface for configuration and manipulation of the gateway and the different APIs.</p> | ||
| 88 | <img src="images/gateway/dashboard01.png"></img> | ||
| 89 | |||
| 90 | </section> | ||
| 91 | |||
| 92 | <section> | ||
| 93 | <h2>Portal</h2> | ||
| 94 | Provides the API documentation and catalogue. | ||
| 95 | <img src="images/gateway/portal01.png"></img> | ||
| 96 | </section> | ||
| 97 | |||
| 98 | <section> | ||
| 99 | <h2>Portal</h2> | ||
| 100 | <img src="images/gateway/portal02.png"></img> | ||
| 101 | </section> | ||
| 102 | |||
| 103 | <section> | ||
| 104 | <h2>Gataway</h2> | ||
| 105 | </br> | ||
| 106 | <ul> | ||
| 107 | <li><p>Still in experimental / pilot phase</p></li> | ||
| 108 | <li><p>Several APIs are already configured in Tyk (Cerebrum WS, SAP WS... )</p></li> | ||
| 109 | <li><p>Support for many <em>authentication modes</em> (Auth Token, JWT, OAuth 2, Basic Auth, Open ID Connect, ...)</p></li> | ||
| 110 | </ul> | ||
| 111 | <p> | ||
| 112 | Official site: <a href="https://tyk.io/">https://tyk.io/</a> | ||
| 113 | </p> | ||
| 114 | </section> | ||
| 115 | |||
| 116 | </section> | ||
| 117 | |||
| 118 | |||
| 119 | <section> | ||
| 120 | <h1>Q&A</h1> | ||
| 121 | </section> | ||
| 122 | |||
| 123 | |||
| 124 | </div> | ||
| 125 | </div> | ||
| 126 | |||
| 127 | <script src="lib/js/head.min.js"></script> | ||
| 128 | <script src="js/reveal.js"></script> | ||
| 129 | <script> | ||
| 130 | // More info https://github.com/hakimel/reveal.js#configuration | ||
| 131 | Reveal.initialize({ | ||
| 132 | controls: true, | ||
| 133 | progress: true, | ||
| 134 | history: true, | ||
| 135 | center: true, | ||
| 136 | |||
| 137 | transition: 'slide', // none/fade/slide/convex/concave/zoom | ||
| 138 | |||
| 139 | // More info https://github.com/hakimel/reveal.js#dependencies | ||
| 140 | dependencies: [ | ||
| 141 | { src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } }, | ||
| 142 | { src: 'plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }, | ||
| 143 | { src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }, | ||
| 144 | { src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }, | ||
| 145 | { src: 'plugin/zoom-js/zoom.js', async: true }, | ||
| 146 | { src: 'plugin/notes/notes.js', async: true } | ||
| 147 | ] | ||
| 148 | }); | ||
| 149 | </script> | ||
| 150 | |||
| 151 | </body> | ||
| 152 | </html> | ||
diff --git a/reveal.js/mqprod.html b/reveal.js/mqprod.html index fb3caf8..9aef4bc 100644 --- a/reveal.js/mqprod.html +++ b/reveal.js/mqprod.html | |||
| @@ -1,248 +1,248 @@ | |||
| 1 | <!doctype html> | 1 | <!doctype html> |
| 2 | <html lang="en"> | 2 | <html lang="en"> |
| 3 | <head> | 3 | <head> |
| 4 | <meta charset="utf-8"> | 4 | <meta charset="utf-8"> |
| 5 | <title>Meldingskø for UiO</title> | 5 | <title>Meldingskø for UiO</title> |
| 6 | <meta name="author" content="Kai Vaade, Simeon Simeonov"> | 6 | <meta name="author" content="Kai Vaade, Simeon Simeonov"> |
| 7 | <meta name="apple-mobile-web-app-capable" content="yes"> | 7 | <meta name="apple-mobile-web-app-capable" content="yes"> |
| 8 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | 8 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> |
| 9 | 9 | ||
| 10 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | 10 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 11 | 11 | ||
| 12 | <link rel="stylesheet" href="dist/reset.css"> | 12 | <link rel="stylesheet" href="dist/reset.css"> |
| 13 | <link rel="stylesheet" href="dist/reveal.css"> | 13 | <link rel="stylesheet" href="dist/reveal.css"> |
| 14 | <link rel="stylesheet" href="dist/theme/serif.css" id="theme"> | 14 | <link rel="stylesheet" href="dist/theme/serif.css" id="theme"> |
| 15 | 15 | ||
| 16 | <!-- Theme used for syntax highlighting of code --> | 16 | <!-- Theme used for syntax highlighting of code --> |
| 17 | <link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme"> | 17 | <link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme"> |
| 18 | </head> | 18 | </head> |
| 19 | <body> | 19 | <body> |
| 20 | <div class="reveal"> | 20 | <div class="reveal"> |
| 21 | 21 | ||
| 22 | <!-- Any section element inside of this container is displayed as a slide --> | 22 | <!-- Any section element inside of this container is displayed as a slide --> |
| 23 | <div class="slides"> | 23 | <div class="slides"> |
| 24 | <section> | 24 | <section> |
| 25 | <h2>Meldingskø for UiO</h2> | 25 | <h2>Meldingskø for UiO</h2> |
| 26 | <p>Utviklerforum 14.12.2017</p> | 26 | <p>Utviklerforum 14.12.2017</p> |
| 27 | <br/> | 27 | <br/> |
| 28 | <p> | 28 | <p> |
| 29 | <small>Kai Vaade (KIA), Simeon Simeonov (INT)</small> | 29 | <small>Kai Vaade (KIA), Simeon Simeonov (INT)</small> |
| 30 | </p> | 30 | </p> |
| 31 | </section> | 31 | </section> |
| 32 | 32 | ||
| 33 | <section> | 33 | <section> |
| 34 | <h2>Agenda</h2> | 34 | <h2>Agenda</h2> |
| 35 | <br/> | 35 | <br/> |
| 36 | <ul> | 36 | <ul> |
| 37 | <li>Administrasjon</li> | 37 | <li>Administrasjon</li> |
| 38 | <li>Roller og entiteter</li> | 38 | <li>Roller og entiteter</li> |
| 39 | <li>Flyt</li> | 39 | <li>Flyt</li> |
| 40 | <li>Rettigheter</li> | 40 | <li>Rettigheter</li> |
| 41 | <li>Q & A</li> | 41 | <li>Q & A</li> |
| 42 | </ul> | 42 | </ul> |
| 43 | </section> | 43 | </section> |
| 44 | 44 | ||
| 45 | <section> | 45 | <section> |
| 46 | <h2>Generelt</h2> | 46 | <h2>Generelt</h2> |
| 47 | </br> | 47 | </br> |
| 48 | <p>Vi bruker RabbitMQ med AMQP 0.9.1 til å utveksle / behandle meldinger (JSON, <a href="https://tools.ietf.org/html/draft-hunt-idevent-scim-00">SCIM</a>)</p> | 48 | <p>Vi bruker RabbitMQ med AMQP 0.9.1 til å utveksle / behandle meldinger (JSON, <a href="https://tools.ietf.org/html/draft-hunt-idevent-scim-00">SCIM</a>)</p> |
| 49 | <p>Bruk av message-broker startet i 2015. I produksjon siden høsten 2017.</p> | 49 | <p>Bruk av message-broker startet i 2015. I produksjon siden høsten 2017.</p> |
| 50 | <p>Driftes av KIA.</p> | 50 | <p>Driftes av KIA.</p> |
| 51 | </section> | 51 | </section> |
| 52 | 52 | ||
| 53 | <section> | 53 | <section> |
| 54 | <h2>Administrasjon</h2> | 54 | <h2>Administrasjon</h2> |
| 55 | <p>Cerebrum har brukt MQ et års tid på egen server satt opp av Seksjon for integrasjon og elektroniske identiteter (USITINT).</p> | 55 | <p>Cerebrum har brukt MQ et års tid på egen server satt opp av Seksjon for integrasjon og elektroniske identiteter (USITINT).</p> |
| 56 | <p>Nå er driften av tjenesten flyttet fra prosjektet og inn i linja, Gruppe for drift av katalog-, integrasjon- og autentiseringstjenester (KIA).</p> | 56 | <p>Nå er driften av tjenesten flyttet fra prosjektet og inn i linja, Gruppe for drift av katalog-, integrasjon- og autentiseringstjenester (KIA).</p> |
| 57 | </section> | 57 | </section> |
| 58 | 58 | ||
| 59 | <section> | 59 | <section> |
| 60 | <h2>Administrasjon (forts...)</h2> | 60 | <h2>Administrasjon (forts...)</h2> |
| 61 | <p>Vi har softlaunchet meldingskø-tjenesten i høst. Tjenesten er ikke "offisielt" lansert, kjører kun våre egne ting (Cerebrum/SAP) foreløpig.</p> | 61 | <p>Vi har softlaunchet meldingskø-tjenesten i høst. Tjenesten er ikke "offisielt" lansert, kjører kun våre egne ting (Cerebrum/SAP) foreløpig.</p> |
| 62 | <p>Antakelig gjør vi tjenesten kjent samtidig som et annet delprosjekt (API Manager) i UiO INTARK lanseres i nær fremtid.</p> | 62 | <p>Antakelig gjør vi tjenesten kjent samtidig som et annet delprosjekt (API Manager) i UiO INTARK lanseres i nær fremtid.</p> |
| 63 | </section> | 63 | </section> |
| 64 | 64 | ||
| 65 | <section> | 65 | <section> |
| 66 | <h2>Administrasjon (forts...)</h2> | 66 | <h2>Administrasjon (forts...)</h2> |
| 67 | <p>Foreløpig så har vi ikke så veldig mye erfaring med tjenesten i linja.</p> | 67 | <p>Foreløpig så har vi ikke så veldig mye erfaring med tjenesten i linja.</p> |
| 68 | <p>Vi vet heller ikke så mye om behovet; dvs hvor mange som kommer til å bruke tjenesten.</p> | 68 | <p>Vi vet heller ikke så mye om behovet; dvs hvor mange som kommer til å bruke tjenesten.</p> |
| 69 | </section> | 69 | </section> |
| 70 | 70 | ||
| 71 | <section> | 71 | <section> |
| 72 | <h2>Administrasjon (forts...)</h2> | 72 | <h2>Administrasjon (forts...)</h2> |
| 73 | <p>Hvordan kontakte oss:</p> | 73 | <p>Hvordan kontakte oss:</p> |
| 74 | <p><a href="https://www.usit.uio.no/om/organisasjon/iti/td/kia/dokumentasjon/meldingsko/" target="_blank">https://www.usit.uio.no/om/organisasjon/iti/td/kia/dokumentasjon/meldingsko/</a></p> | 74 | <p><a href="https://www.usit.uio.no/om/organisasjon/iti/td/kia/dokumentasjon/meldingsko/" target="_blank">https://www.usit.uio.no/om/organisasjon/iti/td/kia/dokumentasjon/meldingsko/</a></p> |
| 75 | <p>Tilgang til grensesnittet: <a href="https://mq.uio.no/">https://mq.uio.no</a></p> | 75 | <p>Tilgang til grensesnittet: <a href="https://mq.uio.no/">https://mq.uio.no</a></p> |
| 76 | </section> | 76 | </section> |
| 77 | 77 | ||
| 78 | <section> | 78 | <section> |
| 79 | <h2>Roller og entiteter</h2> | 79 | <h2>Roller og entiteter</h2> |
| 80 | <ul> | 80 | <ul> |
| 81 | <p>Roller:</p> | 81 | <p>Roller:</p> |
| 82 | <li>Administrator / Manager</li> | 82 | <li>Administrator / Manager</li> |
| 83 | <li>Consumer / Konsument</li> | 83 | <li>Consumer / Konsument</li> |
| 84 | <li>Publisher / Publisist :)</li> | 84 | <li>Publisher / Publisist :)</li> |
| 85 | <p>Entiteter:</p> | 85 | <p>Entiteter:</p> |
| 86 | <li>vhost</li> | 86 | <li>vhost</li> |
| 87 | <li>Exchange (direct, fanout, topic, ...)</li> | 87 | <li>Exchange (direct, fanout, topic, ...)</li> |
| 88 | <li>Kø (transient, durable)</li> | 88 | <li>Kø (transient, durable)</li> |
| 89 | <li>Binding</li> | 89 | <li>Binding</li> |
| 90 | </ul> | 90 | </ul> |
| 91 | </section> | 91 | </section> |
| 92 | 92 | ||
| 93 | <section> | 93 | <section> |
| 94 | <h2>Flyt</h2> | 94 | <h2>Flyt</h2> |
| 95 | </br> | 95 | </br> |
| 96 | <img src="images/mqprod/flow01.png"></img> | 96 | <img src="images/mqprod/flow01.png"></img> |
| 97 | </section> | 97 | </section> |
| 98 | 98 | ||
| 99 | <section> | 99 | <section> |
| 100 | <h2>Rettigheter</h2> | 100 | <h2>Rettigheter</h2> |
| 101 | <p>RabbitMQ implementerer 2 rettighetsnivåer: per <em>vhost</em> og per entitet</p> | 101 | <p>RabbitMQ implementerer 2 rettighetsnivåer: per <em>vhost</em> og per entitet</p> |
| 102 | <p>RabbitMQ (AMQP) definerer 3 typer operasjoner:</p> | 102 | <p>RabbitMQ (AMQP) definerer 3 typer operasjoner:</p> |
| 103 | <ul> | 103 | <ul> |
| 104 | <li><p><em>configure</em> - opprette / slette entiteter eller endre deres oppførsel</p></li> | 104 | <li><p><em>configure</em> - opprette / slette entiteter eller endre deres oppførsel</p></li> |
| 105 | <li><p><em>write</em> - skrive melding til en entitet</p></li> | 105 | <li><p><em>write</em> - skrive melding til en entitet</p></li> |
| 106 | <li><p><em>read</em> - lese melding fra entitet</p></li> | 106 | <li><p><em>read</em> - lese melding fra entitet</p></li> |
| 107 | </ul> | 107 | </ul> |
| 108 | </section> | 108 | </section> |
| 109 | 109 | ||
| 110 | <section> | 110 | <section> |
| 111 | <h2>Rettigheter og brukere</h2> | 111 | <h2>Rettigheter og brukere</h2> |
| 112 | <p>RabbitMQ bruker <em>regular expressions</em> til å definere rettigheter</p> | 112 | <p>RabbitMQ bruker <em>regular expressions</em> til å definere rettigheter</p> |
| 113 | <ul> | 113 | <ul> |
| 114 | <li><p><small><em>rabbitmqctl add_user cerebrum <passord></em></small></p></li> | 114 | <li><p><small><em>rabbitmqctl add_user cerebrum <passord></em></small></p></li> |
| 115 | <li><p><small><em>rabbitmqctl add_user uio_ad_microservice <passord></em></small></p></li> | 115 | <li><p><small><em>rabbitmqctl add_user uio_ad_microservice <passord></em></small></p></li> |
| 116 | <li><p><small><em>rabbitmqctl set_permissions -p /no/uio/integration cerebrum "^$" "^ex_.*" "^$"</em></small></p></li> | 116 | <li><p><small><em>rabbitmqctl set_permissions -p /no/uio/integration cerebrum "^$" "^ex_.*" "^$"</em></small></p></li> |
| 117 | <li><p><small><em>rabbitmqctl set_permissions -p /no/uio/integration uio_ad_microservice "^q_ad_ms_.*" "^q_ad_ms_.*" "^(ex_messages|q_ad_ms_.*)$"</em></small></p></li> | 117 | <li><p><small><em>rabbitmqctl set_permissions -p /no/uio/integration uio_ad_microservice "^q_ad_ms_.*" "^q_ad_ms_.*" "^(ex_messages|q_ad_ms_.*)$"</em></small></p></li> |
| 118 | </ul> | 118 | </ul> |
| 119 | </section> | 119 | </section> |
| 120 | 120 | ||
| 121 | <section> | 121 | <section> |
| 122 | <h2><em>Routing keys</em> i <em>topic exchange</em></h2> | 122 | <h2><em>Routing keys</em> i <em>topic exchange</em></h2> |
| 123 | <p><em>Routing key</em> settes av sender som en del av meldingen og blir inspisert av brokeren dersom meldingen sendes til en <em>topic exchange</em>.</p> | 123 | <p><em>Routing key</em> settes av sender som en del av meldingen og blir inspisert av brokeren dersom meldingen sendes til en <em>topic exchange</em>.</p> |
| 124 | <p>Strukturen til en topic / message routing key er:</p> | 124 | <p>Strukturen til en topic / message routing key er:</p> |
| 125 | <p><small><kilde>.<type>.<objekt>.<hendelse></small></p> | 125 | <p><small><kilde>.<type>.<objekt>.<hendelse></small></p> |
| 126 | <p>F.eks.:</p> | 126 | <p>F.eks.:</p> |
| 127 | <p><small>cerebrum.event.person.delete</small></p> | 127 | <p><small>cerebrum.event.person.delete</small></p> |
| 128 | </section> | 128 | </section> |
| 129 | 129 | ||
| 130 | <section> | 130 | <section> |
| 131 | <h3>Dokumentasjon og lenker</h3> | 131 | <h3>Dokumentasjon og lenker</h3> |
| 132 | <p> | 132 | <p> |
| 133 | RabbitMQ: | 133 | RabbitMQ: |
| 134 | </p> | 134 | </p> |
| 135 | <ul> | 135 | <ul> |
| 136 | <li> | 136 | <li> |
| 137 | <p> | 137 | <p> |
| 138 | <small> | 138 | <small> |
| 139 | <a href="http://www.rabbitmq.com">http://www.rabbitmq.com</a> - RabbitMQ sin offisielle side | 139 | <a href="http://www.rabbitmq.com">http://www.rabbitmq.com</a> - RabbitMQ sin offisielle side |
| 140 | </small> | 140 | </small> |
| 141 | </li> | 141 | </li> |
| 142 | <li> | 142 | <li> |
| 143 | <p> | 143 | <p> |
| 144 | <small> | 144 | <small> |
| 145 | <a href="http://www.rabbitmq.com/configure.html">http://www.rabbitmq.com/configure.html</a> - Generell konfigurasjon av RabbitMQ | 145 | <a href="http://www.rabbitmq.com/configure.html">http://www.rabbitmq.com/configure.html</a> - Generell konfigurasjon av RabbitMQ |
| 146 | </small> | 146 | </small> |
| 147 | </li> | 147 | </li> |
| 148 | <li> | 148 | <li> |
| 149 | <p> | 149 | <p> |
| 150 | <small> | 150 | <small> |
| 151 | <a href="http://www.rabbitmq.com/management.html">http://www.rabbitmq.com/management.html</a> - Bruk av rabbitmq_management plugin i RabbitMQ | 151 | <a href="http://www.rabbitmq.com/management.html">http://www.rabbitmq.com/management.html</a> - Bruk av rabbitmq_management plugin i RabbitMQ |
| 152 | </small> | 152 | </small> |
| 153 | </li> | 153 | </li> |
| 154 | <li> | 154 | <li> |
| 155 | <p> | 155 | <p> |
| 156 | <small> | 156 | <small> |
| 157 | <a href="http://www.rabbitmq.com/access-control.html">http://www.rabbitmq.com/access-control.html</a> - Tilgangskontroll i RabbitMQ | 157 | <a href="http://www.rabbitmq.com/access-control.html">http://www.rabbitmq.com/access-control.html</a> - Tilgangskontroll i RabbitMQ |
| 158 | </small> | 158 | </small> |
| 159 | </li> | 159 | </li> |
| 160 | <li> | 160 | <li> |
| 161 | <p> | 161 | <p> |
| 162 | <small> | 162 | <small> |
| 163 | <a href="http://www.rabbitmq.com/man/rabbitmqctl.1.man.html">http://www.rabbitmq.com/man/rabbitmqctl.1.man.html</a> - rabbitmqctl manual side | 163 | <a href="http://www.rabbitmq.com/man/rabbitmqctl.1.man.html">http://www.rabbitmq.com/man/rabbitmqctl.1.man.html</a> - rabbitmqctl manual side |
| 164 | </small> | 164 | </small> |
| 165 | </li> | 165 | </li> |
| 166 | <li> | 166 | <li> |
| 167 | <p> | 167 | <p> |
| 168 | <small> | 168 | <small> |
| 169 | <a href="http://www.rabbitmq.com/tutorials/amqp-concepts.html">http://www.rabbitmq.com/tutorials/amqp-concepts.html</a> - Generelt om AMQP og forskjellige exchanges | 169 | <a href="http://www.rabbitmq.com/tutorials/amqp-concepts.html">http://www.rabbitmq.com/tutorials/amqp-concepts.html</a> - Generelt om AMQP og forskjellige exchanges |
| 170 | </small> | 170 | </small> |
| 171 | </li> | 171 | </li> |
| 172 | </ul> | 172 | </ul> |
| 173 | </section> | 173 | </section> |
| 174 | 174 | ||
| 175 | <section> | 175 | <section> |
| 176 | <h3>Dokumentasjon og lenker</h3> | 176 | <h3>Dokumentasjon og lenker</h3> |
| 177 | <p> | 177 | <p> |
| 178 | UiO: | 178 | UiO: |
| 179 | </p> | 179 | </p> |
| 180 | <ul> | 180 | <ul> |
| 181 | <li> | 181 | <li> |
| 182 | <p> | 182 | <p> |
| 183 | <small> | 183 | <small> |
| 184 | <a href="http://www.usit.uio.no/om/tjenestegrupper/cerebrum/utvikling/dokumentasjon/tools/rabbitmq.html">Installasjon og oppsett av RabbitMQ for Cerebrum utviklere</a> | 184 | <a href="http://www.usit.uio.no/om/tjenestegrupper/cerebrum/utvikling/dokumentasjon/tools/rabbitmq.html">Installasjon og oppsett av RabbitMQ for Cerebrum utviklere</a> |
| 185 | </small> | 185 | </small> |
| 186 | </li> | 186 | </li> |
| 187 | <li> | 187 | <li> |
| 188 | <p> | 188 | <p> |
| 189 | <small> | 189 | <small> |
| 190 | <a href="http://www.usit.uio.no/om/tjenestegrupper/cerebrum/utvikling/aktiviteter/office365-uia/broker/rabbitmq.html">Oppsett av RabbitMQ for Office365 integrasjon mot UiA</a> | 190 | <a href="http://www.usit.uio.no/om/tjenestegrupper/cerebrum/utvikling/aktiviteter/office365-uia/broker/rabbitmq.html">Oppsett av RabbitMQ for Office365 integrasjon mot UiA</a> |
| 191 | </small> | 191 | </small> |
| 192 | </li> | 192 | </li> |
| 193 | <li> | 193 | <li> |
| 194 | <p> | 194 | <p> |
| 195 | <small> | 195 | <small> |
| 196 | <a href="https://www.usit.uio.no/om/organisasjon/uav/usitint/interndokumentasjon/integrasjon/broker/utredning-meldingsflyt.html">Utredning: Meldingsflyt, kø-struktur og navngiving</a> (intern) | 196 | <a href="https://www.usit.uio.no/om/organisasjon/uav/usitint/interndokumentasjon/integrasjon/broker/utredning-meldingsflyt.html">Utredning: Meldingsflyt, kø-struktur og navngiving</a> (intern) |
| 197 | </small> | 197 | </small> |
| 198 | </li> | 198 | </li> |
| 199 | </ul> | 199 | </ul> |
| 200 | <p> | 200 | <p> |
| 201 | Bøker: | 201 | Bøker: |
| 202 | </p> | 202 | </p> |
| 203 | <ul> | 203 | <ul> |
| 204 | <li> | 204 | <li> |
| 205 | <p> | 205 | <p> |
| 206 | <small> | 206 | <small> |
| 207 | RabbitMQ in action - Alvaro Videla / Jason J.W. Williams - 2012 - Manning | 207 | RabbitMQ in action - Alvaro Videla / Jason J.W. Williams - 2012 - Manning |
| 208 | </small> | 208 | </small> |
| 209 | </li> | 209 | </li> |
| 210 | <li> | 210 | <li> |
| 211 | <p> | 211 | <p> |
| 212 | <small> | 212 | <small> |
| 213 | Mastering RabbitMQ - Ayanoglu / Aytas / Nahum - 2015 - PACKT Publishing | 213 | Mastering RabbitMQ - Ayanoglu / Aytas / Nahum - 2015 - PACKT Publishing |
| 214 | </small> | 214 | </small> |
| 215 | </li> | 215 | </li> |
| 216 | </section> | 216 | </section> |
| 217 | 217 | ||
| 218 | <section> | 218 | <section> |
| 219 | <h1>Q & A</h1> | 219 | <h1>Q & A</h1> |
| 220 | </section> | 220 | </section> |
| 221 | 221 | ||
| 222 | </div> | 222 | </div> |
| 223 | </div> | 223 | </div> |
| 224 | 224 | ||
| 225 | <script src="dist/reveal.js"></script> | 225 | <script src="dist/reveal.js"></script> |
| 226 | <script src="plugin/zoom/zoom.js"></script> | 226 | <script src="plugin/zoom/zoom.js"></script> |
| 227 | <script src="plugin/notes/notes.js"></script> | 227 | <script src="plugin/notes/notes.js"></script> |
| 228 | <script src="plugin/search/search.js"></script> | 228 | <script src="plugin/search/search.js"></script> |
| 229 | <script src="plugin/markdown/markdown.js"></script> | 229 | <script src="plugin/markdown/markdown.js"></script> |
| 230 | <script src="plugin/highlight/highlight.js"></script> | 230 | <script src="plugin/highlight/highlight.js"></script> |
| 231 | <script> | 231 | <script> |
| 232 | 232 | ||
| 233 | // Also available as an ES module, see: | 233 | // Also available as an ES module, see: |
| 234 | // https://revealjs.netlify.app/initialization/ | 234 | // https://revealjs.com/initialization/ |
| 235 | Reveal.initialize({ | 235 | Reveal.initialize({ |
| 236 | controls: true, | 236 | controls: true, |
| 237 | progress: true, | 237 | progress: true, |
| 238 | center: true, | 238 | center: true, |
| 239 | hash: true, | 239 | hash: true, |
| 240 | 240 | ||
| 241 | // Learn about plugins: https://revealjs.netlify.app/plugins/ | 241 | // Learn about plugins: https://revealjs.com/plugins/ |
| 242 | plugins: [ RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight ] | 242 | plugins: [ RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight ] |
| 243 | }); | 243 | }); |
| 244 | 244 | ||
| 245 | </script> | 245 | </script> |
| 246 | 246 | ||
| 247 | </body> | 247 | </body> |
| 248 | </html> | 248 | </html> |
diff --git a/reveal.js/postgresql_tuning.html b/reveal.js/postgresql_tuning.html index d7e3466..8678513 100644 --- a/reveal.js/postgresql_tuning.html +++ b/reveal.js/postgresql_tuning.html | |||
| @@ -1,152 +1,151 @@ | |||
| 1 | <!doctype html> | 1 | <!doctype html> |
| 2 | <html lang="en"> | 2 | <html lang="en"> |
| 3 | <head> | 3 | <head> |
| 4 | <meta charset="utf-8"> | 4 | <meta charset="utf-8"> |
| 5 | <title>SQLAlchemy</title> | 5 | <title>SQLAlchemy</title> |
| 6 | <meta name="author" content="Simeon Simeonov"> | 6 | <meta name="author" content="Simeon Simeonov"> |
| 7 | <meta name="apple-mobile-web-app-capable" content="yes"> | 7 | <meta name="apple-mobile-web-app-capable" content="yes"> |
| 8 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | 8 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> |
| 9 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | 9 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 10 | 10 | ||
| 11 | <link rel="stylesheet" href="dist/reset.css"> | 11 | <link rel="stylesheet" href="dist/reset.css"> |
| 12 | <link rel="stylesheet" href="dist/reveal.css"> | 12 | <link rel="stylesheet" href="dist/reveal.css"> |
| 13 | 13 | ||
| 14 | <link rel="stylesheet" href="dist/theme/fifty.css" id="theme"> | 14 | <link rel="stylesheet" href="dist/theme/fifty.css" id="theme"> |
| 15 | 15 | ||
| 16 | <!-- Theme used for syntax highlighting of code --> | 16 | <!-- Theme used for syntax highlighting of code --> |
| 17 | <link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme"> | 17 | <link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme"> |
| 18 | <!-- <link rel="stylesheet" href="plugin/highlight/zenburn.css" id="highlight-theme"> --> | 18 | <!-- <link rel="stylesheet" href="plugin/highlight/zenburn.css" id="highlight-theme"> --> |
| 19 | </head> | 19 | </head> |
| 20 | <body> | 20 | <body> |
| 21 | <div class="reveal"> | 21 | <div class="reveal"> |
| 22 | 22 | ||
| 23 | <!-- Any section element inside of this container is displayed as a slide --> | 23 | <!-- Any section element inside of this container is displayed as a slide --> |
| 24 | <div class="slides"> | 24 | <div class="slides"> |
| 25 | 25 | ||
| 26 | <section> | 26 | <section> |
| 27 | <h2>PostgreSQL tuning</h2> | 27 | <h2>PostgreSQL tuning</h2> |
| 28 | <h4>Data Science @ Beryl</h4> | 28 | <h4>Data Science @ Beryl</h4> |
| 29 | </br> | 29 | </br> |
| 30 | <p><small>Simeon Simeonov</small></p> | 30 | <p><small>Simeon Simeonov</small></p> |
| 31 | </section> | 31 | </section> |
| 32 | 32 | ||
| 33 | <section> | 33 | <section> |
| 34 | 34 | ||
| 35 | <section id="fragments"> | 35 | <section id="fragments"> |
| 36 | <h2>Agenda</h2> | 36 | <h2>Agenda</h2> |
| 37 | </br> | 37 | </br> |
| 38 | <ul> | 38 | <ul> |
| 39 | <span class="fragment"><li>Generic tools for gathering information</li></span> | 39 | <span class="fragment"><li>Generic tools for gathering information</li></span> |
| 40 | <span class="fragment"><li>Memory settings</li></span> | 40 | <span class="fragment"><li>Memory settings</li></span> |
| 41 | <span class="fragment"><li>Logging and performance reports</li></span> | 41 | <span class="fragment"><li>Logging and performance reports</li></span> |
| 42 | <span class="fragment"><li>Other tools for analysis</li></span> | 42 | <span class="fragment"><li>Other tools for analysis</li></span> |
| 43 | </ul> | 43 | </ul> |
| 44 | </section> | 44 | </section> |
| 45 | 45 | ||
| 46 | </section> | 46 | </section> |
| 47 | 47 | ||
| 48 | <section> | 48 | <section> |
| 49 | <h2>General tools for gathering information</h2> | 49 | <h2>General tools for gathering information</h2> |
| 50 | </br> | 50 | </br> |
| 51 | <pre data-id="code-animation"> | 51 | <pre data-id="code-animation"> |
| 52 | <code class="bash" data-trim type="text/template"> | 52 | <code class="bash" data-trim type="text/template"> |
| 53 | # shell | 53 | # shell |
| 54 | # fetch information from the OS | 54 | # fetch information from the OS |
| 55 | cat /proc/cpuinfo | 55 | cat /proc/cpuinfo |
| 56 | cat /proc/meminfo | 56 | cat /proc/meminfo |
| 57 | sysctl -a | grep shm # get kernel parameters of interest | 57 | sysctl -a | grep shm # get kernel parameters of interest |
| 58 | </code> | 58 | </code> |
| 59 | <code class="sql" data-trim type="text/template"> | 59 | <code class="sql" data-trim type="text/template"> |
| 60 | -- SQL | 60 | -- SQL |
| 61 | -- show the current values of all settings | 61 | -- show the current values of all settings |
| 62 | SHOW ALL; | 62 | SHOW ALL; |
| 63 | 63 | ||
| 64 | -- display even more than all... | 64 | -- display even more than all... |
| 65 | SELECT * FROM pg_settings; | 65 | SELECT * FROM pg_settings; |
| 66 | 66 | ||
| 67 | -- opening postgresql.conf and reading the comments - the old school approach | 67 | -- opening postgresql.conf and reading the comments - the old school approach |
| 68 | </code> | 68 | </code> |
| 69 | <code class="bash" data-trim type="text/template"> | 69 | <code class="bash" data-trim type="text/template"> |
| 70 | # old school: edit postgresql.conf and read the comments | 70 | # old school: edit postgresql.conf and read the comments |
| 71 | </code> | 71 | </code> |
| 72 | </pre> | 72 | </pre> |
| 73 | </section> | 73 | </section> |
| 74 | 74 | ||
| 75 | <section> | 75 | <section> |
| 76 | <h2>Memory settings</h2> | 76 | <h2>Memory settings</h2> |
| 77 | </br> | 77 | </br> |
| 78 | <ul> | 78 | <ul> |
| 79 | <li><em>shared_buffers</em> - how much memory is dedicated to PostgreSQL to use for caching data - for a system with 1GB or more of RAM, a reasonable starting value for <em>shared_buffers</em> is 25% of the system memory (128MB -> 1GB)</li> | 79 | <li><em>shared_buffers</em> - how much memory is dedicated to PostgreSQL to use for caching data - for a system with 1GB or more of RAM, a reasonable starting value for <em>shared_buffers</em> is 25% of the system memory (128MB -> 1GB)</li> |
| 80 | <li><em>effective_cache_size</em> - how much memory we expect to be available in the OS and PostgreSQL buffer caches, not an allocation - used only by the PostgreSQL query planner to figure out whether plans it's considering would be expected to fit in RAM or not - 1/2 of total memory would be a normal conservative setting (4GB -> 8GB)</li> | 80 | <li><em>effective_cache_size</em> - how much memory we expect to be available in the OS and PostgreSQL buffer caches, not an allocation - used only by the PostgreSQL query planner to figure out whether plans it's considering would be expected to fit in RAM or not - 1/2 of total memory would be a normal conservative setting (4GB -> 8GB)</li> |
| 81 | <li><em>work_mem</em> - the base maximum amount of memory to be used by a query operation (such as a sort or hash table) before writing to temporary disk files - for a complex query, several sort or hash operations might be running in parallel; each operation will generally be allowed to use as much memory as this value specifies (4MB -> 8MB)</li> | 81 | <li><em>work_mem</em> - the base maximum amount of memory to be used by a query operation (such as a sort or hash table) before writing to temporary disk files - for a complex query, several sort or hash operations might be running in parallel; each operation will generally be allowed to use as much memory as this value specifies (4MB -> 8MB)</li> |
| 82 | <li><em>maintenance_work_mem</em> - the maximum amount of memory to be used by maintenance operations, such as <em>VACUUM</em> and <em>CREATE INDEX</em>. It's safe to set this value significantly larger than <em>work_mem</em> (64MB -> 256MB)</li> | 82 | <li><em>maintenance_work_mem</em> - the maximum amount of memory to be used by maintenance operations, such as <em>VACUUM</em> and <em>CREATE INDEX</em>. It's safe to set this value significantly larger than <em>work_mem</em> (64MB -> 256MB)</li> |
| 83 | </ul> | 83 | </ul> |
| 84 | </section> | 84 | </section> |
| 85 | 85 | ||
| 86 | <section> | 86 | <section> |
| 87 | <h2>Logging and performance reports</h2> | 87 | <h2>Logging and performance reports</h2> |
| 88 | </br> | 88 | </br> |
| 89 | <p><em>pgBadger - a fast PostgreSQL log analysis report</em> can be used for general analysis.</p> | 89 | <p><em>pgBadger - a fast PostgreSQL log analysis report</em> can be used for general analysis.</p> |
| 90 | <ul> | 90 | <ul> |
| 91 | <li><em>log_checkpoints</em> - checkpoints and restartpoints are logged in the server log. Some statistics are included in the log messages, including the number of buffers written and the time spent writing them</li> | 91 | <li><em>log_checkpoints</em> - checkpoints and restartpoints are logged in the server log. Some statistics are included in the log messages, including the number of buffers written and the time spent writing them</li> |
| 92 | <li><em>log_connections</em> - each attempted connection to the server to be logged, as well as successful completion of client authentication</li> | 92 | <li><em>log_connections</em> - each attempted connection to the server to be logged, as well as successful completion of client authentication</li> |
| 93 | <li><em>log_disconnections</em> - provides information similar to <em>log_connections</em>, plus the duration of the session</li> | 93 | <li><em>log_disconnections</em> - provides information similar to <em>log_connections</em>, plus the duration of the session</li> |
| 94 | <li><em>log_line_prefix</em> - set to '%t [%p]: user=%u,db=%d,app=%a,client=%h '</li> | 94 | <li><em>log_line_prefix</em> - set to '%t [%p]: user=%u,db=%d,app=%a,client=%h '</li> |
| 95 | <li><em>log_lock_waits</em> - log message is produced when a session waits longer than deadlock_timeout to acquire a lock. This is useful in determining if lock waits are causing poor performance</li> | 95 | <li><em>log_lock_waits</em> - log message is produced when a session waits longer than deadlock_timeout to acquire a lock. This is useful in determining if lock waits are causing poor performance</li> |
| 96 | <li><em>log_temp_files</em> - when set to <em>0</em>, a log entry is emitted for each temporary file when it is deleted</li> | 96 | <li><em>log_temp_files</em> - when set to <em>0</em>, a log entry is emitted for each temporary file when it is deleted</li> |
| 97 | <li><em>log_autovacuum_min_duration</em> - set to <em>0</em> it logs all autovacuum actions</li> | 97 | <li><em>log_autovacuum_min_duration</em> - set to <em>0</em> it logs all autovacuum actions</li> |
| 98 | </ul> | 98 | </ul> |
| 99 | </section> | 99 | </section> |
| 100 | 100 | ||
| 101 | <section> | 101 | <section> |
| 102 | <h2>Other tools for analysis</h2> | 102 | <h2>Other tools for analysis</h2> |
| 103 | </br> | 103 | </br> |
| 104 | <ul> | 104 | <ul> |
| 105 | <li><em>ANALYZE</em> - collects statistics about the contents of tables in the database, and stores the results in the pg_statistic system catalog. Subsequently, the query planner uses these statistics to help determine the most efficient execution plans for queries.</li> | 105 | <li><em>ANALYZE</em> - collects statistics about the contents of tables in the database, and stores the results in the pg_statistic system catalog. Subsequently, the query planner uses these statistics to help determine the most efficient execution plans for queries.</li> |
| 106 | <li><em>VACUUM</em> - reclaims storage occupied by dead tuples. In normal PostgreSQL operation, tuples that are deleted or obsoleted by an update are not physically removed from their table; they remain present until a VACUUM is done. (<em>VACUUM</em> vs. <em>VACUUM FULL</em>)</li> | 106 | <li><em>VACUUM</em> - reclaims storage occupied by dead tuples. In normal PostgreSQL operation, tuples that are deleted or obsoleted by an update are not physically removed from their table; they remain present until a VACUUM is done. (<em>VACUUM</em> vs. <em>VACUUM FULL</em>)</li> |
| 107 | </ul> | 107 | </ul> |
| 108 | </section> | 108 | </section> |
| 109 | 109 | ||
| 110 | <section> | 110 | <section> |
| 111 | <h2>Sources</h2> | 111 | <h2>Sources</h2> |
| 112 | </br> | 112 | </br> |
| 113 | <p> | 113 | <p> |
| 114 | <a href="https://www.postgresql.org/docs/">https://www.postgresql.org/docs/</a> - The official documentation | 114 | <a href="https://www.postgresql.org/docs/">https://www.postgresql.org/docs/</a> - The official documentation |
| 115 | </p> | 115 | </p> |
| 116 | <p> | 116 | <p> |
| 117 | <a href="https://wiki.postgresql.org">https://wiki.postgresql.org</a> - The official Wiki | 117 | <a href="https://wiki.postgresql.org">https://wiki.postgresql.org</a> - The official Wiki |
| 118 | </p> | 118 | </p> |
| 119 | </section> | 119 | </section> |
| 120 | 120 | ||
| 121 | <section> | 121 | <section> |
| 122 | <h1>Q & A</h1> | 122 | <h1>Q & A</h1> |
| 123 | </section> | 123 | </section> |
| 124 | 124 | ||
| 125 | </div> | 125 | </div> |
| 126 | </div> | 126 | </div> |
| 127 | |||
| 128 | <script src="dist/reveal.js"></script> | ||
| 129 | <script src="plugin/zoom/zoom.js"></script> | ||
| 130 | <script src="plugin/notes/notes.js"></script> | ||
| 131 | <script src="plugin/search/search.js"></script> | ||
| 132 | <script src="plugin/markdown/markdown.js"></script> | ||
| 133 | <script src="plugin/highlight/highlight.js"></script> | ||
| 134 | <script> | ||
| 135 | 127 | ||
| 136 | // Also available as an ES module, see: | 128 | <script src="dist/reveal.js"></script> |
| 137 | // https://revealjs.netlify.app/initialization/ | 129 | <script src="plugin/zoom/zoom.js"></script> |
| 138 | Reveal.initialize({ | 130 | <script src="plugin/notes/notes.js"></script> |
| 139 | controls: true, | 131 | <script src="plugin/search/search.js"></script> |
| 140 | progress: true, | 132 | <script src="plugin/markdown/markdown.js"></script> |
| 141 | center: true, | 133 | <script src="plugin/highlight/highlight.js"></script> |
| 142 | hash: true, | 134 | <script> |
| 143 | 135 | ||
| 144 | // Learn about plugins: https://revealjs.netlify.app/plugins/ | 136 | // Also available as an ES module, see: |
| 145 | plugins: [ RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight ] | 137 | // https://revealjs.com/initialization/ |
| 146 | }); | 138 | Reveal.initialize({ |
| 147 | Reveal.configure({ pdfSeparateFragments: false }); | 139 | controls: true, |
| 140 | progress: true, | ||
| 141 | center: true, | ||
| 142 | hash: true, | ||
| 148 | 143 | ||
| 149 | </script> | 144 | // Learn about plugins: https://revealjs.com/plugins/ |
| 145 | plugins: [ RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight ] | ||
| 146 | }); | ||
| 150 | 147 | ||
| 151 | </body> | 148 | </script> |
| 149 | |||
| 150 | </body> | ||
| 152 | </html> | 151 | </html> |
diff --git a/reveal.js/python-oo.html b/reveal.js/python-oo.html deleted file mode 100644 index 5279993..0000000 --- a/reveal.js/python-oo.html +++ /dev/null | |||
| @@ -1,145 +0,0 @@ | |||
| 1 | <!doctype html> | ||
| 2 | <html lang="en"> | ||
| 3 | <head> | ||
| 4 | <meta charset="utf-8"> | ||
| 5 | <title>Object-oriented programming in Python</title> | ||
| 6 | <meta name="author" content="Simeon Simeonov"> | ||
| 7 | <meta name="apple-mobile-web-app-capable" content="yes"> | ||
| 8 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | ||
| 9 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| 10 | |||
| 11 | <link rel="stylesheet" href="dist/reset.css"> | ||
| 12 | <link rel="stylesheet" href="dist/reveal.css"> | ||
| 13 | |||
| 14 | <link rel="stylesheet" href="dist/theme/statnett.css" id="theme"> | ||
| 15 | |||
| 16 | <!-- Theme used for syntax highlighting of code --> | ||
| 17 | <link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme"> | ||
| 18 | <!-- <link rel="stylesheet" href="plugin/highlight/zenburn.css" id="highlight-theme"> --> | ||
| 19 | </head> | ||
| 20 | <body> | ||
| 21 | <div class="reveal"> | ||
| 22 | |||
| 23 | <!-- Any section element inside of this container is displayed as a slide --> | ||
| 24 | <div class="slides"> | ||
| 25 | |||
| 26 | <section> | ||
| 27 | <h2>Object-oriented programming in Python</h2> | ||
| 28 | <h4>Data Engineering @ Statnett</h4> | ||
| 29 | </br> | ||
| 30 | <p><small>Simeon Simeonov</small></p> | ||
| 31 | </section> | ||
| 32 | |||
| 33 | <section> | ||
| 34 | <h2>Goals</h2> | ||
| 35 | </br> | ||
| 36 | <ul> | ||
| 37 | <li><p>Present the Python programming language in a different way than <a href="https://docs.python.org">https://docs.python.org</a></p></li> | ||
| 38 | <li><p>Avoid information overload</p></li> | ||
| 39 | <li><p>Use examples and interaction rather than documents and slides</p></li> | ||
| 40 | </ul> | ||
| 41 | </section> | ||
| 42 | |||
| 43 | <section> | ||
| 44 | <h2>Preliminary plan</h2> | ||
| 45 | </br> | ||
| 46 | <ul> | ||
| 47 | <li><p>Basics: About the language, the Python eco-system, types, modules, functions, scopes, decorators, string formatting</p></li> | ||
| 48 | <li><p><b>Object-oriented programming in Python: How Python "really works"</b></p></li> | ||
| 49 | <li><p>Control flow: if / for / while / try, iterators, "tactical programming" tips</p></li> | ||
| 50 | <li><p>A brief tour through Python's standard library</p></li> | ||
| 51 | <li><p>Code design and best practices: How to design your code</p></li> | ||
| 52 | </ul> | ||
| 53 | </section> | ||
| 54 | |||
| 55 | <section> | ||
| 56 | <h2>What is object-oriented programming?</h2> | ||
| 57 | </br> | ||
| 58 | <p>Object-oriented programming does <b>not</b> mean using an object-oriented programming language.</p> | ||
| 59 | <p>It is rather a programming paradigm based on the concept of <em>object</em>, as well as on some general principles and best practices aiming at:</p> | ||
| 60 | <ul> | ||
| 61 | <li><p>improving readability</p></li> | ||
| 62 | <li><p>improving re-usability</p></li> | ||
| 63 | <li><p>improving modularity</p></li> | ||
| 64 | <li><p>providing foundation for a more intuitive design</p></li> | ||
| 65 | </ul> | ||
| 66 | </section> | ||
| 67 | |||
| 68 | <section> | ||
| 69 | <h2>General principles</h2> | ||
| 70 | </br> | ||
| 71 | <p>The following four concepts / principles are presented in most object-oriented programming books:</p> | ||
| 72 | <ul> | ||
| 73 | <li><p>separate and hide "private" details from the outside world and / or child (inheriting) functionality (encapsulation)</p></li> | ||
| 74 | <li><p>separate the interface from its implementation (abstraction)</p></li> | ||
| 75 | <li><p>inherit and extend / adapt existing functionality, through "is-a" relationship hierarchy (inheritance)</p></li> | ||
| 76 | <li><p>execute different code / functionality based on the object's place in the hierarchy (polymorphism)</p></li> | ||
| 77 | </ul> | ||
| 78 | </section> | ||
| 79 | |||
| 80 | <section> | ||
| 81 | <h2>Building blocks and definitions</h2> | ||
| 82 | </br> | ||
| 83 | <p><b>Note:</b> "Lacking universally accepted terminology to talk about classes, I will make occasional use of Smalltalk and C++ terms." - The Python tutorial (https://docs.python.org)</p> | ||
| 84 | <p>When talking about object-oriented programming, the following building blocks are involved:</p> | ||
| 85 | <ul> | ||
| 86 | <li><p>class - a blueprint / template for creating objects</p></li> | ||
| 87 | <li><p>object - an instance of a class that may contain its own attributes as well as references to its class' attributes</p></li> | ||
| 88 | <li><p>attribute - variable, property, function defined in the class and present in its instances</p></li> | ||
| 89 | <li><p>class variable - attribute of which a single copy exists, regardless of how many instances of the class exist</p></li> | ||
| 90 | <li><p>object / instance variable - attribute for which each instantiated object of the class has a separate copy, or instance</p></li> | ||
| 91 | <li><p>method - member function - function that is an attribute</p></li> | ||
| 92 | </ul> | ||
| 93 | </section> | ||
| 94 | |||
| 95 | <section> | ||
| 96 | <h2>The object-oriented world of Python</h2> | ||
| 97 | </br> | ||
| 98 | <p>Some additional concepts and definitions are introduced in Python, as well as in some other programming languages:</p> | ||
| 99 | <ul> | ||
| 100 | <li><p>metaclass - a class whose instances are classes</p></li> | ||
| 101 | <li><p>property - attribute that provides a flexible mechanism to read, write, or compute the value of a "private" attribute</p></li> | ||
| 102 | <li><p>type - old C object-oriented term, now (in Python 3) considered to be the same as class</p></li> | ||
| 103 | </ul> | ||
| 104 | <p> As is true for modules, classes partake of the dynamic nature of Python: they are created at runtime, and can be modified further after creation.</p> | ||
| 105 | </section> | ||
| 106 | |||
| 107 | <section> | ||
| 108 | <h2>The object-oriented world of Python (cont...)</h2> | ||
| 109 | </br> | ||
| 110 | <p>The < </p> | ||
| 111 | <ul> | ||
| 112 | <li><p>metaclass - a class whose instances are classes</p></li> | ||
| 113 | <li><p>property - attribute that provides a flexible mechanism to read, write, or compute the value of a "private" attribute</p></li> | ||
| 114 | <li><p>type - old C object-oriented term, now (in Python 3) considered to be the same as class</p></li> | ||
| 115 | </ul> | ||
| 116 | </section> | ||
| 117 | |||
| 118 | </div> | ||
| 119 | </div> | ||
| 120 | |||
| 121 | <script src="dist/reveal.js"></script> | ||
| 122 | <script src="plugin/zoom/zoom.js"></script> | ||
| 123 | <script src="plugin/notes/notes.js"></script> | ||
| 124 | <script src="plugin/search/search.js"></script> | ||
| 125 | <script src="plugin/markdown/markdown.js"></script> | ||
| 126 | <script src="plugin/highlight/highlight.js"></script> | ||
| 127 | <script> | ||
| 128 | |||
| 129 | // Also available as an ES module, see: | ||
| 130 | // https://revealjs.netlify.app/initialization/ | ||
| 131 | Reveal.initialize({ | ||
| 132 | controls: true, | ||
| 133 | progress: true, | ||
| 134 | center: true, | ||
| 135 | hash: true, | ||
| 136 | |||
| 137 | // Learn about plugins: https://revealjs.netlify.app/plugins/ | ||
| 138 | plugins: [ RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight ] | ||
| 139 | }); | ||
| 140 | Reveal.configure({ pdfSeparateFragments: false }); | ||
| 141 | |||
| 142 | </script> | ||
| 143 | |||
| 144 | </body> | ||
| 145 | </html> | ||
diff --git a/reveal.js/python.html b/reveal.js/python.html deleted file mode 100644 index d27cd49..0000000 --- a/reveal.js/python.html +++ /dev/null | |||
| @@ -1,680 +0,0 @@ | |||
| 1 | <!doctype html> | ||
| 2 | <html lang="en"> | ||
| 3 | <head> | ||
| 4 | <meta charset="utf-8"> | ||
| 5 | <title>Introduction to Python</title> | ||
| 6 | <meta name="author" content="Simeon Simeonov"> | ||
| 7 | <meta name="apple-mobile-web-app-capable" content="yes"> | ||
| 8 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | ||
| 9 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| 10 | |||
| 11 | <link rel="stylesheet" href="dist/reset.css"> | ||
| 12 | <link rel="stylesheet" href="dist/reveal.css"> | ||
| 13 | |||
| 14 | <link rel="stylesheet" href="dist/theme/statnett.css" id="theme"> | ||
| 15 | |||
| 16 | <!-- Theme used for syntax highlighting of code --> | ||
| 17 | <link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme"> | ||
| 18 | <!-- <link rel="stylesheet" href="plugin/highlight/zenburn.css" id="highlight-theme"> --> | ||
| 19 | </head> | ||
| 20 | <body> | ||
| 21 | <div class="reveal"> | ||
| 22 | |||
| 23 | <!-- Any section element inside of this container is displayed as a slide --> | ||
| 24 | <div class="slides"> | ||
| 25 | |||
| 26 | <section> | ||
| 27 | <h2>Introduction to Python</h2> | ||
| 28 | <h4>Data Engineering @ Statnett</h4> | ||
| 29 | </br> | ||
| 30 | <p><small>Simeon Simeonov</small></p> | ||
| 31 | </section> | ||
| 32 | |||
| 33 | <section> | ||
| 34 | <h2>Goals</h2> | ||
| 35 | </br> | ||
| 36 | <ul> | ||
| 37 | <li><p>Present the Python programming language in a different way than <a href="https://docs.python.org">https://docs.python.org</a></p></li> | ||
| 38 | <li><p>Avoid information overload</p></li> | ||
| 39 | <li><p>Use examples and interaction rather than documents and slides</p></li> | ||
| 40 | </ul> | ||
| 41 | </section> | ||
| 42 | |||
| 43 | <section> | ||
| 44 | <h2>Preliminary plan</h2> | ||
| 45 | </br> | ||
| 46 | <ul> | ||
| 47 | <li><p><b>Basics: About the language, the Python eco-system, types, modules, functions, scopes, decorators, string formatting</b></p></li> | ||
| 48 | <li><p>Object-oriented programming in Python: How Python "really works"</p></li> | ||
| 49 | <li><p>Control flow: if / for / while / try, iterators, "tactical programming" tips</p></li> | ||
| 50 | <li><p>A brief tour through Python's standard library</p></li> | ||
| 51 | <li><p>Code design and best practices: How to design your code</p></li> | ||
| 52 | </ul> | ||
| 53 | </section> | ||
| 54 | |||
| 55 | <section> | ||
| 56 | <h2>What is Python?</h2> | ||
| 57 | </br> | ||
| 58 | <ul> | ||
| 59 | <li><p>Python is an interpreted high-level general-purpose programming language - advanced through the Python Enhancement Proposal (PEP) process</p></li> | ||
| 60 | <li><p>CPython is the reference implementation of Python, written in C (alternatives: pypy, jython)</p></li> | ||
| 61 | <li><p>python - interpreter and interpreter shell (alternatives: ipython, bpython)</p></li> | ||
| 62 | <li><p>libpython</p></li> | ||
| 63 | <li><p>Calling C from Python: Cython, CFFI, ctypes</p></li> | ||
| 64 | </ul> | ||
| 65 | </section> | ||
| 66 | |||
| 67 | <section> | ||
| 68 | <h2>Philosophy</h2> | ||
| 69 | </br> | ||
| 70 | <pre data-id="code-animation"> | ||
| 71 | <code class="bash" data-trim type="text/template"> | ||
| 72 | $ python | ||
| 73 | </code> | ||
| 74 | </pre> | ||
| 75 | <pre data-id="code-animation"> | ||
| 76 | <code class="python" data-trim type="text/template"> | ||
| 77 | import this | ||
| 78 | |||
| 79 | # The Zen of Python, by Tim Peters | ||
| 80 | |||
| 81 | # Beautiful is better than ugly. | ||
| 82 | # Explicit is better than implicit. | ||
| 83 | # Simple is better than complex. | ||
| 84 | # Complex is better than complicated. | ||
| 85 | # Flat is better than nested. | ||
| 86 | # Sparse is better than dense. | ||
| 87 | # Readability counts. | ||
| 88 | # Special cases aren't special enough to break the rules. | ||
| 89 | # Although practicality beats purity. | ||
| 90 | # Errors should never pass silently. | ||
| 91 | # Unless explicitly silenced. | ||
| 92 | # In the face of ambiguity, refuse the temptation to guess. | ||
| 93 | # There should be one-- and preferably only one --obvious way to do it. | ||
| 94 | # Although that way may not be obvious at first unless you're Dutch. | ||
| 95 | # Now is better than never. | ||
| 96 | # Although never is often better than *right* now. | ||
| 97 | # If the implementation is hard to explain, it's a bad idea. | ||
| 98 | # If the implementation is easy to explain, it may be a good idea. | ||
| 99 | # Namespaces are one honking great idea -- let's do more of those! | ||
| 100 | </code> | ||
| 101 | </pre> | ||
| 102 | </section> | ||
| 103 | |||
| 104 | <section> | ||
| 105 | <h2>Built-in functions</h2> | ||
| 106 | </br> | ||
| 107 | <p>Few built-in functions.</p> | ||
| 108 | <p><a href="https://docs.python.org/3/library/functions.html">https://docs.python.org/3/library/functions.html</a></p> | ||
| 109 | <ul> | ||
| 110 | <li><em>dir([obj])</em> - returns a list of valid attributes for that object</li> | ||
| 111 | <li><em>id(obj)</em> - returns the "identity" of an object - an integer which is guaranteed to be unique</li> | ||
| 112 | <li><em>print(...)</em> - prints objects to a text stream</li> | ||
| 113 | <li><em>str(...)</em> - returns a string version of object</li> | ||
| 114 | <li><em>type(obj)</em> - returns the type of an object</li> | ||
| 115 | </ul> | ||
| 116 | </section> | ||
| 117 | |||
| 118 | <section> | ||
| 119 | <h2>Common built-in types</h2> | ||
| 120 | </br> | ||
| 121 | <p>Python uses <a href="https://en.wikipedia.org/wiki/Duck_typing"><em>duck typing</em></a> and has typed objects but untyped variable names.</p> | ||
| 122 | <p>Type constraints are not checked at compile time; rather, operations on an object may fail, signifying that the given object is not of a suitable type. Despite being dynamically-typed, Python is strongly-typed, forbidding operations that are not well-defined (for example, adding a number to a string) rather than silently attempting to make sense of them.</p> | ||
| 123 | <pre data-id="code-animation"> | ||
| 124 | <code class="python" data-trim type="text/template"> | ||
| 125 | s = 'foo' # this is a string / str, same as str('foo'), may be encoded, immutable | ||
| 126 | b = b'foo' # bytes, same as bytes('foo', 'utf-8'), may be decoded, immutable | ||
| 127 | i = 6 # int, same as int('6'), immutable | ||
| 128 | f = 0.1 # float, same as float('0.1'), immutable | ||
| 129 | b = False # bool, same as bool(0), bool(''), bool(None)... immutable / constant | ||
| 130 | n = None # NoneType, similar to 'null' in other languages, immutable / constant | ||
| 131 | l = [1, False, 'foo'] # list, same as list((1, False, 'foo')) | ||
| 132 | t = (1, False, 'foo') # tuple, same as tuple([1, False, 'foo']), immutable | ||
| 133 | d = {'foo': 1, 'bar': 8} # dict, same as dict(foo=1, bar=8), similar to hash in other languages | ||
| 134 | s = {'foo', 'bar', 1, 1, 4} # set, same as set(['foo', 'bar', 1, 1, 4]), removes duplicates | ||
| 135 | </code> | ||
| 136 | </pre> | ||
| 137 | <p>Classes, functions, modules, .... and even types are simply other types :)</p> | ||
| 138 | </section> | ||
| 139 | |||
| 140 | <section> | ||
| 141 | <h2>Modules</h2> | ||
| 142 | </br> | ||
| 143 | <p>A module is a file containing Python definitions and statements. The file name is the module name with the suffix <em>.py</em> appended. Within a module, the module's name (as a string) is available as the value of the global variable <em>__name__</em></p> | ||
| 144 | <p>When a module named <em>foo</em> is imported, the interpreter first searches for a built-in module with that name (<em>sys.builtin_module_names</em>). If not found, it then searches for a file named <em>foo.py</em> in a list of directories given by the variable <em>sys.path</em>. <em>sys.path</em> is initialized from these locations:</p> | ||
| 145 | <ul> | ||
| 146 | <li>the directory containing the input script (or the current directory when no file is specified)</li> | ||
| 147 | <li><em>PYTHONPATH</em> - env. variable - a list of directory names</li> | ||
| 148 | <li>the installation-dependent default locations</li> | ||
| 149 | </ul> | ||
| 150 | |||
| 151 | <p>The module is then imported only once and "cached" in <em>sys.modules</em></p> | ||
| 152 | </section> | ||
| 153 | |||
| 154 | <section> | ||
| 155 | <h2>Packages</h2> | ||
| 156 | </br> | ||
| 157 | <p><a href="https://docs.python.org/3/tutorial/modules.html#packages">Packages</a> are a way of structuring Python's module namespace by using "dotted module names"</p> | ||
| 158 | <p>The import statement combines two operations:</p> | ||
| 159 | <ul> | ||
| 160 | <li>it searches for the named module</li> | ||
| 161 | <li>it binds the results of that search to a name in the <em>local scope</em></li> | ||
| 162 | </ul> | ||
| 163 | <pre data-id="code-animation"> | ||
| 164 | <code class="python" data-trim type="text/template"> | ||
| 165 | # bar.py then bar/__init__.py will be considered, the first match executed and bound to 'bar' | ||
| 166 | import bar | ||
| 167 | |||
| 168 | import mymodule.foo # implicitly executes mymodule.py, mymodule/__init__.py and mymodule/foo/__init__.py | ||
| 169 | |||
| 170 | import numpy as np # will be bound as 'np' instead of 'numpy'. N.B. __name__ is still 'numpy' | ||
| 171 | |||
| 172 | import some.extremely.deep.path.Animal as Animal # "sacrifice" the namespace in the name of convinience | ||
| 173 | |||
| 174 | from sys import path # execute sys and only import the 'path' attribute into local scope as 'path' | ||
| 175 | |||
| 176 | # relative imports must be explicit in Python 3 | ||
| 177 | from .othermodule import something # expects that current module and 'othermodule' are in the same | ||
| 178 | # package (containing __init__.py) | ||
| 179 | |||
| 180 | from sys import * # NO! Bad programming practice since 1879 | ||
| 181 | </code> | ||
| 182 | </pre> | ||
| 183 | </section> | ||
| 184 | |||
| 185 | <section> | ||
| 186 | <h2>Creating and maintaining a Python environment</h2> | ||
| 187 | </br> | ||
| 188 | <p>Python's official package repository is <a href="https://pypi.org"><em>PyPi (https://pypi.org)</em></a>, while Python's official package installer is <a href="https://pypi.org/project/pip/"><em>pip (https://pypi.org/project/pip/)</em></a></p> | ||
| 189 | <p>A Python environment is the physical and logical arrangement of Python modules and packages. Several options exist:</p> | ||
| 190 | <ul> | ||
| 191 | <li>using a proper operating system :) (symlinks, real commercial support etc.)</li> | ||
| 192 | <li>using <em>venv</em></li> | ||
| 193 | <li>using higher level tools like <em>poetry</em></li> | ||
| 194 | <li>using a mixture / cocktail of all of the above :)</li> | ||
| 195 | </ul> | ||
| 196 | </section> | ||
| 197 | |||
| 198 | <section> | ||
| 199 | <h2>Creating and maintaining a Python environment (cont...)</h2> | ||
| 200 | </br> | ||
| 201 | <p>Desired qualities for a flexible Python environment:</p> | ||
| 202 | <ul> | ||
| 203 | <li>easy to create and (un)load</li> | ||
| 204 | <li>do not require extra privileges</li> | ||
| 205 | <li>don't repeat yourself (DRY)</li> | ||
| 206 | <li>easy to update without breaking the API</li> | ||
| 207 | <li>easy to debug</li> | ||
| 208 | <li>play nicely with the VCS (git)</li> | ||
| 209 | </ul> | ||
| 210 | </section> | ||
| 211 | |||
| 212 | <section> | ||
| 213 | <h3>Creating and maintaining a Python environment (cont...)</h3> | ||
| 214 | </br> | ||
| 215 | <p>Exploting the operating system can be done by:</p> | ||
| 216 | <ul> | ||
| 217 | <li>(re)defining <em>PYTHONPATH</em></li> | ||
| 218 | <li>using symlinks to point at packages placed at different locations</li> | ||
| 219 | </section> | ||
| 220 | |||
| 221 | <section> | ||
| 222 | <h3>Creating and maintaining a Python environment (cont...)</h3> | ||
| 223 | </br> | ||
| 224 | <p>Using <em>venv</em> can be done by directly invoking <em>python</em>:</p> | ||
| 225 | <pre data-id="code-animation"> | ||
| 226 | <code class="bash" data-trim type="text/template"> | ||
| 227 | # create a virtual environment | ||
| 228 | python -m venv my_virtual_env | ||
| 229 | python -m venv --system-site-packages my_virtual_env | ||
| 230 | |||
| 231 | # load, use and unload the virtual environment | ||
| 232 | source my_virtual_env/bin/activate | ||
| 233 | pip install sqlalchemy | ||
| 234 | # install package from a custom repository (https://artifactory.fifty.eu) | ||
| 235 | pip install --index-url=https://artifactory.fifty.eu/artifactory/api/pypi/pypi/simple/ odin-data-access | ||
| 236 | deactivate | ||
| 237 | |||
| 238 | # one can alternatively use the python "wrapper" of the virtual env | ||
| 239 | my_virtual_env/bin/python -m pip install sqlalchemy | ||
| 240 | </code> | ||
| 241 | </pre> | ||
| 242 | <p><em>--system-site-packages</em> will keep the original <em>site-packages</em> folders at the end of <em>sys.path</em></p> | ||
| 243 | </section> | ||
| 244 | |||
| 245 | <section> | ||
| 246 | <h3>Creating and maintaining a Python environment (cont...)</h3> | ||
| 247 | </br> | ||
| 248 | <p><em>Poetry</em> (<a href="https://python-poetry.org"><em>https://python-poetry.org</em></a>) is the prefered environment and dependency management tool at Statnett.</p> | ||
| 249 | <pre data-id="code-animation"> | ||
| 250 | <code class="bash" data-trim type="text/template"> | ||
| 251 | # create project and a virtual environment from scratch | ||
| 252 | poetry new my-project | ||
| 253 | |||
| 254 | # ... or use Poetry with an existing one | ||
| 255 | cd my-project | ||
| 256 | poetry init | ||
| 257 | |||
| 258 | # edit pyproject.toml for your needs (f.i. add dependencies, metadata ... etc), | ||
| 259 | # create virtual environment and install dependencies | ||
| 260 | poetry install | ||
| 261 | # finally commit your poetry.lock file to version control | ||
| 262 | |||
| 263 | # update all dependencies | ||
| 264 | poetry update | ||
| 265 | </code> | ||
| 266 | </pre> | ||
| 267 | <p>For more info: <a href="https://python-poetry.org/docs/basic-usage/"><em>https://python-poetry.org/docs/basic-usage/</em></a></p> | ||
| 268 | </section> | ||
| 269 | |||
| 270 | <section> | ||
| 271 | <h2>Mutables vs. immutables</h2> | ||
| 272 | <p>Immutable object is an object with a fixed value. Immutable objects include <em>bool, int, float, str, bytes</em> and <em>tuples</em>. Such an object cannot be altered. A new object has to be created if a different value has to be stored. They play an important role in places where a constant hash value is needed, for example as a key in a dictionary.</p> | ||
| 273 | <p>All objects that are not immutable are... mutable. All <em>hashable objects</em> <b>should</b> be immutable or use <em>id()</em>.</p> | ||
| 274 | <pre data-id="code-animation"> | ||
| 275 | <code class="python" data-trim type="text/template"> | ||
| 276 | i = 1 | ||
| 277 | id(i) # returns f.i. 9788992 | ||
| 278 | i += 1 # same as i = i + 1 | ||
| 279 | id(i) # returns a different value, hence - a brand new object | ||
| 280 | |||
| 281 | s = 'Hello' | ||
| 282 | s += ' World' # s is now a different object | ||
| 283 | s[0] # 'H' | ||
| 284 | s[0] = 'h' # TypeError: 'str' object does not support item assignment | ||
| 285 | |||
| 286 | t = (1, 4) # tuple | ||
| 287 | l = [1, 4] # list | ||
| 288 | hash(t) # returns f.i. -6333845781340707986 | ||
| 289 | hash(l) # TypeError: unhashable type: 'list' | ||
| 290 | |||
| 291 | s = 'the long and winding road' | ||
| 292 | s2 = 'the long and winding road' | ||
| 293 | |||
| 294 | # check if s and s2 are the same object: | ||
| 295 | id(s) # Out: 139858905258704 | ||
| 296 | id(s2) # Out: 139858926037472 | ||
| 297 | |||
| 298 | # the hash should be the same | ||
| 299 | hash(s) # Out: 7030216208569256362 | ||
| 300 | hash(s2) # Out: 7030216208569256362 | ||
| 301 | </code> | ||
| 302 | </pre> | ||
| 303 | </section> | ||
| 304 | |||
| 305 | <section> | ||
| 306 | <h2>Functions</h2> | ||
| 307 | </br> | ||
| 308 | <p>A function is a sequence of program instructions that performs a specific task, packaged as a unit.</p> | ||
| 309 | <p>Functions let you:</p> | ||
| 310 | <ul> | ||
| 311 | <li>reuse code across several programs / projects</li> | ||
| 312 | <li>minimize code duplication</li> | ||
| 313 | <li>devide larger programming tasks</li> | ||
| 314 | <li>hide implementation details</li> | ||
| 315 | <li>improve readability</li> | ||
| 316 | <li>improve traceability</li> | ||
| 317 | </ul> | ||
| 318 | <p>Function calls bring some overhead pushing / popping function-data into / from stack.</p> | ||
| 319 | <p>Important definitions (may have different meanings in different programming languages):</p> | ||
| 320 | <ul> | ||
| 321 | <li><em>parameter / formal parameter</em> - variable / data provided as input to the function</li> | ||
| 322 | <li><em>argument / actual parameter</em> - local variable / data to the given function</li> | ||
| 323 | </ul> | ||
| 324 | <p>The keyword <em>def</em> introduces a function definition.</p> | ||
| 325 | </section> | ||
| 326 | |||
| 327 | <section> | ||
| 328 | <h2>Functions (cont...)</h2> | ||
| 329 | </br> | ||
| 330 | <pre data-id="code-animation"> | ||
| 331 | <code class="python" data-trim type="text/template"> | ||
| 332 | def add(a, b): # - function definition / header | ||
| 333 | """Function for adding integers""" # - docstring | ||
| 334 | result = a + b | ||
| 335 | a = 5 | ||
| 336 | return result # - function that does not contain return, implicitly returns None | ||
| 337 | |||
| 338 | a_param = 9 | ||
| 339 | b_param = -2 | ||
| 340 | add(a_param, b_param) # Out: 7 | ||
| 341 | |||
| 342 | # integers are immutable and a_param will remain unchanged | ||
| 343 | print(a_param) # Out: 9 | ||
| 344 | |||
| 345 | |||
| 346 | def addl(a, b): | ||
| 347 | """Function for adding two lists""" | ||
| 348 | result = a + b | ||
| 349 | a += [5] # in this case equal to: a.append(5) | ||
| 350 | return result | ||
| 351 | |||
| 352 | a_param = [9] | ||
| 353 | b_param = [-2] | ||
| 354 | addl(a_param, b_param) # Out: [9, -2] | ||
| 355 | # lists are mutable and a_param will be changed | ||
| 356 | print(a_param) # Out: [9, 5] | ||
| 357 | </code> | ||
| 358 | </pre> | ||
| 359 | </section> | ||
| 360 | |||
| 361 | <section> | ||
| 362 | <h2>Functions (cont ...)</h2> | ||
| 363 | </br> | ||
| 364 | <h3>Parameters and arguments</h3> | ||
| 365 | <pre data-id="code-animation"> | ||
| 366 | <code class="python" data-trim type="text/template"> | ||
| 367 | def add(a, b): | ||
| 368 | """Function for adding integers""" | ||
| 369 | return a + b | ||
| 370 | my_result = add(2, 5) # positional arguments (parameters) | ||
| 371 | my_result = add(b=5, a=2) # keyword arguments (parameters) | ||
| 372 | my_tuple = (2, 5) | ||
| 373 | my_dict = {'b': 5, 'a': 2} | ||
| 374 | my_result = add(*my_tuple) # unpacked and assigned to the positional arguments | ||
| 375 | my_result = add(**my_dict) # unpacked and assigned to the kw. arguments | ||
| 376 | |||
| 377 | def add(a, b=5): | ||
| 378 | """Function for adding integers""" | ||
| 379 | return a + b | ||
| 380 | my_result = add(2) | ||
| 381 | # ... and the rest of the examples above will work | ||
| 382 | |||
| 383 | def add(a, *args, **kwargs): | ||
| 384 | """Function for adding integers""" | ||
| 385 | if args: | ||
| 386 | b = args[0] | ||
| 387 | elif 'b' in kwargs: | ||
| 388 | b = kwargs['b'] | ||
| 389 | return a + b | ||
| 390 | my_result = add(2, 5, 9, 11) # 5 assigned to args[0] | ||
| 391 | my_result = add(2, b=5, c=9, d=11) # 5 assigned to kwargs['b'] | ||
| 392 | my_result = add(2, b=5, 9, 11) # SyntaxError: positional argument follows keyword argument | ||
| 393 | </code> | ||
| 394 | </pre> | ||
| 395 | </section> | ||
| 396 | |||
| 397 | <section> | ||
| 398 | <h2>Functions (cont ...)</h2> | ||
| 399 | </br> | ||
| 400 | <h3>Docstrings annotations and other hints</h3> | ||
| 401 | <pre data-id="code-animation"> | ||
| 402 | <code class="python" data-trim type="text/template"> | ||
| 403 | def decrypt(password: str, edata: str) -> str: | ||
| 404 | """ | ||
| 405 | Decrypts `edata` using `password`. | ||
| 406 | |||
| 407 | `edata` is in the following format: | ||
| 408 | enc-val$`version-num`$`bas64-salt`$`base64-encrypted_data` | ||
| 409 | |||
| 410 | :param password: The password to generate the key with | ||
| 411 | :type password: str | ||
| 412 | |||
| 413 | :param edata: The data to be decrypted | ||
| 414 | :type edata: str | ||
| 415 | |||
| 416 | :raises EtoolkitInstanceError: If the encryption format is unsupported | ||
| 417 | |||
| 418 | :return: The output string / decrypted data | ||
| 419 | :rtype: str | ||
| 420 | """ | ||
| 421 | if not edata.startswith('enc-val$1$'): | ||
| 422 | raise EtoolkitInstanceError('Unsupported encryption format') | ||
| 423 | # some more code magic coming after.... | ||
| 424 | # ... | ||
| 425 | # .. | ||
| 426 | return decrypted_str | ||
| 427 | </code> | ||
| 428 | </pre> | ||
| 429 | </section> | ||
| 430 | |||
| 431 | <section> | ||
| 432 | <h2>Functions (cont ...)</h2> | ||
| 433 | </br> | ||
| 434 | <h3>Using <em>typing</em> for more advanced / flexible hinting</h3> | ||
| 435 | <pre data-id="code-animation"> | ||
| 436 | <code class="python" data-trim type="text/template"> | ||
| 437 | import typing | ||
| 438 | |||
| 439 | Basestring = typing.Union[str, bytes] | ||
| 440 | |||
| 441 | def decrypt(password: Basestring, edata: str) -> str: | ||
| 442 | pass | ||
| 443 | |||
| 444 | |||
| 445 | from typing import Union | ||
| 446 | |||
| 447 | def decrypt(password: Union[str, bytes], edata: str) -> str: | ||
| 448 | """Generic documentation. No need for pass""" | ||
| 449 | |||
| 450 | # Python >= 3.10 only | ||
| 451 | def decrypt(password: str | bytes, edata: str) -> str: | ||
| 452 | """Generic documentation. No need for pass""" | ||
| 453 | </code> | ||
| 454 | </pre> | ||
| 455 | </section> | ||
| 456 | |||
| 457 | <section> | ||
| 458 | <h2>Functions (cont ...)</h2> | ||
| 459 | <h4>Functions as parameters / arguments, lambdas and returning multiple values</h4> | ||
| 460 | <pre data-id="code-animation"> | ||
| 461 | <code class="python" data-trim type="text/template"> | ||
| 462 | def fetch_the_first_letter(input_str: str) -> str: | ||
| 463 | """Fetches the first letter of the string input_str or 'x'"""" | ||
| 464 | try: | ||
| 465 | return input_str[0] | ||
| 466 | except Exception: | ||
| 467 | return 'x' | ||
| 468 | letter_list = list(map(fetch_the_first_letter, ['foo', 'bar', 'test'])) # Out: ['f', 'b', 't'] | ||
| 469 | </code> | ||
| 470 | </pre> | ||
| 471 | <p>Small anonymous functions can be created with the <em>lambda</em> keyword</p> | ||
| 472 | <pre data-id="code-animation"> | ||
| 473 | <code class="python" data-trim type="text/template"> | ||
| 474 | letter_list = list(map(lambda x: x[0], ['foo', 'bar', 'test'])) # Out: ['f', 'b', 't'] | ||
| 475 | </code> | ||
| 476 | </pre> | ||
| 477 | <p>Functions in Python are <em>callable objects</em>. Callable objects can be created by defining the <em>__call__</em> method. More on that later in the course...</p> | ||
| 478 | <p>A function can return multiple values by implicitly returning a tuple:</p> | ||
| 479 | <pre data-id="code-animation"> | ||
| 480 | <code class="python" data-trim type="text/template"> | ||
| 481 | def square_cube(x): | ||
| 482 | """returns x, x^2 and x^3""" | ||
| 483 | return x, x**2, x**3 | ||
| 484 | numbers = square_cube(5) # Out: (5, 25, 125) | ||
| 485 | num, sqnum, cbnum = square_cube(5) # unpacking the tuple | ||
| 486 | </code> | ||
| 487 | </pre> | ||
| 488 | </section> | ||
| 489 | |||
| 490 | <section> | ||
| 491 | <h2>Functions (cont ...)</h2> | ||
| 492 | </br> | ||
| 493 | <h3>Enclosing and nested functions</h3> | ||
| 494 | <p>Can be used as:</p> | ||
| 495 | <ul> | ||
| 496 | <li>regular functions within functions</li> | ||
| 497 | <li>dynamic function factories</li> | ||
| 498 | </ul> | ||
| 499 | <pre data-id="code-animation"> | ||
| 500 | <code class="python" data-trim type="text/template"> | ||
| 501 | def get_multiplier_of(base: int) -> str: | ||
| 502 | """the function enclosing its nested functions""" | ||
| 503 | |||
| 504 | def multiplier_function(x): | ||
| 505 | """a nested function""" | ||
| 506 | return base * x | ||
| 507 | |||
| 508 | return multiplier_function | ||
| 509 | |||
| 510 | times3 = get_multiplier_of(3) | ||
| 511 | times5 = get_multiplier_of(5) | ||
| 512 | print(times3(3)) # Out: 9 | ||
| 513 | print(times5(3)) # Out: 15 | ||
| 514 | </code> | ||
| 515 | </pre> | ||
| 516 | </section> | ||
| 517 | |||
| 518 | <section> | ||
| 519 | <h2>Scopes in Python</h2> | ||
| 520 | </br> | ||
| 521 | <ul> | ||
| 522 | <li><em>local</em> - assigned names are local unless declared <em>global</em></li> | ||
| 523 | <li><em>enclosed</em> - the scope of the variable inside a function with a nested function</li> | ||
| 524 | <li><em>global - global for the current module</em></li> | ||
| 525 | <li><em>built-in</em></li> | ||
| 526 | </ul> | ||
| 527 | <p><em>locals()</em> and <em>globals()</em> return dicts of symbols for their respective scopes</p> | ||
| 528 | <pre data-id="code-animation"> | ||
| 529 | <code class="python" data-trim type="text/template"> | ||
| 530 | num1, num2 = 7, 8 # module globals | ||
| 531 | |||
| 532 | def print_numbers(): | ||
| 533 | print(num1, num2) # OK, these are module globals | ||
| 534 | num3 = 100 | ||
| 535 | print(num3) # prints 100, num3 is in the function (local) scope | ||
| 536 | global num4 # assignes / references num4 to / in the global scope | ||
| 537 | num4 = 99 | ||
| 538 | id = 200 # new symbol in local scope | ||
| 539 | # id(num4) # will not yield the expected result (raises TypeError) | ||
| 540 | |||
| 541 | def print_numbers2(): | ||
| 542 | print(num3) # OK, enclosed scope | ||
| 543 | |||
| 544 | print_numbers2() # prints 100 | ||
| 545 | |||
| 546 | print_numbers() | ||
| 547 | # print(num3) # Raises NameError - why? | ||
| 548 | print(num4) # Prints 99 - why? | ||
| 549 | # print_numbers2() # Raises NameError | ||
| 550 | </code> | ||
| 551 | </pre> | ||
| 552 | </section> | ||
| 553 | |||
| 554 | <section> | ||
| 555 | <h2>Decorators</h2> | ||
| 556 | <p>Decorators can be used to modify the behavior of the objects they decorate. Decorators can be implemented either by using classes or by using nested functions.</p> | ||
| 557 | <pre data-id="code-animation"> | ||
| 558 | <code class="python" data-trim type="text/template"> | ||
| 559 | def my_decorator(func): | ||
| 560 | |||
| 561 | def decorated(): | ||
| 562 | print('Doing something before the decorated function') | ||
| 563 | retval = func() | ||
| 564 | print('Doing something after the decorated function') | ||
| 565 | return retval | ||
| 566 | return decorated | ||
| 567 | |||
| 568 | def my_function(): | ||
| 569 | print('Alice') | ||
| 570 | |||
| 571 | my_function = my_decorator(my_function) | ||
| 572 | my_function() | ||
| 573 | </code> | ||
| 574 | </pre> | ||
| 575 | <p>... may be dificult to read / understand, while:</p> | ||
| 576 | <pre data-id="code-animation"> | ||
| 577 | <code class="python" data-trim type="text/template"> | ||
| 578 | @my_decorator | ||
| 579 | def my_function(): | ||
| 580 | print('Alice') | ||
| 581 | |||
| 582 | my_function() | ||
| 583 | </code> | ||
| 584 | </pre> | ||
| 585 | <p>... may be easier</p> | ||
| 586 | </section> | ||
| 587 | |||
| 588 | <section> | ||
| 589 | <h2>Decorators (cont ...)</h2> | ||
| 590 | </br> | ||
| 591 | <h3>A complete example</h3> | ||
| 592 | <pre data-id="code-animation"> | ||
| 593 | <code class="python" data-trim type="text/template"> | ||
| 594 | import sys | ||
| 595 | from functools import wraps | ||
| 596 | |||
| 597 | def requires_access(access_secret: str): | ||
| 598 | |||
| 599 | def api_access_decorator(f): | ||
| 600 | |||
| 601 | @wraps(f) | ||
| 602 | def decorated(*args, **kwargs): | ||
| 603 | if 'secret' not in kwargs: | ||
| 604 | sys.exit('No secret provided') | ||
| 605 | if not kwargs['secret'] or kwargs['secret'] != access_secret: | ||
| 606 | sys.exit("Secret doesn't match") | ||
| 607 | # return f(args[0], **kwargs) | ||
| 608 | return f(*args, **kwargs) | ||
| 609 | |||
| 610 | return decorated | ||
| 611 | |||
| 612 | return api_access_decorator | ||
| 613 | |||
| 614 | |||
| 615 | @requires_access(access_secret='b28cfeaa65b73cf') | ||
| 616 | def sensitive_function(data, **kwargs): | ||
| 617 | """very sensitive function""" | ||
| 618 | db.save(data) | ||
| 619 | </code> | ||
| 620 | </pre> | ||
| 621 | </section> | ||
| 622 | |||
| 623 | <section> | ||
| 624 | <h2>String formatting</h2> | ||
| 625 | </br> | ||
| 626 | <h3>The old ways...</h3> | ||
| 627 | <pre data-id="code-animation"> | ||
| 628 | <code class="python" data-trim type="text/template"> | ||
| 629 | f = 6.57865 | ||
| 630 | i = 27 | ||
| 631 | s = 'another string' | ||
| 632 | |||
| 633 | |||
| 634 | '%s - %d - %5.2f' % (s, i, f) # Out: 'another string - 27 - 6.58' | ||
| 635 | |||
| 636 | # still used in: | ||
| 637 | logger.debug("%d - %s", event.id, message) | ||
| 638 | |||
| 639 | |||
| 640 | '{} - {} - {:5.2f}'.format(s, i, f) # implicit | ||
| 641 | '{0} - {1} - {2:5.2f}'.format(s, i, f) # explicit | ||
| 642 | '{my_str} - {i} - {fl:5.2f}'.format(my_str=s, fl=f, i=i) # keyword | ||
| 643 | # Out: 'another string - 27 - 6.58' | ||
| 644 | |||
| 645 | |||
| 646 | # modern Python >= 3.6 f-strings | ||
| 647 | f'{s} - {i} - {f:5.2f}' # Out: 'another string - 27 - 6.58' | ||
| 648 | </code> | ||
| 649 | </pre> | ||
| 650 | <p>See <a href="https://docs.python.org/3/library/string.html#formatspec">https://docs.python.org/3/library/string.html#formatspec</a> for the complete format specification</p> | ||
| 651 | </section> | ||
| 652 | |||
| 653 | </div> | ||
| 654 | </div> | ||
| 655 | |||
| 656 | <script src="dist/reveal.js"></script> | ||
| 657 | <script src="plugin/zoom/zoom.js"></script> | ||
| 658 | <script src="plugin/notes/notes.js"></script> | ||
| 659 | <script src="plugin/search/search.js"></script> | ||
| 660 | <script src="plugin/markdown/markdown.js"></script> | ||
| 661 | <script src="plugin/highlight/highlight.js"></script> | ||
| 662 | <script> | ||
| 663 | |||
| 664 | // Also available as an ES module, see: | ||
| 665 | // https://revealjs.netlify.app/initialization/ | ||
| 666 | Reveal.initialize({ | ||
| 667 | controls: true, | ||
| 668 | progress: true, | ||
| 669 | center: true, | ||
| 670 | hash: true, | ||
| 671 | |||
| 672 | // Learn about plugins: https://revealjs.netlify.app/plugins/ | ||
| 673 | plugins: [ RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight ] | ||
| 674 | }); | ||
| 675 | Reveal.configure({ pdfSeparateFragments: false }); | ||
| 676 | |||
| 677 | </script> | ||
| 678 | |||
| 679 | </body> | ||
| 680 | </html> | ||
diff --git a/reveal.js/rabbitmq.html b/reveal.js/rabbitmq.html index ed1cbfd..6d1ba7a 100644 --- a/reveal.js/rabbitmq.html +++ b/reveal.js/rabbitmq.html | |||
| @@ -1,311 +1,311 @@ | |||
| 1 | <!doctype html> | 1 | <!doctype html> |
| 2 | <html lang="en"> | 2 | <html lang="en"> |
| 3 | <head> | 3 | <head> |
| 4 | <meta charset="utf-8"> | 4 | <meta charset="utf-8"> |
| 5 | <title>RabbitMQ - Intern opplæring</title> | 5 | <title>RabbitMQ - Intern opplæring</title> |
| 6 | <meta name="author" content="Simeon Simeonov"> | 6 | <meta name="author" content="Simeon Simeonov"> |
| 7 | <meta name="apple-mobile-web-app-capable" content="yes"> | 7 | <meta name="apple-mobile-web-app-capable" content="yes"> |
| 8 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | 8 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> |
| 9 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | 9 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 10 | 10 | ||
| 11 | <link rel="stylesheet" href="dist/reset.css"> | 11 | <link rel="stylesheet" href="dist/reset.css"> |
| 12 | <link rel="stylesheet" href="dist/reveal.css"> | 12 | <link rel="stylesheet" href="dist/reveal.css"> |
| 13 | 13 | ||
| 14 | <link rel="stylesheet" href="dist/theme/serif.css" id="theme"> | 14 | <link rel="stylesheet" href="dist/theme/serif.css" id="theme"> |
| 15 | 15 | ||
| 16 | <!-- Theme used for syntax highlighting of code --> | 16 | <!-- Theme used for syntax highlighting of code --> |
| 17 | <link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme"> | 17 | <link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme"> |
| 18 | </head> | 18 | </head> |
| 19 | <body> | 19 | <body> |
| 20 | <div class="reveal"> | 20 | <div class="reveal"> |
| 21 | 21 | ||
| 22 | <!-- Any section element inside of this container is displayed as a slide --> | 22 | <!-- Any section element inside of this container is displayed as a slide --> |
| 23 | <div class="slides"> | 23 | <div class="slides"> |
| 24 | <section> | 24 | <section> |
| 25 | <h1>RabbitMQ</h1> | 25 | <h1>RabbitMQ</h1> |
| 26 | <h3>Intern opplæring</h3> | 26 | <h3>Intern opplæring</h3> |
| 27 | </br> | 27 | </br> |
| 28 | <p> | 28 | <p> |
| 29 | <small>Simeon Simeonov</small> | 29 | <small>Simeon Simeonov</small> |
| 30 | </p> | 30 | </p> |
| 31 | </section> | 31 | </section> |
| 32 | 32 | ||
| 33 | <section> | 33 | <section> |
| 34 | <section id="fragments"> | 34 | <section id="fragments"> |
| 35 | <h2>Agenda</h2> | 35 | <h2>Agenda</h2> |
| 36 | </br> | 36 | </br> |
| 37 | <ul> | 37 | <ul> |
| 38 | <span class="fragment"><li>Installasjon</li></span> | 38 | <span class="fragment"><li>Installasjon</li></span> |
| 39 | <span class="fragment"><li>Administrasjon</li></span> | 39 | <span class="fragment"><li>Administrasjon</li></span> |
| 40 | <span class="fragment"><li>Utvikling</li></span> | 40 | <span class="fragment"><li>Utvikling</li></span> |
| 41 | <span class="fragment"><li>Diverse + Q&A</li></span> | 41 | <span class="fragment"><li>Diverse + Q&A</li></span> |
| 42 | </ul> | 42 | </ul> |
| 43 | </section> | 43 | </section> |
| 44 | </section> | 44 | </section> |
| 45 | 45 | ||
| 46 | <section> | 46 | <section> |
| 47 | <section> | 47 | <section> |
| 48 | <h2>Installasjon og oppsett</h2> | 48 | <h2>Installasjon og oppsett</h2> |
| 49 | </br> | 49 | </br> |
| 50 | <ul> | 50 | <ul> |
| 51 | <li><p>Skaffe seg maskin og SSL sertifikat(er)</p></li> | 51 | <li><p>Skaffe seg maskin og SSL sertifikat(er)</p></li> |
| 52 | <li><p>Bruke Ansible</p></li> | 52 | <li><p>Bruke Ansible</p></li> |
| 53 | <li><p>Opprette administrator-bruker og slette guest-brukeren</p></li> | 53 | <li><p>Opprette administrator-bruker og slette guest-brukeren</p></li> |
| 54 | <li><p>Sette rettigheter og opprette entiteter</p></li> | 54 | <li><p>Sette rettigheter og opprette entiteter</p></li> |
| 55 | </ul> | 55 | </ul> |
| 56 | </section> | 56 | </section> |
| 57 | <section> | 57 | <section> |
| 58 | <h2>Ansible</h2> | 58 | <h2>Ansible</h2> |
| 59 | </br> | 59 | </br> |
| 60 | <p>Vi bruker repo <em>UAIT/int-ansible-hosts</em></p> | 60 | <p>Vi bruker repo <em>UAIT/int-ansible-hosts</em></p> |
| 61 | <p>Etter å ha installert Ansible på lokalmaskina gjør vi følgende:</p> | 61 | <p>Etter å ha installert Ansible på lokalmaskina gjør vi følgende:</p> |
| 62 | <ul> | 62 | <ul> |
| 63 | <li><p><small><em>git clone ssh://git@bitbucket.usit.uio.no:7999/uait/int-ansible-hosts.git</em></small></p></li> | 63 | <li><p><small><em>git clone ssh://git@bitbucket.usit.uio.no:7999/uait/int-ansible-hosts.git</em></small></p></li> |
| 64 | <li><p><small><em>cd int-ansible-hosts</em></small></p></li> | 64 | <li><p><small><em>cd int-ansible-hosts</em></small></p></li> |
| 65 | <li><p><small><em>ansible-playbook --ask-become-pass -v --extra-vars '{"hosts": "mq-hostname"}' rabbitmq_playbook.yml</em></small></p></li> | 65 | <li><p><small><em>ansible-playbook --ask-become-pass -v --extra-vars '{"hosts": "mq-hostname"}' rabbitmq_playbook.yml</em></small></p></li> |
| 66 | </ul> | 66 | </ul> |
| 67 | <p>RabbitMQ skal nå være installert og tilgjengelig på <em>https://mq-hostname</em></p> | 67 | <p>RabbitMQ skal nå være installert og tilgjengelig på <em>https://mq-hostname</em></p> |
| 68 | </section> | 68 | </section> |
| 69 | 69 | ||
| 70 | <section> | 70 | <section> |
| 71 | <h2>Administrator-bruker</h2> | 71 | <h2>Administrator-bruker</h2> |
| 72 | </br> | 72 | </br> |
| 73 | <p>Brukeren <em>guest</em> med passord <em>guest</em> vil eksistere etter at Ansible har kjørt</p> | 73 | <p>Brukeren <em>guest</em> med passord <em>guest</em> vil eksistere etter at Ansible har kjørt</p> |
| 74 | <p>I tillegg til webgrensesnittet, kan en bruke <em>rabbitmqctl</em></p> | 74 | <p>I tillegg til webgrensesnittet, kan en bruke <em>rabbitmqctl</em></p> |
| 75 | <ul> | 75 | <ul> |
| 76 | <li><p><small><em>rabbitmqctl list_users</em> (Lister alle eksisterende RabbitMQ-brukere)</small></p></li> | 76 | <li><p><small><em>rabbitmqctl list_users</em> (Lister alle eksisterende RabbitMQ-brukere)</small></p></li> |
| 77 | <li><p><small><em>rabbitmqctl add_user rmq_admin <passord></em> (Legger til dedikert admin bruker <em>rmq_admin</em>)</small></p></li> | 77 | <li><p><small><em>rabbitmqctl add_user rmq_admin <passord></em> (Legger til dedikert admin bruker <em>rmq_admin</em>)</small></p></li> |
| 78 | <li><p><small><em>rabbitmqctl set_user_tags rmq_admin administrator</em> (Gir administrator rolle til <em>rmq_admin</em>)</small></p></li> | 78 | <li><p><small><em>rabbitmqctl set_user_tags rmq_admin administrator</em> (Gir administrator rolle til <em>rmq_admin</em>)</small></p></li> |
| 79 | <li><p><small><em>rabbitmqctl delete_user guest</em> (Sletter brukeren <em>guest</em>)</small></p></li> | 79 | <li><p><small><em>rabbitmqctl delete_user guest</em> (Sletter brukeren <em>guest</em>)</small></p></li> |
| 80 | </ul> | 80 | </ul> |
| 81 | </section> | 81 | </section> |
| 82 | 82 | ||
| 83 | <section> | 83 | <section> |
| 84 | <h2><em>vhost</em></h2> | 84 | <h2><em>vhost</em></h2> |
| 85 | </br> | 85 | </br> |
| 86 | <p><em>vhost</em> fungerer som en beholder (container) for RabbitMQ/AMQP objekter. Nok igjen kan en velge mellom webgrensesnitt og <em>rabbitmqctl</em></p> | 86 | <p><em>vhost</em> fungerer som en beholder (container) for RabbitMQ/AMQP objekter. Nok igjen kan en velge mellom webgrensesnitt og <em>rabbitmqctl</em></p> |
| 87 | <ul> | 87 | <ul> |
| 88 | <li><p><small><em>rabbitmqctl list_vhosts</em> (Lister alle eksisterende vhosts)</small></p></li> | 88 | <li><p><small><em>rabbitmqctl list_vhosts</em> (Lister alle eksisterende vhosts)</small></p></li> |
| 89 | <li><p><small><em>rabbitmqctl add_vhost /no/uio/integration</em> (Oppretter <em>vhost</em> med navn <em>/no/uio/integration</em>)</small></p></li> | 89 | <li><p><small><em>rabbitmqctl add_vhost /no/uio/integration</em> (Oppretter <em>vhost</em> med navn <em>/no/uio/integration</em>)</small></p></li> |
| 90 | </ul> | 90 | </ul> |
| 91 | </section> | 91 | </section> |
| 92 | 92 | ||
| 93 | <section> | 93 | <section> |
| 94 | <h2>Rettigheter</h2> | 94 | <h2>Rettigheter</h2> |
| 95 | <p>RabbitMQ implementerer 2 rettighetsnivåer(*):</p> | 95 | <p>RabbitMQ implementerer 2 rettighetsnivåer(*):</p> |
| 96 | <ul> | 96 | <ul> |
| 97 | <li>Per <em>vhost</em></li> | 97 | <li>Per <em>vhost</em></li> |
| 98 | <li>Per entitet</li> | 98 | <li>Per entitet</li> |
| 99 | </ul> | 99 | </ul> |
| 100 | <p>RabbitMQ definerer 3 typer operasjoner:</p> | 100 | <p>RabbitMQ definerer 3 typer operasjoner:</p> |
| 101 | <ul> | 101 | <ul> |
| 102 | <li><p><em>configure</em> - opprette / slette entiteter eller endre deres oppførsel</p></li> | 102 | <li><p><em>configure</em> - opprette / slette entiteter eller endre deres oppførsel</p></li> |
| 103 | <li><p><em>write</em> - skrive melding til en entitet</p></li> | 103 | <li><p><em>write</em> - skrive melding til en entitet</p></li> |
| 104 | <li><p><em>read</em> - lese melding fra entitet</p></li> | 104 | <li><p><em>read</em> - lese melding fra entitet</p></li> |
| 105 | </ul> | 105 | </ul> |
| 106 | <p><small>(*) - <em>/etc/rabbitmq/rabbitmq.config</em> gir flere muligheter</small></p> | 106 | <p><small>(*) - <em>/etc/rabbitmq/rabbitmq.config</em> gir flere muligheter</small></p> |
| 107 | </section> | 107 | </section> |
| 108 | 108 | ||
| 109 | <section> | 109 | <section> |
| 110 | <h2>Rettigheter og brukere</h2> | 110 | <h2>Rettigheter og brukere</h2> |
| 111 | <p>RabbitMQ bruker <em>regular expressions</em> til å definere rettigheter</p> | 111 | <p>RabbitMQ bruker <em>regular expressions</em> til å definere rettigheter</p> |
| 112 | <ul> | 112 | <ul> |
| 113 | <li><p><small><em>rabbitmqctl add_user cerebrum <passord></em></small></p></li> | 113 | <li><p><small><em>rabbitmqctl add_user cerebrum <passord></em></small></p></li> |
| 114 | <li><p><small><em>rabbitmqctl add_user uio_ad_microservice <passord></em></small></p></li> | 114 | <li><p><small><em>rabbitmqctl add_user uio_ad_microservice <passord></em></small></p></li> |
| 115 | <li><p><small><em>rabbitmqctl set_permissions -p /no/uio/integration cerebrum "^$" "^ex_.*" "^$"</em></small></p></li> | 115 | <li><p><small><em>rabbitmqctl set_permissions -p /no/uio/integration cerebrum "^$" "^ex_.*" "^$"</em></small></p></li> |
| 116 | <li><p><small><em>rabbitmqctl set_permissions -p /no/uio/integration uio_ad_microservice "^q_ad_ms_.*" "^q_ad_ms_.*" "^(ex_messages|q_ad_ms_.*)$"</em></small></p></li> | 116 | <li><p><small><em>rabbitmqctl set_permissions -p /no/uio/integration uio_ad_microservice "^q_ad_ms_.*" "^q_ad_ms_.*" "^(ex_messages|q_ad_ms_.*)$"</em></small></p></li> |
| 117 | </ul> | 117 | </ul> |
| 118 | </section> | 118 | </section> |
| 119 | 119 | ||
| 120 | <section> | 120 | <section> |
| 121 | <h2>Køer</h2> | 121 | <h2>Køer</h2> |
| 122 | <p>Køer defineres som <em>durable</em> ved hjelp av webgrensesnittet.</p> | 122 | <p>Køer defineres som <em>durable</em> ved hjelp av webgrensesnittet.</p> |
| 123 | <p>"Alle køer (både forhåndsdefinerte og de som defineres av konsument) må ha navn som starter med <em>q_</em>. Køen vil ha navn som uttryker mottakeren som bruker den."</p> | 123 | <p>"Alle køer (både forhåndsdefinerte og de som defineres av konsument) må ha navn som starter med <em>q_</em>. Køen vil ha navn som uttryker mottakeren som bruker den."</p> |
| 124 | <p>F.eks. <em>q_ad_ms_all</em> vil være et passende navn for køen som brukes av AD-microservice mottakeren.</p> | 124 | <p>F.eks. <em>q_ad_ms_all</em> vil være et passende navn for køen som brukes av AD-microservice mottakeren.</p> |
| 125 | </section> | 125 | </section> |
| 126 | 126 | ||
| 127 | <section> | 127 | <section> |
| 128 | <h2><em>Exchange</em></h2> | 128 | <h2><em>Exchange</em></h2> |
| 129 | <p>"Avsendere vil vanligvis sende meldinger til exchange <em>ex_messages</em>."</p> | 129 | <p>"Avsendere vil vanligvis sende meldinger til exchange <em>ex_messages</em>."</p> |
| 130 | <p>Det er flere typer <em>exchange</em>, men vi vil bruke kun <em>topic exchange</em>.</p> | 130 | <p>Det er flere typer <em>exchange</em>, men vi vil bruke kun <em>topic exchange</em>.</p> |
| 131 | </section> | 131 | </section> |
| 132 | </section> | 132 | </section> |
| 133 | 133 | ||
| 134 | <section> | 134 | <section> |
| 135 | <section> | 135 | <section> |
| 136 | <h2>Utvikling</h2> | 136 | <h2>Utvikling</h2> |
| 137 | </br> | 137 | </br> |
| 138 | <ul> | 138 | <ul> |
| 139 | <li><p><em>Routing keys</em> i <em>topic exchange</em></p></li> | 139 | <li><p><em>Routing keys</em> i <em>topic exchange</em></p></li> |
| 140 | <li><p>Bindinger</p></li> | 140 | <li><p>Bindinger</p></li> |
| 141 | <li><p>Protokoller og porter</p></li> | 141 | <li><p>Protokoller og porter</p></li> |
| 142 | <li><p>Eksempler</p></li> | 142 | <li><p>Eksempler</p></li> |
| 143 | </ul> | 143 | </ul> |
| 144 | </section> | 144 | </section> |
| 145 | 145 | ||
| 146 | <section> | 146 | <section> |
| 147 | <h2><em>Routing keys</em> i <em>topic exchange</em></h2> | 147 | <h2><em>Routing keys</em> i <em>topic exchange</em></h2> |
| 148 | </br> | 148 | </br> |
| 149 | <p><em>Routing key</em> settes av sender som en del av meldingen og blir inspisert av brokeren dersom meldingen sendes til en <em>topic exchange</em>.</p> | 149 | <p><em>Routing key</em> settes av sender som en del av meldingen og blir inspisert av brokeren dersom meldingen sendes til en <em>topic exchange</em>.</p> |
| 150 | <p>Strukturen til en topic / message routing key er:</p> | 150 | <p>Strukturen til en topic / message routing key er:</p> |
| 151 | <p><small><kilde>.<type>.<objekt>.<hendelse></small></p> | 151 | <p><small><kilde>.<type>.<objekt>.<hendelse></small></p> |
| 152 | <p>F.eks.:</p> | 152 | <p>F.eks.:</p> |
| 153 | <p><small>cerebrum.event.person.delete</small></p> | 153 | <p><small>cerebrum.event.person.delete</small></p> |
| 154 | </section> | 154 | </section> |
| 155 | 155 | ||
| 156 | <section> | 156 | <section> |
| 157 | <h2>Bindinger</h2> | 157 | <h2>Bindinger</h2> |
| 158 | <p>For at en melding sendt til en <em>topic exchange</em> skal havne i en bestemt kø, må køen være bundet (<em>bound</em>) til <em>exchange</em>.</p> | 158 | <p>For at en melding sendt til en <em>topic exchange</em> skal havne i en bestemt kø, må køen være bundet (<em>bound</em>) til <em>exchange</em>.</p> |
| 159 | <p>I <em>topic exchange</em> brukes <em>routing key</em> (<em>topic</em>) til å avgjøre hvilke av meldingene som blir sendt vil havne i køen som er bundet.</p> | 159 | <p>I <em>topic exchange</em> brukes <em>routing key</em> (<em>topic</em>) til å avgjøre hvilke av meldingene som blir sendt vil havne i køen som er bundet.</p> |
| 160 | <p>Dersom vi binder køen <em>q_ad_ms_all</em> til <em>topic exchange</em> <em>ex_messages</em> med <em>binding key</em> <em>cerebrum.event.account.*</em> vil alle meldinger som blir sendt til <em>ex_messages</em> med topic som starter med <em>cerebrum.event.account.</em> havne i <em>q_ad_ms_all</em>.</p> | 160 | <p>Dersom vi binder køen <em>q_ad_ms_all</em> til <em>topic exchange</em> <em>ex_messages</em> med <em>binding key</em> <em>cerebrum.event.account.*</em> vil alle meldinger som blir sendt til <em>ex_messages</em> med topic som starter med <em>cerebrum.event.account.</em> havne i <em>q_ad_ms_all</em>.</p> |
| 161 | <p>En kø kan bli bundet med én eller flere nøkler til en <em>topic exchange</em>.</p> | 161 | <p>En kø kan bli bundet med én eller flere nøkler til en <em>topic exchange</em>.</p> |
| 162 | </section> | 162 | </section> |
| 163 | 163 | ||
| 164 | <section> | 164 | <section> |
| 165 | <h2>Protokoller og porter</h2> | 165 | <h2>Protokoller og porter</h2> |
| 166 | </br> | 166 | </br> |
| 167 | <p>"AMQP 0.9.1 prioritert siden det er protokollen som mapper best mot RabbitMQs funksjonalitet. Vi har også noe erfaring med bruk av denne."</p> | 167 | <p>"AMQP 0.9.1 prioritert siden det er protokollen som mapper best mot RabbitMQs funksjonalitet. Vi har også noe erfaring med bruk av denne."</p> |
| 168 | <p>RabbitMQ lytter for AMQP0.9.1 på SSL port <em>5671</em>.</p> | 168 | <p>RabbitMQ lytter for AMQP0.9.1 på SSL port <em>5671</em>.</p> |
| 169 | </br> | 169 | </br> |
| 170 | <p>Andre protokoller og tjenester for RabbitMQ:</p> | 170 | <p>Andre protokoller og tjenester for RabbitMQ:</p> |
| 171 | <ul> | 171 | <ul> |
| 172 | <li>STOMP - 61614 (SSL)</li> | 172 | <li>STOMP - 61614 (SSL)</li> |
| 173 | <li>RabbitMQ Management (webgrensesnitt) - 15671 (SSL)</li> | 173 | <li>RabbitMQ Management (webgrensesnitt) - 15671 (SSL)</li> |
| 174 | </ul> | 174 | </ul> |
| 175 | </section> | 175 | </section> |
| 176 | 176 | ||
| 177 | <section> | 177 | <section> |
| 178 | <h2>Eksempler</h2> | 178 | <h2>Eksempler</h2> |
| 179 | </br> | 179 | </br> |
| 180 | <pre> | 180 | <pre> |
| 181 | <code class="hljs" data-trim contenteditable> | 181 | <code class="hljs" data-trim contenteditable> |
| 182 | $ pip install pika | 182 | $ pip install pika |
| 183 | </code> | 183 | </code> |
| 184 | </pre> | 184 | </pre> |
| 185 | <p>Python:</p> | 185 | <p>Python:</p> |
| 186 | <p>"Well, we'll not risk another frontal assault. That rabbit's dynamite...."</p> | 186 | <p>"Well, we'll not risk another frontal assault. That rabbit's dynamite...."</p> |
| 187 | </section> | 187 | </section> |
| 188 | 188 | ||
| 189 | </section> | 189 | </section> |
| 190 | 190 | ||
| 191 | <section> | 191 | <section> |
| 192 | <section> | 192 | <section> |
| 193 | <h2>Dokumentasjon og lenker</h2> | 193 | <h2>Dokumentasjon og lenker</h2> |
| 194 | </br> | 194 | </br> |
| 195 | <p> | 195 | <p> |
| 196 | RabbitMQ: | 196 | RabbitMQ: |
| 197 | </p> | 197 | </p> |
| 198 | <ul> | 198 | <ul> |
| 199 | <li> | 199 | <li> |
| 200 | <p> | 200 | <p> |
| 201 | <small> | 201 | <small> |
| 202 | <a href="http://www.rabbitmq.com">http://www.rabbitmq.com</a> - RabbitMQ sin offisielle side | 202 | <a href="http://www.rabbitmq.com">http://www.rabbitmq.com</a> - RabbitMQ sin offisielle side |
| 203 | </small> | 203 | </small> |
| 204 | </li> | 204 | </li> |
| 205 | <li> | 205 | <li> |
| 206 | <p> | 206 | <p> |
| 207 | <small> | 207 | <small> |
| 208 | <a href="http://www.rabbitmq.com/configure.html">http://www.rabbitmq.com/configure.html</a> - Generell konfigurasjon av RabbitMQ | 208 | <a href="http://www.rabbitmq.com/configure.html">http://www.rabbitmq.com/configure.html</a> - Generell konfigurasjon av RabbitMQ |
| 209 | </small> | 209 | </small> |
| 210 | </li> | 210 | </li> |
| 211 | <li> | 211 | <li> |
| 212 | <p> | 212 | <p> |
| 213 | <small> | 213 | <small> |
| 214 | <a href="http://www.rabbitmq.com/management.html">http://www.rabbitmq.com/management.html</a> - Bruk av rabbitmq_management plugin i RabbitMQ | 214 | <a href="http://www.rabbitmq.com/management.html">http://www.rabbitmq.com/management.html</a> - Bruk av rabbitmq_management plugin i RabbitMQ |
| 215 | </small> | 215 | </small> |
| 216 | </li> | 216 | </li> |
| 217 | <li> | 217 | <li> |
| 218 | <p> | 218 | <p> |
| 219 | <small> | 219 | <small> |
| 220 | <a href="http://www.rabbitmq.com/access-control.html">http://www.rabbitmq.com/access-control.html</a> - Tilgangskontroll i RabbitMQ | 220 | <a href="http://www.rabbitmq.com/access-control.html">http://www.rabbitmq.com/access-control.html</a> - Tilgangskontroll i RabbitMQ |
| 221 | </small> | 221 | </small> |
| 222 | </li> | 222 | </li> |
| 223 | <li> | 223 | <li> |
| 224 | <p> | 224 | <p> |
| 225 | <small> | 225 | <small> |
| 226 | <a href="http://www.rabbitmq.com/man/rabbitmqctl.1.man.html">http://www.rabbitmq.com/man/rabbitmqctl.1.man.html</a> - rabbitmqctl manual side | 226 | <a href="http://www.rabbitmq.com/man/rabbitmqctl.1.man.html">http://www.rabbitmq.com/man/rabbitmqctl.1.man.html</a> - rabbitmqctl manual side |
| 227 | </small> | 227 | </small> |
| 228 | </li> | 228 | </li> |
| 229 | <li> | 229 | <li> |
| 230 | <p> | 230 | <p> |
| 231 | <small> | 231 | <small> |
| 232 | <a href="http://www.rabbitmq.com/tutorials/amqp-concepts.html">http://www.rabbitmq.com/tutorials/amqp-concepts.html</a> - Generelt om AMQP og forskjellige exchanges | 232 | <a href="http://www.rabbitmq.com/tutorials/amqp-concepts.html">http://www.rabbitmq.com/tutorials/amqp-concepts.html</a> - Generelt om AMQP og forskjellige exchanges |
| 233 | </small> | 233 | </small> |
| 234 | </li> | 234 | </li> |
| 235 | </ul> | 235 | </ul> |
| 236 | </section> | 236 | </section> |
| 237 | <section> | 237 | <section> |
| 238 | <h2>Dokumentasjon og lenker</h2> | 238 | <h2>Dokumentasjon og lenker</h2> |
| 239 | <p> | 239 | <p> |
| 240 | UiO: | 240 | UiO: |
| 241 | </p> | 241 | </p> |
| 242 | <ul> | 242 | <ul> |
| 243 | <li> | 243 | <li> |
| 244 | <p> | 244 | <p> |
| 245 | <small> | 245 | <small> |
| 246 | <a href="http://www.usit.uio.no/om/tjenestegrupper/cerebrum/utvikling/dokumentasjon/tools/rabbitmq.html">Installasjon og oppsett av RabbitMQ for Cerebrum utviklere</a> | 246 | <a href="http://www.usit.uio.no/om/tjenestegrupper/cerebrum/utvikling/dokumentasjon/tools/rabbitmq.html">Installasjon og oppsett av RabbitMQ for Cerebrum utviklere</a> |
| 247 | </small> | 247 | </small> |
| 248 | </li> | 248 | </li> |
| 249 | <li> | 249 | <li> |
| 250 | <p> | 250 | <p> |
| 251 | <small> | 251 | <small> |
| 252 | <a href="http://www.usit.uio.no/om/tjenestegrupper/cerebrum/utvikling/aktiviteter/office365-uia/broker/rabbitmq.html">Oppsett av RabbitMQ for Office365 integrasjon mot UiA</a> | 252 | <a href="http://www.usit.uio.no/om/tjenestegrupper/cerebrum/utvikling/aktiviteter/office365-uia/broker/rabbitmq.html">Oppsett av RabbitMQ for Office365 integrasjon mot UiA</a> |
| 253 | </small> | 253 | </small> |
| 254 | </li> | 254 | </li> |
| 255 | <li> | 255 | <li> |
| 256 | <p> | 256 | <p> |
| 257 | <small> | 257 | <small> |
| 258 | <a href="https://www.usit.uio.no/om/organisasjon/uav/usitint/interndokumentasjon/integrasjon/broker/utredning-meldingsflyt.html">Utredning: Meldingsflyt, kø-struktur og navngiving</a> (intern) | 258 | <a href="https://www.usit.uio.no/om/organisasjon/uav/usitint/interndokumentasjon/integrasjon/broker/utredning-meldingsflyt.html">Utredning: Meldingsflyt, kø-struktur og navngiving</a> (intern) |
| 259 | </small> | 259 | </small> |
| 260 | </li> | 260 | </li> |
| 261 | </ul> | 261 | </ul> |
| 262 | <p> | 262 | <p> |
| 263 | Bøker: | 263 | Bøker: |
| 264 | </p> | 264 | </p> |
| 265 | <ul> | 265 | <ul> |
| 266 | <li> | 266 | <li> |
| 267 | <p> | 267 | <p> |
| 268 | <small> | 268 | <small> |
| 269 | RabbitMQ in action - Alvaro Videla / Jason J.W. Williams - 2012 - Manning | 269 | RabbitMQ in action - Alvaro Videla / Jason J.W. Williams - 2012 - Manning |
| 270 | </small> | 270 | </small> |
| 271 | </li> | 271 | </li> |
| 272 | <li> | 272 | <li> |
| 273 | <p> | 273 | <p> |
| 274 | <small> | 274 | <small> |
| 275 | Mastering RabbitMQ - Ayanoglu / Aytas / Nahum - 2015 - PACKT Publishing | 275 | Mastering RabbitMQ - Ayanoglu / Aytas / Nahum - 2015 - PACKT Publishing |
| 276 | </small> | 276 | </small> |
| 277 | </li> | 277 | </li> |
| 278 | </section> | 278 | </section> |
| 279 | </section> | 279 | </section> |
| 280 | |||
| 281 | <section> | ||
| 282 | <h1>Q&A</h1> | ||
| 283 | </section> | ||
| 284 | |||
| 285 | </div> | ||
| 286 | </div> | ||
| 280 | 287 | ||
| 281 | <section> | 288 | <script src="dist/reveal.js"></script> |
| 282 | <h1>Q&A</h1> | 289 | <script src="plugin/zoom/zoom.js"></script> |
| 283 | </section> | 290 | <script src="plugin/notes/notes.js"></script> |
| 291 | <script src="plugin/search/search.js"></script> | ||
| 292 | <script src="plugin/markdown/markdown.js"></script> | ||
| 293 | <script src="plugin/highlight/highlight.js"></script> | ||
| 294 | <script> | ||
| 284 | 295 | ||
| 285 | </div> | 296 | // Also available as an ES module, see: |
| 286 | </div> | 297 | // https://revealjs.com/initialization/ |
| 287 | 298 | Reveal.initialize({ | |
| 288 | <script src="dist/reveal.js"></script> | 299 | controls: true, |
| 289 | <script src="plugin/zoom/zoom.js"></script> | 300 | progress: true, |
| 290 | <script src="plugin/notes/notes.js"></script> | 301 | center: true, |
| 291 | <script src="plugin/search/search.js"></script> | 302 | hash: true, |
| 292 | <script src="plugin/markdown/markdown.js"></script> | ||
| 293 | <script src="plugin/highlight/highlight.js"></script> | ||
| 294 | <script> | ||
| 295 | 303 | ||
| 296 | // Also available as an ES module, see: | 304 | // Learn about plugins: https://revealjs.com/plugins/ |
| 297 | // https://revealjs.netlify.app/initialization/ | 305 | plugins: [ RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight ] |
| 298 | Reveal.initialize({ | 306 | }); |
| 299 | controls: true, | ||
| 300 | progress: true, | ||
| 301 | center: true, | ||
| 302 | hash: true, | ||
| 303 | 307 | ||
| 304 | // Learn about plugins: https://revealjs.netlify.app/plugins/ | 308 | </script> |
| 305 | plugins: [ RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight ] | ||
| 306 | }); | ||
| 307 | |||
| 308 | </script> | ||
| 309 | 309 | ||
| 310 | </body> | 310 | </body> |
| 311 | </html> | 311 | </html> |
diff --git a/reveal.js/sqlalchemy.html b/reveal.js/sqlalchemy.html index a0d6617..bbcd1ca 100755..100644 --- a/reveal.js/sqlalchemy.html +++ b/reveal.js/sqlalchemy.html | |||
| @@ -98,29 +98,29 @@ | |||
| 98 | metadata = MetaData() | 98 | metadata = MetaData() |
| 99 | 99 | ||
| 100 | production_types_table = Table( | 100 | production_types_table = Table( |
| 101 | "production_types", | 101 | "production_types", |
| 102 | metadata, | 102 | metadata, |
| 103 | Column("production_type_id", Integer, primary_key=True), | 103 | Column("production_type_id", Integer, primary_key=True), |
| 104 | Column("code", String(3), nullable=False, unique=True), | 104 | Column("code", String(3), nullable=False, unique=True), |
| 105 | Column("description", String), # Column("name", String(128)) is possible | 105 | Column("description", String), # Column("name", String(128)) is possible |
| 106 | ) | 106 | ) |
| 107 | 107 | ||
| 108 | bidding_areas_table = Table( | 108 | bidding_areas_table = Table( |
| 109 | "bidding_areas", | 109 | "bidding_areas", |
| 110 | metadata, | 110 | metadata, |
| 111 | Column("bidding_area_id", Integer, primary_key=True), | 111 | Column("bidding_area_id", Integer, primary_key=True), |
| 112 | Column("code", String(3), nullable=False, unique=True), | 112 | Column("code", String(3), nullable=False, unique=True), |
| 113 | Column("name", String(32)), | 113 | Column("name", String(32)), |
| 114 | ) | 114 | ) |
| 115 | 115 | ||
| 116 | production_plans_table = Table( | 116 | production_plans_table = Table( |
| 117 | "production_plans", | 117 | "production_plans", |
| 118 | metadata, | 118 | metadata, |
| 119 | Column("record_created_time", DateTime(timezone=False), primary_key=True), | 119 | Column("record_created_time", DateTime(timezone=False), primary_key=True), |
| 120 | Column("start_time", DateTime(timezone=False), primary_key=True), | 120 | Column("start_time", DateTime(timezone=False), primary_key=True), |
| 121 | Column("bidding_area_id", Integer, ForeignKey("bidding_areas.bidding_area_id"), primary_key=True), | 121 | Column("bidding_area_id", Integer, ForeignKey("bidding_areas.bidding_area_id"), primary_key=True), |
| 122 | Column("production_type_id", Integer, ForeignKey("production_types.production_type_id"), primary_key=True), | 122 | Column("production_type_id", Integer, ForeignKey("production_types.production_type_id"), primary_key=True), |
| 123 | Column("value", Numeric, nullable=False), | 123 | Column("value", Numeric, nullable=False), |
| 124 | ) | 124 | ) |
| 125 | 125 | ||
| 126 | metadata.create_all(engine) # creates the tables | 126 | metadata.create_all(engine) # creates the tables |
| @@ -150,13 +150,13 @@ | |||
| 150 | insert_stmt.execute(bidding_area_id=1, code="NO1", name="Elspot NO1") # insert a single entry | 150 | insert_stmt.execute(bidding_area_id=1, code="NO1", name="Elspot NO1") # insert a single entry |
| 151 | # ... or a list of entries | 151 | # ... or a list of entries |
| 152 | insert_stmt.execute( | 152 | insert_stmt.execute( |
| 153 | [ | 153 | [ |
| 154 | {"bidding_area_id": 2, "code": "NO2", "name": "Elspot NO2"}, | 154 | {"bidding_area_id": 2, "code": "NO2", "name": "Elspot NO2"}, |
| 155 | {"bidding_area_id": 3, "code": "NO3", "name": "Elspot NO3"}, | 155 | {"bidding_area_id": 3, "code": "NO3", "name": "Elspot NO3"}, |
| 156 | {"bidding_area_id": 4, "code": "NO4", "name": "Elspot NO4"}, | 156 | {"bidding_area_id": 4, "code": "NO4", "name": "Elspot NO4"}, |
| 157 | {"bidding_area_id": 5, "code": "NO5", "name": "Elspot NO5"}, | 157 | {"bidding_area_id": 5, "code": "NO5", "name": "Elspot NO5"}, |
| 158 | {"bidding_area_id": 6, "code": "NO6", "name": "Elspot NO6"}, | 158 | {"bidding_area_id": 6, "code": "NO6", "name": "Elspot NO6"}, |
| 159 | ] | 159 | ] |
| 160 | ) | 160 | ) |
| 161 | 161 | ||
| 162 | metadata.bind = engine # no need to explicitly bind the engine from now on | 162 | metadata.bind = engine # no need to explicitly bind the engine from now on |
| @@ -182,21 +182,21 @@ | |||
| 182 | from sqlalchemy.orm import mapper | 182 | from sqlalchemy.orm import mapper |
| 183 | 183 | ||
| 184 | class ProductionType: | 184 | class ProductionType: |
| 185 | def __init__(self, code, description): | 185 | def __init__(self, code, description): |
| 186 | self.code = code | 186 | self.code = code |
| 187 | self.description = description | 187 | self.description = description |
| 188 | 188 | ||
| 189 | def __str__(self): | 189 | def __str__(self): |
| 190 | return self.code | 190 | return self.code |
| 191 | 191 | ||
| 192 | 192 | ||
| 193 | class BiddingArea: | 193 | class BiddingArea: |
| 194 | def __init__(self, code, name): | 194 | def __init__(self, code, name): |
| 195 | self.code = code | 195 | self.code = code |
| 196 | self.name = name | 196 | self.name = name |
| 197 | 197 | ||
| 198 | def __str__(self): | 198 | def __str__(self): |
| 199 | return self.code | 199 | return self.code |
| 200 | 200 | ||
| 201 | mapper(ProductionType, production_types_table) | 201 | mapper(ProductionType, production_types_table) |
| 202 | mapper(BiddingArea, bidding_areas_table) | 202 | mapper(BiddingArea, bidding_areas_table) |
| @@ -212,27 +212,27 @@ | |||
| 212 | from sqlalchemy.orm import relationship | 212 | from sqlalchemy.orm import relationship |
| 213 | 213 | ||
| 214 | class ProductionPlan: | 214 | class ProductionPlan: |
| 215 | def __init__(self, record_created_time, start_time, production_type, bidding_area, value): | 215 | def __init__(self, record_created_time, start_time, production_type, bidding_area, value): |
| 216 | self.record_created_time = record_created_time | 216 | self.record_created_time = record_created_time |
| 217 | self.start_time = start_time | 217 | self.start_time = start_time |
| 218 | self.production_type = production_type | 218 | self.production_type = production_type |
| 219 | self.bidding_area = bidding_area | 219 | self.bidding_area = bidding_area |
| 220 | self.value = value | 220 | self.value = value |
| 221 | 221 | ||
| 222 | def __str__(self): | 222 | def __str__(self): |
| 223 | return ( | 223 | return ( |
| 224 | f"{self.record_created_time} {self.start_time} " | 224 | f"{self.record_created_time} {self.start_time} " |
| 225 | f"{self.production_type} {self.bidding_area} {self.value}" | 225 | f"{self.production_type} {self.bidding_area} {self.value}" |
| 226 | ) | 226 | ) |
| 227 | 227 | ||
| 228 | 228 | ||
| 229 | mapper( | 229 | mapper( |
| 230 | ProductionPlan, | 230 | ProductionPlan, |
| 231 | production_plans_table, | 231 | production_plans_table, |
| 232 | properties = { | 232 | properties = { |
| 233 | "production_type": relationship(ProductionType, backref="production_plans"), | 233 | "production_type": relationship(ProductionType, backref="production_plans"), |
| 234 | "bidding_area": relationship(BiddingArea, backref="production_plans"), | 234 | "bidding_area": relationship(BiddingArea, backref="production_plans"), |
| 235 | }, | 235 | }, |
| 236 | ) | 236 | ) |
| 237 | </code> | 237 | </code> |
| 238 | </pre> | 238 | </pre> |
| @@ -249,32 +249,32 @@ | |||
| 249 | Base = declarative_base() | 249 | Base = declarative_base() |
| 250 | 250 | ||
| 251 | class ProductionType(Base): | 251 | class ProductionType(Base): |
| 252 | __tablename__ = "production_types" | 252 | __tablename__ = "production_types" |
| 253 | 253 | ||
| 254 | production_type_id = Column(Integer, primary_key=True) | 254 | production_type_id = Column(Integer, primary_key=True) |
| 255 | code = Column(String(3), nullable=False, unique=True) | 255 | code = Column(String(3), nullable=False, unique=True) |
| 256 | description = Column(String) | 256 | description = Column(String) |
| 257 | 257 | ||
| 258 | def __init__(self, code, description): | 258 | def __init__(self, code, description): |
| 259 | self.code = code | 259 | self.code = code |
| 260 | self.description = description | 260 | self.description = description |
| 261 | 261 | ||
| 262 | def __str__(self): | 262 | def __str__(self): |
| 263 | return self.code | 263 | return self.code |
| 264 | 264 | ||
| 265 | class BiddingArea(Base): | 265 | class BiddingArea(Base): |
| 266 | __tablename__ = "bidding_areas" | 266 | __tablename__ = "bidding_areas" |
| 267 | 267 | ||
| 268 | bidding_area_id = Column(Integer, primary_key=True) | 268 | bidding_area_id = Column(Integer, primary_key=True) |
| 269 | code = Column(String(3), nullable=False, unique=True) | 269 | code = Column(String(3), nullable=False, unique=True) |
| 270 | name = Column(String(32)) | 270 | name = Column(String(32)) |
| 271 | 271 | ||
| 272 | def __init__(self, code, name): | 272 | def __init__(self, code, name): |
| 273 | self.code = code | 273 | self.code = code |
| 274 | self.name = name | 274 | self.name = name |
| 275 | 275 | ||
| 276 | def __str__(self): | 276 | def __str__(self): |
| 277 | return self.code | 277 | return self.code |
| 278 | </code> | 278 | </code> |
| 279 | </pre> | 279 | </pre> |
| 280 | </section> | 280 | </section> |
| @@ -288,31 +288,31 @@ | |||
| 288 | from sqlalchemy.orm import relationship, backref | 288 | from sqlalchemy.orm import relationship, backref |
| 289 | 289 | ||
| 290 | class ProductionPlan(Base): | 290 | class ProductionPlan(Base): |
| 291 | __tablename__ = "production_plans" | 291 | __tablename__ = "production_plans" |
| 292 | 292 | ||
| 293 | record_created_time = Column(DateTime(timezone=False), primary_key=True) | 293 | record_created_time = Column(DateTime(timezone=False), primary_key=True) |
| 294 | start_time = Column(DateTime(timezone=False), primary_key=True) | 294 | start_time = Column(DateTime(timezone=False), primary_key=True) |
| 295 | bidding_area_id = Column(Integer, ForeignKey("bidding_areas.bidding_area_id"), primary_key=True) | 295 | bidding_area_id = Column(Integer, ForeignKey("bidding_areas.bidding_area_id"), primary_key=True) |
| 296 | production_type_id = Column(Integer, ForeignKey("production_types.production_type_id"), primary_key=True) | 296 | production_type_id = Column(Integer, ForeignKey("production_types.production_type_id"), primary_key=True) |
| 297 | value = Column(Numeric, nullable=False) | 297 | value = Column(Numeric, nullable=False) |
| 298 | 298 | ||
| 299 | # defining relationships. | 299 | # defining relationships. |
| 300 | # the defined attributes will reference 'ProductionType' and 'BiddingArea' objects | 300 | # the defined attributes will reference 'ProductionType' and 'BiddingArea' objects |
| 301 | production_type = relationship(ProductionType, backref=backref("production_plans")) | 301 | production_type = relationship(ProductionType, backref=backref("production_plans")) |
| 302 | bidding_area = relationship(BiddingArea, backref=backref("production_plans")) | 302 | bidding_area = relationship(BiddingArea, backref=backref("production_plans")) |
| 303 | 303 | ||
| 304 | def __init__(self, record_created_time, start_time, production_type, bidding_area, value): | 304 | def __init__(self, record_created_time, start_time, production_type, bidding_area, value): |
| 305 | self.record_created_time = record_created_time | 305 | self.record_created_time = record_created_time |
| 306 | self.start_time = start_time | 306 | self.start_time = start_time |
| 307 | self.production_type = production_type # a 'ProductionType' object | 307 | self.production_type = production_type # a 'ProductionType' object |
| 308 | self.bidding_area = bidding_area # a 'BiddingArea' object | 308 | self.bidding_area = bidding_area # a 'BiddingArea' object |
| 309 | self.value = value | 309 | self.value = value |
| 310 | 310 | ||
| 311 | def __str__(self): | 311 | def __str__(self): |
| 312 | return ( | 312 | return ( |
| 313 | f"{self.record_created_time} {self.start_time} " | 313 | f"{self.record_created_time} {self.start_time} " |
| 314 | f"{self.production_type} {self.bidding_area} {self.value}" | 314 | f"{self.production_type} {self.bidding_area} {self.value}" |
| 315 | ) | 315 | ) |
| 316 | 316 | ||
| 317 | Base.metadata.create_all(engine) # create tables | 317 | Base.metadata.create_all(engine) # create tables |
| 318 | </code> | 318 | </code> |
| @@ -337,27 +337,27 @@ | |||
| 337 | session.add(bidding_area1) | 337 | session.add(bidding_area1) |
| 338 | 338 | ||
| 339 | session.add_all( | 339 | session.add_all( |
| 340 | [ | 340 | [ |
| 341 | BiddingArea("NO2", "Elspot NO2"), | 341 | BiddingArea("NO2", "Elspot NO2"), |
| 342 | BiddingArea("NO3", "Elspot NO3"), | 342 | BiddingArea("NO3", "Elspot NO3"), |
| 343 | BiddingArea("NO4", "Elspot NO4"), | 343 | BiddingArea("NO4", "Elspot NO4"), |
| 344 | BiddingArea("NO5", "Elspot NO5"), | 344 | BiddingArea("NO5", "Elspot NO5"), |
| 345 | ] | 345 | ] |
| 346 | ) | 346 | ) |
| 347 | 347 | ||
| 348 | production_type_B37 = ProductionType("B37", "Thermal unspecified") | 348 | production_type_B37 = ProductionType("B37", "Thermal unspecified") |
| 349 | production_type_B30 = ProductionType("B30", "Wind unspecified") | 349 | production_type_B30 = ProductionType("B30", "Wind unspecified") |
| 350 | 350 | ||
| 351 | session.add_all( | 351 | session.add_all( |
| 352 | [ | 352 | [ |
| 353 | ProductionType("B19", "Wind Onshore"), | 353 | ProductionType("B19", "Wind Onshore"), |
| 354 | ProductionType("B10", "Hydro-electric pure pumped storage head installation"), | 354 | ProductionType("B10", "Hydro-electric pure pumped storage head installation"), |
| 355 | ProductionType("B11", "Hydro Run-of-river head installation"), | 355 | ProductionType("B11", "Hydro Run-of-river head installation"), |
| 356 | ProductionType("B12", "Hydro-electric storage head installation"), | 356 | ProductionType("B12", "Hydro-electric storage head installation"), |
| 357 | ProductionType("A04", "Generation"), | 357 | ProductionType("A04", "Generation"), |
| 358 | production_type_B37, | 358 | production_type_B37, |
| 359 | production_type_B30, | 359 | production_type_B30, |
| 360 | ] | 360 | ] |
| 361 | ) | 361 | ) |
| 362 | </code> | 362 | </code> |
| 363 | </pre> | 363 | </pre> |
| @@ -371,21 +371,21 @@ | |||
| 371 | # adding some production plans... | 371 | # adding some production plans... |
| 372 | 372 | ||
| 373 | session.add( | 373 | session.add( |
| 374 | ProductionPlan( | 374 | ProductionPlan( |
| 375 | datetime.datetime.now(), | 375 | datetime.datetime.now(), |
| 376 | datetime.datetime(2022, 11, 2, 1, 0), | 376 | datetime.datetime(2022, 11, 2, 1, 0), |
| 377 | production_type_B37, | 377 | production_type_B37, |
| 378 | bidding_area1, | 378 | bidding_area1, |
| 379 | decimal.Decimal("80.5"), | 379 | decimal.Decimal("80.5"), |
| 380 | ) | 380 | ) |
| 381 | ) | 381 | ) |
| 382 | 382 | ||
| 383 | production_plan2 = ProductionPlan( | 383 | production_plan2 = ProductionPlan( |
| 384 | datetime.datetime.now(), | 384 | datetime.datetime.now(), |
| 385 | datetime.datetime(2022, 11, 2, 2, 0), | 385 | datetime.datetime(2022, 11, 2, 2, 0), |
| 386 | production_type_B37, | 386 | production_type_B37, |
| 387 | bidding_area1, | 387 | bidding_area1, |
| 388 | decimal.Decimal("90.5"), | 388 | decimal.Decimal("90.5"), |
| 389 | ) | 389 | ) |
| 390 | 390 | ||
| 391 | session.add(production_plan2) | 391 | session.add(production_plan2) |
| @@ -427,17 +427,17 @@ | |||
| 427 | 427 | ||
| 428 | # return production plans with production type 'B37' | 428 | # return production plans with production type 'B37' |
| 429 | session.query(ProductionPlan).filter( | 429 | session.query(ProductionPlan).filter( |
| 430 | ProductionPlan.production_type_id == ProductionType.production_type_id | 430 | ProductionPlan.production_type_id == ProductionType.production_type_id |
| 431 | ).filter(ProductionType.code == "B37").all() | 431 | ).filter(ProductionType.code == "B37").all() |
| 432 | session.query(ProductionPlan).join(ProductionType).filter(ProductionType.code == "B37").all() | 432 | session.query(ProductionPlan).join(ProductionType).filter(ProductionType.code == "B37").all() |
| 433 | session.query(ProductionPlan).filter(ProductionPlan.production_type == production_type_B37).all() | 433 | session.query(ProductionPlan).filter(ProductionPlan.production_type == production_type_B37).all() |
| 434 | session.query( | 434 | session.query( |
| 435 | ProductionPlan | 435 | ProductionPlan |
| 436 | ).from_statement( | 436 | ).from_statement( |
| 437 | text( | 437 | text( |
| 438 | "SELECT pp.* FROM production_plans pp, production_types pt " | 438 | "SELECT pp.* FROM production_plans pp, production_types pt " |
| 439 | "WHERE pp.production_type_id = pt.production_type_id AND pt.code=:code" | 439 | "WHERE pp.production_type_id = pt.production_type_id AND pt.code=:code" |
| 440 | ) | 440 | ) |
| 441 | ).params(code="B37").all() | 441 | ).params(code="B37").all() |
| 442 | </code> | 442 | </code> |
| 443 | </pre> | 443 | </pre> |
| @@ -449,29 +449,28 @@ | |||
| 449 | 449 | ||
| 450 | </div> | 450 | </div> |
| 451 | </div> | 451 | </div> |
| 452 | |||
| 453 | <script src="dist/reveal.js"></script> | ||
| 454 | <script src="plugin/zoom/zoom.js"></script> | ||
| 455 | <script src="plugin/notes/notes.js"></script> | ||
| 456 | <script src="plugin/search/search.js"></script> | ||
| 457 | <script src="plugin/markdown/markdown.js"></script> | ||
| 458 | <script src="plugin/highlight/highlight.js"></script> | ||
| 459 | <script> | ||
| 460 | 452 | ||
| 461 | // Also available as an ES module, see: | 453 | <script src="dist/reveal.js"></script> |
| 462 | // https://revealjs.netlify.app/initialization/ | 454 | <script src="plugin/zoom/zoom.js"></script> |
| 463 | Reveal.initialize({ | 455 | <script src="plugin/notes/notes.js"></script> |
| 464 | controls: true, | 456 | <script src="plugin/search/search.js"></script> |
| 465 | progress: true, | 457 | <script src="plugin/markdown/markdown.js"></script> |
| 466 | center: true, | 458 | <script src="plugin/highlight/highlight.js"></script> |
| 467 | hash: true, | 459 | <script> |
| 460 | |||
| 461 | // Also available as an ES module, see: | ||
| 462 | // https://revealjs.com/initialization/ | ||
| 463 | Reveal.initialize({ | ||
| 464 | controls: true, | ||
| 465 | progress: true, | ||
| 466 | center: true, | ||
| 467 | hash: true, | ||
| 468 | 468 | ||
| 469 | // Learn about plugins: https://revealjs.netlify.app/plugins/ | 469 | // Learn about plugins: https://revealjs.com/plugins/ |
| 470 | plugins: [ RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight ] | 470 | plugins: [ RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight ] |
| 471 | }); | 471 | }); |
| 472 | Reveal.configure({ pdfSeparateFragments: false }); | ||
| 473 | 472 | ||
| 474 | </script> | 473 | </script> |
| 475 | 474 | ||
| 476 | </body> | 475 | </body> |
| 477 | </html> | 476 | </html> |
diff --git a/reveal.js/timetravel.html b/reveal.js/timetravel.html index e073f92..b297e2f 100644 --- a/reveal.js/timetravel.html +++ b/reveal.js/timetravel.html | |||
| @@ -1,127 +1,127 @@ | |||
| 1 | <!doctype html> | 1 | <!doctype html> |
| 2 | <html lang="en"> | 2 | <html lang="en"> |
| 3 | <head> | 3 | <head> |
| 4 | <meta charset="utf-8"> | 4 | <meta charset="utf-8"> |
| 5 | <title>Solving problems using time travel</title> | 5 | <title>Solving problems using time travel</title> |
| 6 | <meta name="author" content="Simeon Simeonov"> | 6 | <meta name="author" content="Simeon Simeonov"> |
| 7 | <meta name="apple-mobile-web-app-capable" content="yes"> | 7 | <meta name="apple-mobile-web-app-capable" content="yes"> |
| 8 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | 8 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> |
| 9 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | 9 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 10 | 10 | ||
| 11 | <link rel="stylesheet" href="dist/reset.css"> | 11 | <link rel="stylesheet" href="dist/reset.css"> |
| 12 | <link rel="stylesheet" href="dist/reveal.css"> | 12 | <link rel="stylesheet" href="dist/reveal.css"> |
| 13 | 13 | ||
| 14 | <link rel="stylesheet" href="dist/theme/fifty.css" id="theme"> | 14 | <link rel="stylesheet" href="dist/theme/fifty.css" id="theme"> |
| 15 | 15 | ||
| 16 | <!-- Theme used for syntax highlighting of code --> | 16 | <!-- Theme used for syntax highlighting of code --> |
| 17 | <link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme"> | 17 | <link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme"> |
| 18 | <!-- <link rel="stylesheet" href="plugin/highlight/zenburn.css" id="highlight-theme"> --> | 18 | <!-- <link rel="stylesheet" href="plugin/highlight/zenburn.css" id="highlight-theme"> --> |
| 19 | </head> | 19 | </head> |
| 20 | <body> | 20 | <body> |
| 21 | <div class="reveal"> | 21 | <div class="reveal"> |
| 22 | 22 | ||
| 23 | <!-- Any section element inside of this container is displayed as a slide --> | 23 | <!-- Any section element inside of this container is displayed as a slide --> |
| 24 | <div class="slides"> | 24 | <div class="slides"> |
| 25 | 25 | ||
| 26 | <section> | 26 | <section> |
| 27 | <h2>Solving problems using <em>time travel</em></h2> | 27 | <h2>Solving problems using <em>time travel</em></h2> |
| 28 | <h4>Beryl @ Fifty</h4> | 28 | <h4>Beryl @ Fifty</h4> |
| 29 | </br> | 29 | </br> |
| 30 | <p><small>Tomas Robertson (team ACE-OL) & Simeon Simeonov (team Forecasts)</small></p> | 30 | <p><small>Tomas Robertson (team ACE-OL) & Simeon Simeonov (team Forecasts)</small></p> |
| 31 | </section> | 31 | </section> |
| 32 | 32 | ||
| 33 | 33 | ||
| 34 | <section> | 34 | <section> |
| 35 | <h2>What is <em>time travel</em>???</h2> | 35 | <h2>What is <em>time travel</em>???</h2> |
| 36 | <br/> | 36 | <br/> |
| 37 | <p><em>Time travel</em> - our ability to look at our input data at the state it was at a specific point in time (not only at its last state).</p> | 37 | <p><em>Time travel</em> - our ability to look at our input data at the state it was at a specific point in time (not only at its last state).</p> |
| 38 | <p><em>Time travel</em> is achieved by adding the <em>record_created_time</em> column to our DB tables and storing Kafka's <em>created_time</em> value (converted to UTC).</p> | 38 | <p><em>Time travel</em> is achieved by adding the <em>record_created_time</em> column to our DB tables and storing Kafka's <em>created_time</em> value (converted to UTC).</p> |
| 39 | <br/> | 39 | <br/> |
| 40 | <p>The main concepts around how and why were presented by Peter Sandberg.</p> | 40 | <p>The main concepts around how and why were presented by Peter Sandberg.</p> |
| 41 | </section> | 41 | </section> |
| 42 | 42 | ||
| 43 | <section> | 43 | <section> |
| 44 | <h2>Solving problems</h2> | 44 | <h2>Solving problems</h2> |
| 45 | <br/> | 45 | <br/> |
| 46 | <p>The following spike was observed 2021-10-06 around 10:15 CET @ NO4</p> | 46 | <p>The following spike was observed 2021-10-06 around 10:15 CET @ NO4</p> |
| 47 | <img src="images/timetravel/ace_ol_spike.png"></img> | 47 | <img src="images/timetravel/ace_ol_spike.png"></img> |
| 48 | </section> | 48 | </section> |
| 49 | 49 | ||
| 50 | <section> | 50 | <section> |
| 51 | <h2>Solving problems (cont)</h2> | 51 | <h2>Solving problems (cont)</h2> |
| 52 | <br/> | 52 | <br/> |
| 53 | <p>No spikes shown in Grafana</p> | 53 | <p>No spikes shown in Grafana</p> |
| 54 | <img src="images/timetravel/ace_ol_spike_grafana.png"></img> | 54 | <img src="images/timetravel/ace_ol_spike_grafana.png"></img> |
| 55 | </section> | 55 | </section> |
| 56 | 56 | ||
| 57 | <section> | 57 | <section> |
| 58 | <h2>Solving problems with <em>time travel</em></h2> | 58 | <h2>Solving problems with <em>time travel</em></h2> |
| 59 | <br/> | 59 | <br/> |
| 60 | <pre data-id="code-animation"> | 60 | <pre data-id="code-animation"> |
| 61 | <code class="sql" data-trim type="text/template"> | 61 | <code class="sql" data-trim type="text/template"> |
| 62 | SELECT record_created_time, start_time, value | 62 | SELECT record_created_time, start_time, value |
| 63 | FROM misc.app_odin_ace_ol_ba_10s_avro_v01 | 63 | FROM misc.app_odin_ace_ol_ba_10s_avro_v01 |
| 64 | WHERE bidding_area_name = 'NO4' AND start_time = '2021-10-06 08:14:10' | 64 | WHERE bidding_area_name = 'NO4' AND start_time = '2021-10-06 08:14:10' |
| 65 | ORDER BY record_created_time; | 65 | ORDER BY record_created_time; |
| 66 | </code> | 66 | </code> |
| 67 | </pre> | 67 | </pre> |
| 68 | 68 | ||
| 69 | <table> | 69 | <table> |
| 70 | <thead> | 70 | <thead> |
| 71 | <tr> | 71 | <tr> |
| 72 | <th>record_created_time</th> | 72 | <th>record_created_time</th> |
| 73 | <th>start_time</th> | 73 | <th>start_time</th> |
| 74 | <th>value</th> | 74 | <th>value</th> |
| 75 | </tr> | 75 | </tr> |
| 76 | </thead> | 76 | </thead> |
| 77 | <tbody> | 77 | <tbody> |
| 78 | <tr> | 78 | <tr> |
| 79 | <td>2021-10-06 08:15:52.258</td> | 79 | <td>2021-10-06 08:15:52.258</td> |
| 80 | <td>2021-10-06 08:14:10</td> | 80 | <td>2021-10-06 08:14:10</td> |
| 81 | <td>-370.37683609008127</td> | 81 | <td>-370.37683609008127</td> |
| 82 | </tr> | 82 | </tr> |
| 83 | <tr> | 83 | <tr> |
| 84 | <td>2021-10-06 08:19:06.222</td> | 84 | <td>2021-10-06 08:19:06.222</td> |
| 85 | <td>2021-10-06 08:14:10</td> | 85 | <td>2021-10-06 08:14:10</td> |
| 86 | <td>25.183745117193457</td> | 86 | <td>25.183745117193457</td> |
| 87 | </tr> | 87 | </tr> |
| 88 | <tr> | 88 | <tr> |
| 89 | <td>2021-10-06 08:21:56.207</td> | 89 | <td>2021-10-06 08:21:56.207</td> |
| 90 | <td>2021-10-06 08:14:10</td> | 90 | <td>2021-10-06 08:14:10</td> |
| 91 | <td>34.78731922151518</td> | 91 | <td>34.78731922151518</td> |
| 92 | </tr> | 92 | </tr> |
| 93 | </tbody> | 93 | </tbody> |
| 94 | </table> | 94 | </table> |
| 95 | </section> | 95 | </section> |
| 96 | 96 | ||
| 97 | <section> | 97 | <section> |
| 98 | <h1>Q & A</h1> | 98 | <h1>Q & A</h1> |
| 99 | </section> | 99 | </section> |
| 100 | 100 | ||
| 101 | </div> | 101 | </div> |
| 102 | </div> | 102 | </div> |
| 103 | |||
| 104 | <script src="dist/reveal.js"></script> | ||
| 105 | <script src="plugin/zoom/zoom.js"></script> | ||
| 106 | <script src="plugin/notes/notes.js"></script> | ||
| 107 | <script src="plugin/search/search.js"></script> | ||
| 108 | <script src="plugin/markdown/markdown.js"></script> | ||
| 109 | <script src="plugin/highlight/highlight.js"></script> | ||
| 110 | <script> | ||
| 111 | 103 | ||
| 112 | // Also available as an ES module, see: | 104 | <script src="dist/reveal.js"></script> |
| 113 | // https://revealjs.netlify.app/initialization/ | 105 | <script src="plugin/zoom/zoom.js"></script> |
| 114 | Reveal.initialize({ | 106 | <script src="plugin/notes/notes.js"></script> |
| 115 | controls: true, | 107 | <script src="plugin/search/search.js"></script> |
| 116 | progress: true, | 108 | <script src="plugin/markdown/markdown.js"></script> |
| 117 | center: true, | 109 | <script src="plugin/highlight/highlight.js"></script> |
| 118 | hash: true, | 110 | <script> |
| 119 | 111 | ||
| 120 | // Learn about plugins: https://revealjs.netlify.app/plugins/ | 112 | // Also available as an ES module, see: |
| 121 | plugins: [ RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight ] | 113 | // https://revealjs.com/initialization/ |
| 122 | }); | 114 | Reveal.initialize({ |
| 123 | 115 | controls: true, | |
| 124 | </script> | 116 | progress: true, |
| 117 | center: true, | ||
| 118 | hash: true, | ||
| 125 | 119 | ||
| 126 | </body> | 120 | // Learn about plugins: https://revealjs.com/plugins/ |
| 121 | plugins: [ RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight ] | ||
| 122 | }); | ||
| 123 | |||
| 124 | </script> | ||
| 125 | |||
| 126 | </body> | ||
| 127 | </html> | 127 | </html> |
