commit af7609b5765d1f4a294ebec7e0997262ae9f057d
Author: Tomáš Ballák <ballak.tomas@protonmail.com>
Date:   Tue Jan 12 22:35:17 2021 +0100

    Re #8193 - refactoring crawler

diff --git a/modules/crawler/DatasetCrawler/JIS_crawler.py b/modules/crawler/DatasetCrawler/JIS_crawler.py
index 15f3599..f03a1bc 100644
--- a/modules/crawler/DatasetCrawler/JIS_crawler.py
+++ b/modules/crawler/DatasetCrawler/JIS_crawler.py
@@ -1,11 +1,11 @@
 from Utilities import folder_processor
 from Utilities.Crawler import basic_crawler_functions
-
+from shared_types import ConfigType
 # Path to crawled data
 CRAWLED_DATA_PATH = "CrawledData/"
 
 
-def crawl(config):
+def crawl(config: ConfigType):
     """
     Implement crawl method that downloads new data to path_for_files
     For keeping the project structure
@@ -21,20 +21,25 @@ def crawl(config):
     path_for_files = CRAWLED_DATA_PATH + dataset_name + '/'
 
     first_level_links = basic_crawler_functions.get_all_links(url)
-    filtered_first_level_links = basic_crawler_functions.filter_links(first_level_links, "^OD_ZCU")
-    absolute_first_level_links = basic_crawler_functions.create_absolute_links(filtered_first_level_links, url)
+    filtered_first_level_links = basic_crawler_functions.filter_links(
+        first_level_links, "^OD_ZCU")
+    absolute_first_level_links = basic_crawler_functions.create_absolute_links(
+        filtered_first_level_links, url)
 
     files = []
 
     for link in absolute_first_level_links:
         second_level_links = basic_crawler_functions.get_all_links(link)
-        filtered_second_level_links = basic_crawler_functions.filter_links(second_level_links, regex)
-        absolute_second_level_links = basic_crawler_functions.create_absolute_links(filtered_second_level_links, link)
+        filtered_second_level_links = basic_crawler_functions.filter_links(
+            second_level_links, regex)
+        absolute_second_level_links = basic_crawler_functions.create_absolute_links(
+            filtered_second_level_links, link)
 
         for file_link in absolute_second_level_links:
             files.append(file_link)
 
-    files = basic_crawler_functions.remove_downloaded_links(files, dataset_name)
+    files = basic_crawler_functions.remove_downloaded_links(
+        files, dataset_name)
 
     for file in files:
         basic_crawler_functions.download_file_from_url(file, dataset_name)
diff --git a/modules/crawler/DatasetCrawler/KOLOBEZKY_crawler.py b/modules/crawler/DatasetCrawler/KOLOBEZKY_crawler.py
index 6851240..9b1bc9e 100644
--- a/modules/crawler/DatasetCrawler/KOLOBEZKY_crawler.py
+++ b/modules/crawler/DatasetCrawler/KOLOBEZKY_crawler.py
@@ -1,11 +1,11 @@
 from Utilities import folder_processor
 from Utilities.Crawler import basic_crawler_functions
-
+from shared_types import ConfigType
 # Path to crawled data
 CRAWLED_DATA_PATH = "CrawledData/"
 
 
-def crawl(config):
+def crawl(config: ConfigType):
     """
     Implement crawl method that downloads new data to path_for_files
     For keeping the project structure
@@ -21,20 +21,25 @@ def crawl(config):
     path_for_files = CRAWLED_DATA_PATH + dataset_name + '/'
 
     first_level_links = basic_crawler_functions.get_all_links(url)
-    filtered_first_level_links = basic_crawler_functions.filter_links(first_level_links, "^OD_ZCU")
-    absolute_first_level_links = basic_crawler_functions.create_absolute_links(filtered_first_level_links, url)
+    filtered_first_level_links = basic_crawler_functions.filter_links(
+        first_level_links, "^OD_ZCU")
+    absolute_first_level_links = basic_crawler_functions.create_absolute_links(
+        filtered_first_level_links, url)
 
     files = []
 
     for link in absolute_first_level_links:
         second_level_links = basic_crawler_functions.get_all_links(link)
-        filtered_second_level_links = basic_crawler_functions.filter_links(second_level_links, regex)
-        absolute_second_level_links = basic_crawler_functions.create_absolute_links(filtered_second_level_links, link)
+        filtered_second_level_links = basic_crawler_functions.filter_links(
+            second_level_links, regex)
+        absolute_second_level_links = basic_crawler_functions.create_absolute_links(
+            filtered_second_level_links, link)
 
         for file_link in absolute_second_level_links:
             files.append(file_link)
 
-    files = basic_crawler_functions.remove_downloaded_links(files, dataset_name)
+    files = basic_crawler_functions.remove_downloaded_links(
+        files, dataset_name)
 
     for file in files:
         basic_crawler_functions.download_file_from_url(file, dataset_name)
diff --git a/modules/crawler/DatasetCrawler/OBSAZENIMISTNOSTI_crawler.py b/modules/crawler/DatasetCrawler/OBSAZENIMISTNOSTI_crawler.py
index f405f28..d8575b3 100644
--- a/modules/crawler/DatasetCrawler/OBSAZENIMISTNOSTI_crawler.py
+++ b/modules/crawler/DatasetCrawler/OBSAZENIMISTNOSTI_crawler.py
@@ -1,11 +1,12 @@
 from Utilities import folder_processor
 from Utilities.Crawler import basic_crawler_functions
+from shared_types import ConfigType
 
 # Path to crawled data
 CRAWLED_DATA_PATH = "CrawledData/"
 
 
-def crawl(config):
+def crawl(config: ConfigType):
     """
     Implement crawl method that downloads new data to path_for_files
     For keeping the project structure
diff --git a/modules/crawler/DatasetCrawler/WIFI_crawler.py b/modules/crawler/DatasetCrawler/WIFI_crawler.py
index 6851240..40e0576 100644
--- a/modules/crawler/DatasetCrawler/WIFI_crawler.py
+++ b/modules/crawler/DatasetCrawler/WIFI_crawler.py
@@ -1,11 +1,12 @@
 from Utilities import folder_processor
 from Utilities.Crawler import basic_crawler_functions
+from shared_types import ConfigType
 
 # Path to crawled data
 CRAWLED_DATA_PATH = "CrawledData/"
 
 
-def crawl(config):
+def crawl(config: ConfigType):
     """
     Implement crawl method that downloads new data to path_for_files
     For keeping the project structure
@@ -21,20 +22,25 @@ def crawl(config):
     path_for_files = CRAWLED_DATA_PATH + dataset_name + '/'
 
     first_level_links = basic_crawler_functions.get_all_links(url)
-    filtered_first_level_links = basic_crawler_functions.filter_links(first_level_links, "^OD_ZCU")
-    absolute_first_level_links = basic_crawler_functions.create_absolute_links(filtered_first_level_links, url)
+    filtered_first_level_links = basic_crawler_functions.filter_links(
+        first_level_links, "^OD_ZCU")
+    absolute_first_level_links = basic_crawler_functions.create_absolute_links(
+        filtered_first_level_links, url)
 
     files = []
 
     for link in absolute_first_level_links:
         second_level_links = basic_crawler_functions.get_all_links(link)
-        filtered_second_level_links = basic_crawler_functions.filter_links(second_level_links, regex)
-        absolute_second_level_links = basic_crawler_functions.create_absolute_links(filtered_second_level_links, link)
+        filtered_second_level_links = basic_crawler_functions.filter_links(
+            second_level_links, regex)
+        absolute_second_level_links = basic_crawler_functions.create_absolute_links(
+            filtered_second_level_links, link)
 
         for file_link in absolute_second_level_links:
             files.append(file_link)
 
-    files = basic_crawler_functions.remove_downloaded_links(files, dataset_name)
+    files = basic_crawler_functions.remove_downloaded_links(
+        files, dataset_name)
 
     for file in files:
         basic_crawler_functions.download_file_from_url(file, dataset_name)
diff --git a/modules/crawler/DatasetProcessing/JIS_processor.py b/modules/crawler/DatasetProcessing/JIS_processor.py
index c141564..faa6cf3 100644
--- a/modules/crawler/DatasetProcessing/JIS_processor.py
+++ b/modules/crawler/DatasetProcessing/JIS_processor.py
@@ -1,8 +1,10 @@
 from Utilities.CSV import csv_data_line
 from Utilities import date_formating
 
+from shared_types import DateDict
 
-def process_file(filename):
+
+def process_file(filename: str) -> DateDict:
     """
     Method that take path to crawled file and outputs date dictionary:
     Date dictionary is a dictionary where keys are dates in format YYYY-mm-dd-hh (2018-04-08-15)
@@ -16,7 +18,7 @@ def process_file(filename):
     None if not implemented
     date_dict when implemented
     """
-    date_dict = dict()
+    date_dict = {}
 
     with open(filename, "r", encoding="utf-8") as file:
 
@@ -29,12 +31,12 @@ def process_file(filename):
             occurrence = array[2][:-1]
 
             if date not in date_dict:
-                date_dict[date] = dict()
+                date_dict[date] = {}
 
             if name in date_dict[date]:
                 date_dict[date][name].occurrence += int(occurrence)
             else:
-                date_dict[date][name] = csv_data_line.CSVDataLine(name, date, occurrence)
+                date_dict[date][name] = csv_data_line.CSVDataLine(
+                    name, date, occurrence)
 
     return date_dict
-
diff --git a/modules/crawler/DatasetProcessing/KOLOBEZKY_processor.py b/modules/crawler/DatasetProcessing/KOLOBEZKY_processor.py
index ac449dc..7539790 100644
--- a/modules/crawler/DatasetProcessing/KOLOBEZKY_processor.py
+++ b/modules/crawler/DatasetProcessing/KOLOBEZKY_processor.py
@@ -1,8 +1,10 @@
 from Utilities.CSV import csv_data_line
 from Utilities import date_formating
 
+from shared_types import DateDict
 
-def process_file(filename):
+
+def process_file(filename: str) -> DateDict:
     """
     Method that take path to crawled file and outputs date dictionary:
     Date dictionary is a dictionary where keys are dates in format YYYY-mm-dd-hh (2018-04-08-15)
@@ -16,7 +18,7 @@ def process_file(filename):
     None if not implemented
     date_dict when implemented
     """
-    date_dict = dict()
+    date_dict = {}
 
     with open(filename, "r") as file:
 
@@ -28,12 +30,12 @@ def process_file(filename):
             name = array[1][1:-1]
 
             if date not in date_dict:
-                date_dict[date] = dict()
+                date_dict[date] = {}
 
             if name in date_dict[date]:
                 date_dict[date][name].occurrence += 1
             else:
-                date_dict[date][name] = csv_data_line.CSVDataLine(name, date, 1)
+                date_dict[date][name] = csv_data_line.CSVDataLine(
+                    name, date, 1)
 
     return date_dict
-
diff --git a/modules/crawler/DatasetProcessing/OBSAZENIMISTNOSTI_processor.py b/modules/crawler/DatasetProcessing/OBSAZENIMISTNOSTI_processor.py
index d457c62..790b9fb 100644
--- a/modules/crawler/DatasetProcessing/OBSAZENIMISTNOSTI_processor.py
+++ b/modules/crawler/DatasetProcessing/OBSAZENIMISTNOSTI_processor.py
@@ -5,13 +5,15 @@ from datetime import date
 import time
 import datetime
 
+from shared_types import DateDict
+
 logging.basicConfig(filename='../../CrawlerLogs' + 'Crawlerlog-' +
                     date.today().strftime("%b-%Y") + '.log',
                     level=logging.INFO,
                     format='%(asctime)s %(message)s')
 
 
-def process_file(filename):
+def process_file(filename: str) -> DateDict:
     """
     Method that take path to crawled file and outputs date dictionary:
     Date dictionary is a dictionary where keys are dates in format YYYY-mm-dd-hh (2018-04-08-15)
@@ -25,7 +27,7 @@ def process_file(filename):
     None if not implemented
     date_dict when implemented
     """
-    date_dict = dict()
+    date_dict = {}
 
     with open(filename, "r") as file:
 
diff --git a/modules/crawler/DatasetProcessing/WIFI_processor.py b/modules/crawler/DatasetProcessing/WIFI_processor.py
index bb027b3..7448fed 100644
--- a/modules/crawler/DatasetProcessing/WIFI_processor.py
+++ b/modules/crawler/DatasetProcessing/WIFI_processor.py
@@ -1,8 +1,9 @@
 from Utilities.CSV import csv_data_line
 from Utilities import date_formating
+from shared_types import DateDict
 
 
-def process_file(filename):
+def process_file(filename: str) -> DateDict:
     """
     Method that take path to crawled file and outputs date dictionary:
     Date dictionary is a dictionary where keys are dates in format YYYY-mm-dd-hh (2018-04-08-15)
diff --git a/modules/crawler/Utilities/CSV/csv_data_line.py b/modules/crawler/Utilities/CSV/csv_data_line.py
index 90f15e7..2cdf146 100644
--- a/modules/crawler/Utilities/CSV/csv_data_line.py
+++ b/modules/crawler/Utilities/CSV/csv_data_line.py
@@ -3,20 +3,18 @@ class CSVDataLine:
     Class that specifies the look of data line in processed csv file
     prepared for database
     """
-
-    def __init__(self, name, date, occurrence):
+    def __init__(self, name: str, date: str, occurrence: int) -> None:
         try:
             test_val = int(occurrence)
         except ValueError:
             print("Occurence should be and integer value!")
 
         if len(date) != 13:
-            raise ValueError("Invalid date format YYYY-dd-mm-hh expected!")    
+            raise ValueError("Invalid date format YYYY-dd-mm-hh expected!")
 
         self.name = name
         self.date = date
         self.occurrence = test_val
 
-    def to_csv(self):
+    def to_csv(self) -> str:
         return self.name + ";" + str(self.occurrence) + ";" + self.date
-
diff --git a/modules/crawler/Utilities/CSV/csv_utils.py b/modules/crawler/Utilities/CSV/csv_utils.py
index 2865e38..adcf4cd 100644
--- a/modules/crawler/Utilities/CSV/csv_utils.py
+++ b/modules/crawler/Utilities/CSV/csv_utils.py
@@ -1,11 +1,13 @@
 import inspect
+from shared_types import StringSetType
 from Utilities.CSV import csv_data_line
 
 # Path to processed data
 PROCESSED_DATA_PATH = "ProcessedData/"
 
 
-def get_unique_names_from_file(filename, column_number):
+def get_unique_names_from_file(filename: str,
+                               column_number: int) -> StringSetType:
     """
         Extract set of unique names from file
     Args:
@@ -29,7 +31,7 @@ def get_unique_names_from_file(filename, column_number):
     return name_set
 
 
-def export_data_to_csv(filename, data_dict):
+def export_data_to_csv(filename: str, data_dict) -> None:
     """
         Takes data_dict and export it into a csv file
     Args:
@@ -40,9 +42,12 @@ def export_data_to_csv(filename, data_dict):
 
         for date in data_dict:
             if len(date) != 13:
-                raise ValueError("Invalid date format for key value --> YYYY-mm-dd-hh expected!")   
+                raise ValueError(
+                    "Invalid date format for key value --> YYYY-mm-dd-hh expected!"
+                )
             for data in data_dict[date]:
                 csv_line = data_dict[date][data]
-                if not isinstance(csv_line,csv_data_line.CSVDataLine):
-                    raise ValueError("data_dict is expected to have CSVDataLine as values")
+                if not isinstance(csv_line, csv_data_line.CSVDataLine):
+                    raise ValueError(
+                        "data_dict is expected to have CSVDataLine as values")
                 file.write(csv_line.to_csv() + '\n')
\ No newline at end of file
diff --git a/modules/crawler/Utilities/Crawler/basic_crawler_functions.py b/modules/crawler/Utilities/Crawler/basic_crawler_functions.py
index 9099b69..d14a4a3 100644
--- a/modules/crawler/Utilities/Crawler/basic_crawler_functions.py
+++ b/modules/crawler/Utilities/Crawler/basic_crawler_functions.py
@@ -3,14 +3,16 @@ import re
 from Utilities import folder_processor
 from Utilities.Database import database_record_logs
 from bs4 import BeautifulSoup
+from typing import List
 
 # Path to crawler logs
 CRAWLER_LOGS_PATH = "CrawlerLogs/"
 # Path to crawled data
 CRAWLED_DATA_PATH = "CrawledData/"
+LinksType = List[str]
 
 
-def get_all_links(url):
+def get_all_links(url: str) -> LinksType:
     """
     Sends http request to url, downloads all data,
     extract links
@@ -34,7 +36,7 @@ def get_all_links(url):
     return links
 
 
-def filter_links(links, regex):
+def filter_links(links: LinksType, regex: str) -> LinksType:
     """
     Filters list of links using regex
 
@@ -54,7 +56,7 @@ def filter_links(links, regex):
     return filtered_links
 
 
-def create_absolute_links(links, archive):
+def create_absolute_links(links: LinksType, archive: str) -> LinksType:
     """
         Appends archive path to every link in links
     Args:
@@ -72,7 +74,7 @@ def create_absolute_links(links, archive):
     return absolute_links
 
 
-def remove_downloaded_links(links, dataset_name):
+def remove_downloaded_links(links: LinksType, dataset_name: str) -> LinksType:
     """
     Loads already downloaded links from CRAWLER_LOGS_PATH ignore.txt
     Args:
@@ -88,7 +90,7 @@ def remove_downloaded_links(links, dataset_name):
     return final_links
 
 
-def download_file_from_url(url, dataset_name):
+def download_file_from_url(url: str, dataset_name: str) -> None:
     """
     Downloads file on provided url and saves it to path
     Args:
diff --git a/modules/crawler/Utilities/Database/database_data_line.py b/modules/crawler/Utilities/Database/database_data_line.py
index 47e53d8..3912e54 100644
--- a/modules/crawler/Utilities/Database/database_data_line.py
+++ b/modules/crawler/Utilities/Database/database_data_line.py
@@ -1,14 +1,23 @@
+from typing import Dict
+
+
 class DatabaseDataLine:
     """
     Class that specifies the look of data line in database
     """
-    def __init__(self, name, longitude, latitude, date, occurrence):
+    def __init__(self, name: str, longitude: float, latitude: float, date: str,
+                 occurrence: int):
         self.name = name
         self.latitude = latitude
         self.longitude = longitude
         self.date = date
         self.occurrence = occurrence
 
-    def to_dictionary(self):
-        return {"place": self.name, "x": self.longitude, "y": self.latitude, "number": self.occurrence,
-                "date": self.date}
+    def to_dictionary(self) -> Dict[str, any]:
+        return {
+            "place": self.name,
+            "x": self.longitude,
+            "y": self.latitude,
+            "number": self.occurrence,
+            "date": self.date
+        }
diff --git a/modules/crawler/Utilities/Database/database_loader.py b/modules/crawler/Utilities/Database/database_loader.py
index c48d6ce..e53a3b8 100644
--- a/modules/crawler/Utilities/Database/database_loader.py
+++ b/modules/crawler/Utilities/Database/database_loader.py
@@ -1,6 +1,8 @@
 from Utilities.Database import database_data_line, database_record_logs
 from Utilities import configure_functions
 from Utilities.helpers import should_skip, detect_change
+from shared_types import ConfigType
+from typing import Dict
 import pymongo
 import re
 
@@ -20,8 +22,10 @@ MONGODB_DATASET_DEVICES_COLLECTION = "DEVICES"
 # Path to processed data
 PROCESSED_DATA_PATH = "ProcessedData/"
 
+DatabaseConnectionType = Dict[str, any]
 
-def create_database_connection():
+
+def create_database_connection() -> pymongo.database.Database:
     """
     Creates connection to mongoDB
 
@@ -38,7 +42,7 @@ def create_database_connection():
     return database
 
 
-def get_data_from_file(filename, config):
+def get_data_from_file(filename: str, config: ConfigType) -> Dict[str, any]:
     """
         Opens processed file, reads it line by line
         name, ocurrence, date
@@ -59,7 +63,7 @@ def get_data_from_file(filename, config):
     f = open(dataset_path + filename, "r")
 
     devices = config["devices"]
-    date_dict = dict()
+    date_dict = {}
 
     for line in f:
         line = line[:-1]
@@ -86,8 +90,9 @@ def get_data_from_file(filename, config):
     return date_dict
 
 
-def load_data_to_database(database_connection, dataset_name, data_dic,
-                          file_name):
+def load_data_to_database(database_connection: DatabaseConnectionType,
+                          dataset_name: str, data_dic: Dict[str, any],
+                          file_name: str) -> None:
     """
     Takes data_dic created in method get_data_from_file
     and loads into into database where collection name is dataset_name + data_dic key
@@ -107,7 +112,8 @@ def load_data_to_database(database_connection, dataset_name, data_dic,
         date_dataset.insert_many(data_dic[date])
 
 
-def check_or_update_datasets_collection(database_connection, config):
+def check_or_update_datasets_collection(
+        database_connection: DatabaseConnectionType, config: ConfigType):
     """
     Checks if DATASETS collection contains dataset and if display name was not updated
 
@@ -116,8 +122,7 @@ def check_or_update_datasets_collection(database_connection, config):
         config: loaded configuration file of dataset
     """
     # collection where are specified aviable datasets
-    compareKeys = ['display-name',
-                   'display-color']
+    compareKeys = ['display-name', 'display-color']
     collection_datasets = database_connection[MONGODB_DATASET_COLLECTION]
 
     query = {'key-name': config['dataset-name']}
@@ -139,7 +144,7 @@ def check_or_update_datasets_collection(database_connection, config):
         collection_datasets.update_one(query, {"$set": newVal})
 
 
-def update_devices_collection(config):
+def update_devices_collection(config: ConfigType):
     """
     Checks if there are any changes in devices specified in config file against 
     devices processed and loaded into the database
@@ -164,7 +169,7 @@ def update_devices_collection(config):
 
     devices_cursor = collection_devices.find()
 
-    db_device_dict = dict()
+    db_device_dict = {}
 
     for device in devices_cursor:
         name = device['name']
@@ -208,7 +213,7 @@ def update_devices_collection(config):
     return change_in_devices
 
 
-def remove_dataset_database(dataset_name):
+def remove_dataset_database(dataset_name: str):
     """
     Removes dataset entries from database
     Args:
@@ -221,7 +226,7 @@ def remove_dataset_database(dataset_name):
     collection_datasets = mydb[MONGODB_DATASET_COLLECTION]
 
     collection_datasets.delete_one({"key-name": dataset_name})
-    print("Removing record from DATASETS collection")
+    print("Odstraňování záznamu z DATASETS kolekce")
 
     # Retrieve list of all collections
     collections = mydb.list_collection_names()
@@ -230,10 +235,10 @@ def remove_dataset_database(dataset_name):
     for name in collections:
         if name.startswith(dataset_name):
             mydb[name].drop()
-            print("Dropping: " + name)
+            print("Odstraňuji: " + name)
 
 
-def reset_dataset_database(dataset_name):
+def reset_dataset_database(dataset_name: str):
     """
     Reset dataset in database 
      - delete everything from except crawled links and mention in DATASETS collection
@@ -252,6 +257,6 @@ def reset_dataset_database(dataset_name):
     for name in collections:
         if pattern.match(name):
             mydb[name].drop()
-            print("Dropping: " + name)
+            print("Odstraňuji: " + name)
 
     database_record_logs.reset_ignore_set_loaded(dataset_name)
diff --git a/modules/crawler/Utilities/Database/database_record_logs.py b/modules/crawler/Utilities/Database/database_record_logs.py
index 37503f8..1e172db 100644
--- a/modules/crawler/Utilities/Database/database_record_logs.py
+++ b/modules/crawler/Utilities/Database/database_record_logs.py
@@ -1,5 +1,5 @@
 from Utilities.Database import database_loader
-
+from shared_types import StringSetType
 # mongodb collection with with already downloaded links
 MONGODB_DATASET_LINK_COLLECTION = "LINKS"
 # mongodb collection with with already processed files
@@ -10,7 +10,7 @@ MONGODB_DATASET_LOADED_COLLECTION = "LOADED"
 MONGODB_DATASET_COLLECTION = "DATASETS"
 
 
-def load_ignore_set_links(dataset_name):
+def load_ignore_set_links(dataset_name: str) -> StringSetType:
     """
     Loades from database links of already downloaded files by crawler
     
@@ -32,7 +32,7 @@ def load_ignore_set_links(dataset_name):
     return ignore_set
 
 
-def update_ignore_set_links(dataset_name,link):
+def update_ignore_set_links(dataset_name: str, link: str) -> None:
     """
     Adds links of newly crawled files to the database
     
@@ -44,10 +44,10 @@ def update_ignore_set_links(dataset_name,link):
 
     my_col = connection[dataset_name + MONGODB_DATASET_LINK_COLLECTION]
 
-    my_col.insert({ "name": link})
+    my_col.insert({"name": link})
 
 
-def reset_ignore_set_links(dataset_name):
+def reset_ignore_set_links(dataset_name: str) -> None:
     """
     Drops collection of already downloaded links
     
@@ -62,8 +62,7 @@ def reset_ignore_set_links(dataset_name):
     my_col.drop()
 
 
-
-def load_ignore_set_processed(dataset_name):
+def load_ignore_set_processed(dataset_name: str) -> StringSetType:
     """
     Loads from database set of already processed files
     
@@ -85,7 +84,7 @@ def load_ignore_set_processed(dataset_name):
     return ignore_set
 
 
-def update_ignore_set_processed(dataset_name,filename):
+def update_ignore_set_processed(dataset_name: str, filename: str) -> None:
     """
     Adds files of newly processed files to the database
     
@@ -97,11 +96,10 @@ def update_ignore_set_processed(dataset_name,filename):
 
     my_col = connection[dataset_name + MONGODB_DATASET_PROCESSED_COLLECTION]
 
-    my_col.insert({ "name": filename})
-
+    my_col.insert({"name": filename})
 
 
-def reset_ignore_set_processed(dataset_name):
+def reset_ignore_set_processed(dataset_name: str) -> None:
     """
     Drops collection of already processed files
     
@@ -116,8 +114,7 @@ def reset_ignore_set_processed(dataset_name):
     my_col.drop()
 
 
-
-def load_ignore_set_loaded(dataset_name):
+def load_ignore_set_loaded(dataset_name: str) -> StringSetType:
     """
     Loads from database set of already loaded files in database
     
@@ -139,8 +136,7 @@ def load_ignore_set_loaded(dataset_name):
     return ignore_set
 
 
-
-def update_ignore_set_loaded(dataset_name,filename):
+def update_ignore_set_loaded(dataset_name: str, filename: str) -> None:
     """
     Adds files of newly loaded files to the database
     
@@ -152,10 +148,10 @@ def update_ignore_set_loaded(dataset_name,filename):
 
     my_col = connection[dataset_name + MONGODB_DATASET_LOADED_COLLECTION]
 
-    my_col.insert({ "name": filename})
+    my_col.insert({"name": filename})
 
 
-def reset_ignore_set_loaded(dataset_name):
+def reset_ignore_set_loaded(dataset_name: str) -> None:
     """
     Drops collection of already loaded files
     
@@ -170,7 +166,7 @@ def reset_ignore_set_loaded(dataset_name):
     my_col.drop()
 
 
-def load_updated(dataset_name):
+def load_updated(dataset_name: str) -> int:
     """
     Loads value of (days from last update) from db
     
@@ -184,14 +180,14 @@ def load_updated(dataset_name):
 
     my_col = connection[MONGODB_DATASET_COLLECTION]
 
-    data = my_col.find_one({'key-name': dataset_name},{'updated'})
+    data = my_col.find_one({'key-name': dataset_name}, {'updated'})
 
     updated = int(data['updated'])
 
     return updated
 
 
-def update_updated(dataset_name,value):
+def update_updated(dataset_name: str, value: int):
     """
     Updates value of (days from last update) in db
     
@@ -203,7 +199,7 @@ def update_updated(dataset_name,value):
 
     my_col = connection[MONGODB_DATASET_COLLECTION]
 
-    myquery = { 'key-name': dataset_name }
-    new_values = { "$set": { "updated": value } }
+    myquery = {'key-name': dataset_name}
+    new_values = {"$set": {"updated": value}}
 
-    my_col.update_one(myquery,new_values)
+    my_col.update_one(myquery, new_values)
diff --git a/modules/crawler/Utilities/configure_functions.py b/modules/crawler/Utilities/configure_functions.py
index 27b2615..911b8e7 100644
--- a/modules/crawler/Utilities/configure_functions.py
+++ b/modules/crawler/Utilities/configure_functions.py
@@ -1,5 +1,7 @@
 import yaml
 import os
+from typing import Dict, Set
+from shared_types import StringSetType
 from Utilities.Database import database_record_logs
 from Utilities.helpers import should_skip
 
@@ -9,7 +11,7 @@ CONFIG_FILES_PATH = "DatasetConfigs/"
 CONFIG_FILE_TYPE = ".yaml"
 
 
-def load_configuration(dataset_name):
+def load_configuration(dataset_name: str) -> Dict[str, any]:
     """
     Loads yaml configuration file into memory
 
@@ -22,7 +24,7 @@ def load_configuration(dataset_name):
     with open(CONFIG_FILES_PATH + dataset_name + CONFIG_FILE_TYPE, "r") as f:
         data = yaml.load(f, Loader=yaml.FullLoader)
 
-    devices_dic = dict()
+    devices_dic = {}
 
     if data["devices"] is not None:
         for item in data["devices"]:
@@ -33,7 +35,8 @@ def load_configuration(dataset_name):
     return data
 
 
-def update_configuration(dataset_name, new_devices):
+def update_configuration(dataset_name: str,
+                         new_devices: StringSetType) -> None:
     """
     Open dataset and appends new_devices to the end
 
@@ -53,7 +56,7 @@ def update_configuration(dataset_name, new_devices):
             file.write("\n")
 
 
-def check_if_there_is_a_config_file(dataset_name):
+def check_if_there_is_a_config_file(dataset_name: str) -> bool:
     """
     Goes trough all config files (represeting valid dataset in database)
     and checks if dataset_name is there
@@ -75,7 +78,8 @@ def check_if_there_is_a_config_file(dataset_name):
     return False
 
 
-def return_dictionary_of_valid_devices(devices):
+def return_dictionary_of_valid_devices(
+        devices: Dict[str, any]) -> Dict[str, Dict[str, any]]:
     """
     Iterates over all devices specified in config file
 
@@ -87,7 +91,7 @@ def return_dictionary_of_valid_devices(devices):
     Returns:   
         Dictonary containing only valid devices
     """
-    valid_devices = dict()
+    valid_devices = {}
 
     for device in devices.keys():
         if not should_skip(devices[device]):
diff --git a/modules/crawler/Utilities/date_formating.py b/modules/crawler/Utilities/date_formating.py
index 1ec11cf..100e83e 100644
--- a/modules/crawler/Utilities/date_formating.py
+++ b/modules/crawler/Utilities/date_formating.py
@@ -1,4 +1,4 @@
-def date_formatter(string_date):
+def date_formatter(string_date: str) -> str:
     """
 
     Args:
@@ -21,12 +21,13 @@ def date_formatter(string_date):
 
         string_date = srr
 
-    return_date = string_date[6:10] + '-' + string_date[3:5] + '-' + string_date[:2]
+    return_date = string_date[6:10] + '-' + string_date[
+        3:5] + '-' + string_date[:2]
 
     return return_date
 
 
-def date_time_formatter(string_date):
+def date_time_formatter(string_date: str) -> str:
     """
     Converts one type of date format "dd.mm.yyyy hh.mm.ss" to date format YYYY-mm-dd-hh
     Args:
@@ -49,6 +50,7 @@ def date_time_formatter(string_date):
 
         string_date = srr
 
-    return_date = string_date[6:10] + '-' + string_date[3:5] + '-' + string_date[:2] + '-' + string_date[11:13]
+    return_date = string_date[6:10] + '-' + string_date[
+        3:5] + '-' + string_date[:2] + '-' + string_date[11:13]
 
     return return_date
diff --git a/modules/crawler/Utilities/folder_processor.py b/modules/crawler/Utilities/folder_processor.py
index 93997ac..1fee890 100644
--- a/modules/crawler/Utilities/folder_processor.py
+++ b/modules/crawler/Utilities/folder_processor.py
@@ -1,10 +1,12 @@
 import os
 import zipfile
+from shared_types import ConfigType, StringSetType
 from Utilities.CSV import csv_utils
 from Utilities.Database import database_record_logs
 
 
-def list_of_all_new_files(ignore_set,path):
+def list_of_all_new_files(ignore_set: StringSetType,
+                          path: str) -> StringSetType:
     """
     Get all files from directory and all files written in ignore.txt
     and return the difference
@@ -16,12 +18,10 @@ def list_of_all_new_files(ignore_set,path):
     """
     files_in_dir = os.listdir(path)
 
-
     return set(files_in_dir).difference(ignore_set)
 
 
-
-def get_devices_set(dataset_name,path):
+def get_devices_set(dataset_name: str, path: str) -> StringSetType:
     """
      Goes trough every not loaded file(not contained in ProcessedData/ignore.txt)
      Extracts names from not loaded file which should be in first column
@@ -34,17 +34,19 @@ def get_devices_set(dataset_name,path):
         set of unique names contained in not loaded files
     """
     ignore_set = database_record_logs.load_ignore_set_loaded(dataset_name)
-    files_in_dir = list_of_all_new_files(ignore_set,path)
+    files_in_dir = list_of_all_new_files(ignore_set, path)
 
     unique_names = set()
 
     for file_path in files_in_dir:
-        unique_names.update(csv_utils.get_unique_names_from_file(path+file_path, 0))
+        unique_names.update(
+            csv_utils.get_unique_names_from_file(path + file_path, 0))
 
     return unique_names
 
 
-def get_unknown_devices_set(config, devices):
+def get_unknown_devices_set(config: ConfigType,
+                            devices: StringSetType) -> StringSetType:
     """
     Compares config and devices a return difference
 
@@ -61,7 +63,7 @@ def get_unknown_devices_set(config, devices):
     return unknown_devices_set
 
 
-def unzip_all_csv_zip_files_in_folder(path):
+def unzip_all_csv_zip_files_in_folder(path: str) -> None:
     """
     Load all files from directory and unzip those which end by .zip
     After unziping deletes the zip file
@@ -83,7 +85,7 @@ def unzip_all_csv_zip_files_in_folder(path):
         os.remove(zip_file)
 
 
-def clean_folder(path):
+def clean_folder(path: str) -> None:
     """
     Deletes all files in folder
 
@@ -93,4 +95,4 @@ def clean_folder(path):
     files = os.listdir(path)
 
     for file in files:
-        os.remove(path+file)
+        os.remove(path + file)
diff --git a/modules/crawler/Utilities/helpers.py b/modules/crawler/Utilities/helpers.py
index 4d5e0c7..b15d0d8 100644
--- a/modules/crawler/Utilities/helpers.py
+++ b/modules/crawler/Utilities/helpers.py
@@ -4,12 +4,13 @@ SKIP = "SKIP"
 UNKNOWN = "UNKNOWN!"
 
 
-def should_skip(device) -> bool:
+def should_skip(device: Dict[str, str]) -> bool:
     return device['x'] == SKIP or device['y'] == SKIP or device[
         'x'] == UNKNOWN or device['y'] == UNKNOWN
 
 
-def detect_change(first: Dict[str, str], second: Dict[str, str], compareKeys: [str]) -> bool:
+def detect_change(first: Dict[str, str], second: Dict[str, str],
+                  compareKeys: [str]) -> bool:
     """Detects change between two dictonaries
 
     Args:
diff --git a/modules/crawler/crone_update_script.py b/modules/crawler/crone_update_script.py
index 84a96bc..d37e494 100644
--- a/modules/crawler/crone_update_script.py
+++ b/modules/crawler/crone_update_script.py
@@ -5,7 +5,7 @@ import os
 CONFIG_FILES_PATH = "DatasetConfigs/"
 
 
-def run_pipeline_for_all_datasets():
+def run_pipeline_for_all_datasets() -> None:
     """
     Runs whole DataScript pipeline for every dataset that has existing configuration file
     """
@@ -16,4 +16,9 @@ def run_pipeline_for_all_datasets():
         pipeline.run_full_pipeline_crone(name)
 
 
-run_pipeline_for_all_datasets()
+def main() -> None:
+    run_pipeline_for_all_datasets()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/modules/crawler/docker_prepare_structure.py b/modules/crawler/docker_prepare_structure.py
index 5119c46..5e1433f 100644
--- a/modules/crawler/docker_prepare_structure.py
+++ b/modules/crawler/docker_prepare_structure.py
@@ -10,32 +10,31 @@ PROCESSED_DATA_PATH = "ProcessedData/"
 CRAWLER_LOGS_PATH = "CrawlerLogs/"
 
 
-def prepare_strucure_for_all_datasets():
+def prepare_strucure_for_all_datasets() -> None:
     """
     Prepares folders that are necessery but does not contain code so they are excluded from gitlab by gitignore
     """
 
-    if  not os.path.isdir(CRAWLED_DATA_PATH) :
+    if not os.path.isdir(CRAWLED_DATA_PATH):
         try:
             os.mkdir(CRAWLED_DATA_PATH)
         except os.error as e:
             print(e)
-            print("Creation of the directory %s failed" % CRAWLED_DATA_PATH)
+            print("Nelze vytvořit adresář %s" % CRAWLED_DATA_PATH)
 
-    if  not os.path.isdir(PROCESSED_DATA_PATH) :
+    if not os.path.isdir(PROCESSED_DATA_PATH):
         try:
             os.mkdir(PROCESSED_DATA_PATH)
         except os.error as e:
             print(e)
-            print("Creation of the directory %s failed" % PROCESSED_DATA_PATH)
-    
-    if  not os.path.isdir(CRAWLER_LOGS_PATH) :
+            print("Nelze vytvořit adresář %s" % PROCESSED_DATA_PATH)
+
+    if not os.path.isdir(CRAWLER_LOGS_PATH):
         try:
             os.mkdir(CRAWLER_LOGS_PATH)
         except os.error as e:
             print(e)
-            print("Creation of the directory %s failed" % PROCESSED_DATA_PATH)
-
+            print("Nelze vytvořit adresář %s" % CRAWLER_LOGS_PATH)
 
     files_in_dir = os.listdir(CONFIG_FILES_PATH)
 
@@ -44,19 +43,24 @@ def prepare_strucure_for_all_datasets():
         prepare_structure(name[0])
 
 
-def prepare_structure(dataset_name):
+def prepare_structure(dataset_name: str) -> None:
     """
     Create folder for every dataset in newly created folder for processed and crawled data
     """
 
-    path =  CRAWLED_DATA_PATH + dataset_name
-    if  not os.path.isdir(path) :
+    path = CRAWLED_DATA_PATH + dataset_name
+    if not os.path.isdir(path):
         os.mkdir(path)
 
-    path =  PROCESSED_DATA_PATH + dataset_name
-    if not  os.path.isdir(path):
+    path = PROCESSED_DATA_PATH + dataset_name
+    if not os.path.isdir(path):
         os.mkdir(PROCESSED_DATA_PATH + dataset_name)
 
 
-print("Inicializuji počáteční strukturu pro stažená a zpracovaná data")
-prepare_strucure_for_all_datasets()
+def main() -> None:
+    print("Inicializuji počáteční strukturu pro stažená a zpracovaná data")
+    prepare_strucure_for_all_datasets()
+
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/modules/crawler/force_update_datasets.py b/modules/crawler/force_update_datasets.py
index abd6504..302a6c4 100644
--- a/modules/crawler/force_update_datasets.py
+++ b/modules/crawler/force_update_datasets.py
@@ -1,17 +1,20 @@
+from Utilities import configure_functions
 import pipeline
 import os
-from Utilities import configure_functions
+import sys
 
 # Path to configuration files
 CONFIG_FILES_PATH = "DatasetConfigs/"
+WRONG_ARG_MSG = "Do argumentu funkce dejte jméno Datasetu, který chcete aktualizovat (pokud všechny zadejte 'ALL'):\n"
+DATASET_NOT_FOUND_MSG = "Tento dataset v architektuře neexistuje"
 
 
-def run_pipeline_for_one_datasets(dataset_name):
+def run_pipeline_for_one_datasets(dataset_name: str) -> None:
     print("Probíhá update datasetu " + dataset_name)
     pipeline.run_full_pipeline(dataset_name)
 
 
-def run_pipeline_for_all_datasets():
+def run_pipeline_for_all_datasets() -> None:
     """
     Runs whole DataScript pipeline for every dataset that has existing configuration file
     """
@@ -23,15 +26,21 @@ def run_pipeline_for_all_datasets():
         pipeline.run_full_pipeline(name)
 
 
-print("Zadejte jméno Datasetu který chcete updatovat (pokud všechny zadejte '-ALL'):\n")
+def main() -> None:
+    if len(sys.argv) > 1:
+        dataset_name = sys.argv[1].upper()
+        if dataset_name == "ALL":
+            run_pipeline_for_all_datasets()
+        else:
+            test = configure_functions.check_if_there_is_a_config_file(
+                dataset_name)
+            if test == True:
+                run_pipeline_for_one_datasets(dataset_name)
+            else:
+                print(DATASET_NOT_FOUND_MSG)
+    else:
+        print(WRONG_ARG_MSG)
 
-dataset_name = input().upper()
 
-if dataset_name == '-ALL':
-    run_pipeline_for_all_datasets()
-else:
-    test = configure_functions.check_if_there_is_a_config_file(dataset_name)
-    if test == True:
-        run_pipeline_for_one_datasets(dataset_name)
-    else:
-        print("Tento dataset v architektuře neexistuje")
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/modules/crawler/fully_clean_database.py b/modules/crawler/fully_clean_database.py
index 5666602..dcfd933 100644
--- a/modules/crawler/fully_clean_database.py
+++ b/modules/crawler/fully_clean_database.py
@@ -1,8 +1,7 @@
 from Utilities.Database import database_loader
 
 
-#TODO: smazat vsechny pomocny soubory po cisteni databaze + prejmenovat
-def clean_database():
+def clean_database() -> None:
     """
     Drops every collection in database
     """
@@ -18,5 +17,10 @@ def clean_database():
         mydb[name].drop()
 
 
-print('Data z databáze budou smazána:')
-clean_database()
\ No newline at end of file
+def main() -> None:
+    print('Data z databáze budou smazána:')
+    clean_database()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/modules/crawler/pipeline.py b/modules/crawler/pipeline.py
index 51a429c..31bd208 100644
--- a/modules/crawler/pipeline.py
+++ b/modules/crawler/pipeline.py
@@ -1,6 +1,7 @@
 from Utilities import folder_processor, configure_functions
 from Utilities.Database import database_loader, database_record_logs
 from Utilities.CSV import csv_utils
+from shared_types import ConfigType
 import os
 import pymongo
 
@@ -20,12 +21,12 @@ PROCESSOR_LIB_PATH = "DatasetProcessing."
 
 #logger
 logging.basicConfig(filename=CRAWLER_LOGS_PATH + 'Applicationlog-' +
-                               date.today().strftime("%b-%Y") + '.log',
-                               level=logging.INFO,
-                               format='%(asctime)s %(message)s')
+                    date.today().strftime("%b-%Y") + '.log',
+                    level=logging.INFO,
+                    format='%(asctime)s %(message)s')
 
 
-def check_last_update(config):
+def check_last_update(config: ConfigType) -> bool:
     """
     Loads integer from updated.txt in CrawlerLogs/"dataset_name"
     representing number of days from last update if number equals
@@ -56,7 +57,7 @@ def check_last_update(config):
         return False
 
 
-def crawl_data(config):
+def crawl_data(config: ConfigType) -> None:
     """
       Imports dataset crawler in DatasetCrawler/"dataset_name"_crawler.py
       runs crawler.
@@ -73,7 +74,7 @@ def crawl_data(config):
     dataset_name += '/'
 
 
-def process_data(config):
+def process_data(config: ConfigType) -> None:
     """
     Goes trough every not processed file(list of processed files is saved in databse)
     Imports dataset processor in DatasetProcessing/"dataset_name"_processor.py
@@ -100,6 +101,7 @@ def process_data(config):
         path = CRAWLED_DATA_PATH + dataset_path + not_processed_file
         date_dic = process_file_func(path)
         csv_utils.export_data_to_csv(path, date_dic)
+        print("Vytvářím: " + not_processed_file)
         database_record_logs.update_ignore_set_processed(
             dataset_name, not_processed_file)
 
@@ -107,7 +109,7 @@ def process_data(config):
                  str(len(not_processed_files)) + " newly crawled files")
 
 
-def process_data_crone(config):
+def process_data_crone(config: ConfigType) -> None:
     """
     Goes trough every not processed file(list of processed files is saved in database)
     Imports dataset processor in DatasetProcessing/"dataset_name"_processor.py
@@ -146,7 +148,7 @@ def process_data_crone(config):
                  str(len(not_processed_files)) + " newly crawled files")
 
 
-def validate_process_data(config):
+def validate_process_data(config: ConfigType) -> bool:
     """
     Function goes through newly processed data and checks theirs status
 
@@ -186,7 +188,7 @@ def validate_process_data(config):
     return True
 
 
-def load_data_to_database(config):
+def load_data_to_database(config: ConfigType) -> None:
     """
     Goes trough every not loaded file(list of loaded files is saved in database)
     loads data appends coordination from configurations
@@ -207,10 +209,10 @@ def load_data_to_database(config):
     changes_in_devices = database_loader.update_devices_collection(config)
 
     if changes_in_devices == True:
-        logging.info(
-            dataset_name +
-            " contains changes in devices configuration. Deleting old data and preparing new"
-        )
+        logg_string = dataset_name + " contains changes in devices configuration. Deleting old data and preparing new"
+        logg_string_cs = dataset_name + " obsahuje změny v konfiguračním souboru. Probíha odstraňování starých dat a připravení nových."
+        logging.info(logg_string)
+        print(logg_string_cs)
         database_loader.reset_dataset_database(dataset_name)
 
     # get all unprocessed files from dataset
@@ -230,14 +232,19 @@ def load_data_to_database(config):
         database_record_logs.update_ignore_set_loaded(dataset_name,
                                                       not_loaded_file)
 
-    logging.info(dataset_name + " has loaded to database " +
-                 str(len(not_loaded_files)) + " newly processed files.")
+    logg_string = dataset_name + " has loaded to database " + str(
+        len(not_loaded_files)) + " newly processed files."
+    logg_string_cs = dataset_name + " načetl " + str(
+        len(not_loaded_files)) + " nových zpracovaných souborů \n"
+
+    logging.info(logg_string)
+    print(logg_string_cs)
 
     client = pymongo.MongoClient()
     client.close()
 
 
-def load_data_to_database_crone(config):
+def load_data_to_database_crone(config: ConfigType) -> None:
     """
     Goes trough every not loaded file(list of loaded files is saved in database)
     loads data appends coordination from configurations
@@ -280,7 +287,7 @@ def load_data_to_database_crone(config):
     client.close()
 
 
-def run_full_pipeline(dataset_name):
+def run_full_pipeline(dataset_name: str) -> None:
     """
     Loads config file and starts full pipeline
     -crawl data
@@ -292,7 +299,7 @@ def run_full_pipeline(dataset_name):
     """
     logging.info("Starting pipeline for dataset " + dataset_name)
     print("Zpracovávám dataset " + dataset_name +
-          " průběh lze sledovat v logu umístěném v in CrawlerLogs folder")
+          ", průběh lze sledovat v logu umístěném v adresáři CrawlerLogs")
 
     config = configure_functions.load_configuration(dataset_name)
     crawl_data(config)
@@ -304,7 +311,7 @@ def run_full_pipeline(dataset_name):
         load_data_to_database(config)
 
 
-def run_full_pipeline_crone(dataset_name):
+def run_full_pipeline_crone(dataset_name: str) -> None:
     """
     Loads config file and starts full pipeline
     -crawl data
diff --git a/modules/crawler/prepare_new_dataset.py b/modules/crawler/prepare_new_dataset.py
index dcf1860..2d623e6 100644
--- a/modules/crawler/prepare_new_dataset.py
+++ b/modules/crawler/prepare_new_dataset.py
@@ -1,5 +1,4 @@
 import os
-
 # Path to crawled data
 CRAWLED_DATA_PATH = "CrawledData/"
 # Path to processed data
@@ -14,7 +13,7 @@ CONFIG_FILES_PATH = "DatasetConfigs"
 DEFAULT_COLOR = "#000000"
 
 
-def create_default_config_file(dataset_name: str):
+def create_default_config_file(dataset_name: str) -> None:
     """
     Creates default config file
 
@@ -22,64 +21,79 @@ def create_default_config_file(dataset_name: str):
         dataset_name: Name of newly created dataset
     """
     with open(CONFIG_FILES_PATH + "/" + dataset_name + ".yaml", "w") as file:
-        file.write("# jmeno datasetu, pod kterym bude zobrazen v aplikaci\n")
+        file.write("# Name of the dataset inside the application\n")
         file.write("display-name: " + dataset_name + "\n")
         file.write(
-            "# jednoslovný název datasetu, pod kterym bude reprezentovana v architektuře\n")
-        file.write("display-color: " + DEFAULT_COLOR + "\n")
+            "# Color for the dataset in a hex value (default value #000000)\n")
+        file.write(f'display-color: \'{DEFAULT_COLOR}\' \n')
         file.write(
-            "# barva pro tento dataset v hexadecimální hodnotě (#000000)\n")
+            "# One word dataset name (structure of all modules will be affected by this)\n"
+        )
         file.write("dataset-name: " + dataset_name + "\n")
-        file.write("# root slozka, ktera obsahuje odkazy na dataset\n")
-        file.write("url: ZDE VLOZTE URL\n")
+        file.write("# Url for the source of this dataset\n")
+        file.write("url: ENTER URL HERE\n")
+        file.write(
+            "# Optional parameter which specifies a pattern of the datasets name\n"
+        )
+        file.write(
+            "# Example: DATASET_NAME_[0-9][0-9]_[0-9][0-9][0-9][0-9].zip\n")
+        file.write(
+            "# - DATASET_NAME_01_2020.zip where '01_2020' specifies date in this dataset\n"
+        )
+        file.write("regex: ENTER REGEX HERE\n")
         file.write(
-            "# volitelny parameter, ktery specifikuje vzor jmrna datasetu, ktera se budou stahovat\n")
-        file.write("regex: ZDE VLOZTE REGEX\n")
-        file.write("# volitelny parametr, ktery udava jak casto se budou hledat nove datasety, pokud prazdne, "
-                   "tak defaultni hodnota (dny)\n")
-        file.write("update-period: ZDE VLOZTE HODNOTU\n")
-        file.write("# pozice jednotlivych zarizeni, ktera jsou v datasetu\n")
+            "# Optional parameter which specifies the way of searching new datasets (if empty the period is set to every day)\n"
+        )
+        file.write("update-period: ENTER UPDATE PERIOD HERE\n")
+        file.write("# Coordinates of every datasets device (entinty)\n")
         file.write("devices:\n")
 
 
-def create_default_processor(dataset_name):
+def create_default_processor(dataset_name: str) -> None:
     """
     Creates default processor for dataset
 
     Args:
         dataset_name: Name of newly created dataset
     """
-    with open(PROCESSOR_PROGRAM_PATH + "/" + dataset_name + "_processor.py", "w") as file:
-        file.write("from Utilities.CSV import csv_data_line")
+    with open(PROCESSOR_PROGRAM_PATH + "/" + dataset_name + "_processor.py",
+              "w") as file:
+        file.write("from Utilities.CSV import csv_data_line\n")
+        file.write("from shared_types import DateDict")
         file.write("\n")
         file.write("\n")
-        file.write("def process_file(filename):\n")
+        file.write("def process_file(filename: str) -> DateDict:\n")
         file.write("    \"\"\"\n")
         file.write(
-            "    Method that take path to crawled file and outputs date dictionary:\n")
+            "    Method that takes the path to crawled file and outputs date dictionary:\n"
+        )
         file.write(
-            "    Date dictionary is a dictionary where keys are dates in format YYYY-mm-dd-hh (2018-04-08-15)\n")
+            "    Date dictionary is a dictionary where keys are dates in format YYYY-mm-dd-hh (2018-04-08-15)\n"
+        )
         file.write(
-            "    and value is dictionary where keys are devices (specified in configuration file)\n")
+            "    and value is dictionary where keys are devices (specified in configuration file)\n"
+        )
         file.write(
-            "    and value is CSVDataLine.csv_data_line with device,date and occurrence\n")
+            "    and value is CSVDataLine.csv_data_line with device,date and occurrence\n"
+        )
         file.write("\n")
         file.write("    Args:\n")
-        file.write("    filename: name of processed file\n")
+        file.write("    filename: name of the processed file\n")
         file.write("\n")
         file.write("    Returns:\n")
         file.write("    None if not implemented\n")
         file.write("    date_dict when implemented\n")
         file.write("    \"\"\"\n")
-        file.write("    date_dict = dict()\n")
+        file.write("    date_dict: DateDict = {}\n")
         file.write("\n")
         file.write("    #with open(filename, \"r\") as file:\n")
         file.write(
-            "    print(\"You must implements process_file method first!\")\n")
-        file.write("    return None\n")
+            "    print(\"You must implement the process_file method first!\")\n"
+        )
+        file.write("    return date_dict\n")
 
 
-def create_default_crawler(dataset_name):
+def create_default_crawler(dataset_name: str) -> None:
     """
     Creates default crawler for dataset
 
@@ -87,19 +101,23 @@ def create_default_crawler(dataset_name):
         dataset_name: Name of newly created dataset
     """
 
-    with open(CRAWLER_PROGRAM_PATH + "/" + dataset_name + "_crawler.py", "w") as file:
+    with open(CRAWLER_PROGRAM_PATH + "/" + dataset_name + "_crawler.py",
+              "w") as file:
+        file.write("from shared_types import ConfigType\n")
         file.write("# Path to crawled data\n")
-        file.write("CRAWLED_DATA_PATH = \"CrawledData/\"\n")
+        file.write(f'CRAWLED_DATA_PATH = "{CRAWLED_DATA_PATH}" \n')
         file.write("\n")
         file.write("\n")
-        file.write("def crawl(config):\n")
+        file.write("def crawl(config: ConfigType):\n")
         file.write("    \"\"\"\n")
         file.write(
-            "    Implement crawl method that downloads new data to path_for_files\n")
+            "    Implementation the crawl method which downloads new data to the path_for_files\n"
+        )
         file.write("    For keeping the project structure\n")
         file.write("    url , regex, and dataset_name from config\n")
         file.write(
-            "    You can use already implemented functions from Utilities/Crawler/BasicCrawlerFunctions.py\n")
+            "    You can use already implemented functions from Utilities/Crawler/BasicCrawlerFunctions.py\n"
+        )
         file.write("\n")
         file.write("    Args:\n")
         file.write("        config: loaded configuration file of dataset\n")
@@ -109,10 +127,11 @@ def create_default_crawler(dataset_name):
         file.write("    regex = config['regex']\n")
         file.write(
             "    path_for_files = CRAWLED_DATA_PATH + dataset_name + '/'\n")
-        file.write("    print(\"You must implements Crawl method first!\")\n")
+        file.write(
+            "    print(\"Není implementován crawler pro získávání dat!\")\n")
 
 
-def prepare_dataset_structure(dataset_name):
+def prepare_dataset_structure(dataset_name: str) -> None:
     """
     Prepares folders for new dataset
     Args:
@@ -120,7 +139,7 @@ def prepare_dataset_structure(dataset_name):
     """
 
     # create folder for crawled data
-    path = CRAWLED_DATA_PATH+dataset_name
+    path = CRAWLED_DATA_PATH + dataset_name
     try:
         os.mkdir(path)
     except os.error as e:
@@ -132,19 +151,23 @@ def prepare_dataset_structure(dataset_name):
     try:
         os.mkdir(path)
     except OSError:
-        print("Creation of the directory %s failed" % path)
+        print("Nelze vytvořit adresář %s" % path)
 
     create_default_crawler(dataset_name)
     create_default_processor(dataset_name)
     create_default_config_file(dataset_name)
 
 
-print("Zadejte jméno nového datasetu:\n")
+def main() -> None:
+    print("Zadejte jméno nového datasetu:\n")
+    dataset_name = input().upper()
+
+    if dataset_name.isalpha():
+        prepare_dataset_structure(dataset_name)
+        print("Architektura vytvořena \n")
+    else:
+        print("Jméno musí obsahovat pouze písmena z abecedy (bez mezer)\n")
 
-dataset_name = input().upper()
 
-if dataset_name.isalpha():
-    prepare_dataset_structure(dataset_name)
-    print("Architektura vytvořena \n")
-else:
-    print("Jméno musí obsahovat pouze písmena z abecedy (bez mezer)\n")
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/modules/crawler/python.code-workspace b/modules/crawler/python.code-workspace
index 6e06519..bc5226b 100644
--- a/modules/crawler/python.code-workspace
+++ b/modules/crawler/python.code-workspace
@@ -12,7 +12,6 @@
 		"python.linting.pylintEnabled": true,
 		"python.linting.enabled": true,
 		"python.linting.pylintPath": "pylint",
-		"python.pythonPath": "/usr/local/bin/python",
 		"python.formatting.provider": "yapf",
 	},
 	"extensions": {
diff --git a/modules/crawler/remove_dataset.py b/modules/crawler/remove_dataset.py
index 6268b31..9d419f0 100644
--- a/modules/crawler/remove_dataset.py
+++ b/modules/crawler/remove_dataset.py
@@ -17,7 +17,7 @@ CRAWLER_PROGRAM_PATH = "DatasetCrawler"
 PROCESSOR_PROGRAM_PATH = "DatasetProcessing"
 
 
-def remove_dataset(dataset_name):
+def remove_dataset(dataset_name: str) -> None:
     """
     Remove dataset
     Args:
@@ -36,14 +36,17 @@ def remove_dataset(dataset_name):
 
     print("Dataset " + dataset_name + " odebrán z databáze")
 
-print("Zadejte jméno Datasetu který chcete odstranit:\n")
 
-dataset_name = input().upper()
-test = configure_functions.check_if_there_is_a_config_file(dataset_name)
+def main() -> None:
+    print("Zadejte jméno Datasetu který chcete odstranit:\n")
+    dataset_name = input().upper()
+    test = configure_functions.check_if_there_is_a_config_file(dataset_name)
 
-if test == True:
-    remove_dataset(dataset_name)
-else:
-    print("Tento dataset v architektuře neexistuje")
+    if test == True:
+        remove_dataset(dataset_name)
+    else:
+        print("Tento dataset v architektuře neexistuje")
 
 
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/modules/crawler/reset_datasets.py b/modules/crawler/reset_datasets.py
index 4b7fc8a..3c1b4ca 100644
--- a/modules/crawler/reset_datasets.py
+++ b/modules/crawler/reset_datasets.py
@@ -2,7 +2,7 @@ import os
 from Utilities import folder_processor
 from Utilities.Database import database_loader
 from Utilities import configure_functions
- 
+
 # Path to crawled data
 CRAWLED_DATA_PATH = "CrawledData/"
 # Path to processed data
@@ -13,7 +13,7 @@ CRAWLER_LOGS_PATH = "CrawlerLogs/"
 CONFIG_FILES_PATH = "DatasetConfigs"
 
 
-def hard_reset_dataset(dataset_name):
+def hard_reset_dataset(dataset_name: str) -> None:
     """
     Resets all saved data in dataset except config and implementation
     Args:
@@ -29,7 +29,7 @@ def hard_reset_dataset(dataset_name):
     database_loader.remove_dataset_database(dataset_name)
 
 
-def soft_reset_dataset(dataset_name):
+def soft_reset_dataset(dataset_name: str) -> None:
     """
     Resets all saved data in dataset except config and implementation
     Args:
@@ -41,8 +41,7 @@ def soft_reset_dataset(dataset_name):
     database_loader.remove_dataset_database(dataset_name)
 
 
-
-def soft_reset_all_datasets():
+def soft_reset_all_datasets() -> None:
     """
     Resets all saved data in all datasets with config file except configs and implementation
     """
@@ -52,7 +51,7 @@ def soft_reset_all_datasets():
         soft_reset_dataset(dataset.split('.')[0])
 
 
-def hard_reset_all_datasets():
+def hard_reset_all_datasets() -> None:
     """
     Resets all saved data in all datasets with config file except configs and implementation
     """
@@ -62,30 +61,37 @@ def hard_reset_all_datasets():
         hard_reset_dataset(dataset.split('.')[0])
 
 
-print("Zadejte jméno Datasetu který chcete resetovat (pokud všechny zadejte '-ALL'):\n")
+def main() -> None:
+    print(
+        "Zadejte jméno Datasetu který chcete resetovat (pokud všechny zadejte '-ALL'):\n"
+    )
 
-dataset_name = input().upper()
+    dataset_name = input().upper()
 
-print("Chcete smazat i stažené stažená data ? (ANO/NE) \n")
+    print("Chcete smazat i stažená data ? (ANO/NE) \n")
 
-input_decision = input().upper()
+    input_decision = input().upper()
 
-if dataset_name == '-ALL':
-    if input_decision == 'ANO':
-        hard_reset_all_datasets()
-    elif input_decision == 'NE':
-        soft_reset_all_datasets()
-    else:
-        print('Neplatný vstup (ANO/NE)')
-else:
-    test = configure_functions.check_if_there_is_a_config_file(dataset_name)
-    if test == True:
+    if dataset_name == '-ALL':
         if input_decision == 'ANO':
-            hard_reset_dataset(dataset_name)
+            hard_reset_all_datasets()
         elif input_decision == 'NE':
-            soft_reset_dataset(dataset_name)
+            soft_reset_all_datasets()
         else:
             print('Neplatný vstup (ANO/NE)')
     else:
-        print("Tento dataset v architektuře neexistuje")
+        test = configure_functions.check_if_there_is_a_config_file(
+            dataset_name)
+        if test == True:
+            if input_decision == 'ANO':
+                hard_reset_dataset(dataset_name)
+            elif input_decision == 'NE':
+                soft_reset_dataset(dataset_name)
+            else:
+                print('Neplatný vstup (ANO/NE)')
+        else:
+            print("Tento dataset v architektuře neexistuje")
+
 
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/modules/crawler/shared_types.py b/modules/crawler/shared_types.py
new file mode 100644
index 0000000..e3780d0
--- /dev/null
+++ b/modules/crawler/shared_types.py
@@ -0,0 +1,4 @@
+from typing import Dict, Set
+ConfigType = Dict[str, any]
+StringSetType = Set[str]
+DateDict = Dict[str, any]
\ No newline at end of file
diff --git a/scripts/build.sh b/scripts/build.sh
index ce263ae..4a5eca8 100755
--- a/scripts/build.sh
+++ b/scripts/build.sh
@@ -4,4 +4,4 @@ docker-compose -f docker-compose.yml -f docker-compose-prod.yml up --build -d
 docker-compose exec -T php-fpm composer install --no-interaction --working-dir=/var/www/symfony --no-dev
 docker-compose exec -T php-fpm /bin/sh -c "cd /var/www/symfony && npm install && npm link webpack && npm run build"
 #python module initiation
-docker-compose exec -T crawler /bin/sh -c "cd /src && python docker_prepare_structure.py && echo '-all' | python force_update_datasets.py"
\ No newline at end of file
+docker-compose exec -T crawler /bin/sh -c "cd /src && python docker_prepare_structure.py && python force_update_datasets.py all"
diff --git a/scripts/dev/build.bat b/scripts/dev/build.bat
index a2533c2..94633ad 100644
--- a/scripts/dev/build.bat
+++ b/scripts/dev/build.bat
@@ -2,5 +2,5 @@ docker-compose -f docker-compose.yml -f docker-compose-dev.yml up --build -d
 docker-compose exec php-fpm composer config extra.symfony.allow-contrib true --no-interaction --working-dir=/var/www/symfony
 docker-compose exec php-fpm composer install --no-interaction --working-dir=/var/www/symfony
 docker-compose exec php-fpm composer update --no-interaction --working-dir=/var/www/symfony
-docker-compose exec php-fpm /bin/sh -c "cd /var/www/symfony && npm install"
-docker-compose exec crawler /bin/sh -c "cd /src && python docker_prepare_structure.py && echo '-all' | python force_update_datasets.py"
\ No newline at end of file
+docker-compose exec php-fpm /bin/sh -c "cd /var/www/symfony && npm install && npm link webpack && npm run build"
+docker-compose exec crawler /bin/sh -c "cd /src && python docker_prepare_structure.py && python force_update_datasets.py all"
diff --git a/scripts/dev/build.sh b/scripts/dev/build.sh
index 914e5c9..6a8c227 100755
--- a/scripts/dev/build.sh
+++ b/scripts/dev/build.sh
@@ -25,5 +25,5 @@ cat << "EOF"
 EOF
 docker-compose exec -T php-fpm /bin/sh -c "cd /var/www/symfony && npm install && npm link webpack && npm run build"
 echo "================= NPM INSTALL DONE ==================="
-docker-compose exec -T crawler /bin/sh -c "cd /src && python docker_prepare_structure.py && echo '-all' | python force_update_datasets.py"
-echo "================= LOADING DATASETS DONE ==================="
\ No newline at end of file
+docker-compose exec -T crawler /bin/sh -c "cd /src && python docker_prepare_structure.py && python force_update_datasets.py all"
+echo "================= LOADING DATASETS DONE ==================="
diff --git a/website/public/css/style.css b/website/public/css/style.css
index 243a6b3..6588620 100644
--- a/website/public/css/style.css
+++ b/website/public/css/style.css
@@ -1,1349 +1,3 @@
-@import url("https://fonts.googleapis.com/css2?family=Be+Vietnam:wght@400;600;700;800&display=swap");
-html, body {
-  position: relative;
-  height: 96.2%;
-  font-family: 'Be Vietnam', sans-serif;
-  color: #ffffff;
-  background: #0b155a;
-}
+@import url(https://fonts.googleapis.com/css2?family=Be+Vietnam:wght@400;600;700;800&display=swap);
+html,body{position:relative;height:96.2%;font-family:'Be Vietnam', sans-serif;color:#fff;background:#0b155a}@media (max-width: 767.98px){html.intro,body.intro{height:calc(100% - 45px)}}a{color:#fff;outline:none}a:hover{color:#fff}h1{text-transform:uppercase;font-size:21px;font-weight:700;letter-spacing:.53px}@media (min-width: 992px){h1{line-height:55px}}h2{font-size:16px;font-weight:400;letter-spacing:.4px}@media (min-width: 992px){h2{margin:0}}h3{font-size:38px;font-weight:800;letter-spacing:.95px}@media (max-width: 575.98px){h3{font-size:28px}}hr{width:300px;text-align:left;margin:30px 0;border-top:2px solid #fff}@media (max-width: 1199.98px){hr{width:90%}}@media (max-width: 575.98px){hr{margin:15px 0}}header{height:70px;background:#0048A9}header .logo{width:450px;height:181px;position:absolute;top:70px;left:0;z-index:2;background:url(img/header-bg.png)}@media (max-width: 1400px){header .logo{display:none}}header .logo img{height:100px;position:relative;top:-50px;left:50px}header nav.navbar{height:70px;position:relative}header .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgb(255,255,255)' stroke-width='3' stroke-linecap='round' stroke-miterlimit='10' d='M4 8h24M4 16h24M4 24h24'/%3E%3C/svg%3E")}@media (min-width: 1401px){header .navbar-brand.shifted{position:absolute;top:0;left:450px}}@media (max-width: 1199.98px){header .navbar-brand.shifted{left:20px}}@media (max-width: 991.98px){header #navigation{width:100%;padding:20px;position:absolute;top:70px;left:0;z-index:1001;opacity:.98;background:#004fb3}}header .nav-item{margin-right:40px}@media (max-width: 1480px){header .nav-item{margin-right:30px}}@media (max-width: 1440px){header .nav-item{margin-right:15px}}@media (max-width: 1199.98px){header .nav-item{margin-right:30px}}@media (max-width: 991.98px){header .nav-item{margin-right:0}}header .nav-item .active{font-weight:800}header .nav-item.button .nav-link{padding-left:22px;padding-right:22px;border:1px solid #fff;border-radius:20px}header .nav-item.button .nav-link:hover{background:#004fb3}header .navbar-button{padding:.5rem 22px;margin-right:40px;border:1px solid #fff;border-radius:20px;text-decoration:none}@media (max-width: 991.98px){header .navbar-button{display:none}}header .navbar-button:hover{background:#004fb3}header .btn-secondary{height:100%;font-size:16px;text-transform:uppercase;font-weight:800;letter-spacing:.4px;border-radius:0;border:#0048A9;background:#004fb3}header .btn-secondary:hover{background:#0048A9 !important}.swiper-container{height:100%;position:relative}.swiper-container .btn-primary{width:300px;height:50px;line-height:32px;position:relative;padding-left:30px;margin-top:40px;text-align:left;font-size:16px;text-transform:uppercase;letter-spacing:0.4px;border-radius:30px;border:1px solid #0b155a;background:#0b155a}.swiper-container .btn-primary:hover,.swiper-container .btn-primary:focus,.swiper-container .btn-primary:active{border:1px solid #0048A9;background:#0048A9 !important}@media (max-width: 575.98px){.swiper-container .btn-primary{width:100%;max-width:270px;height:auto;margin-top:20px}}.swiper-container .btn-secondary{background:#0b155a}.swiper-container .btn-secondary:hover,.swiper-container .btn-secondary:focus,.swiper-container .btn-secondary:active{background:rgba(255,255,255,0.1) !important}.swiper-slide{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.swiper-slide.slide-bg-1{background:url(img/bg-1.jpg) no-repeat top center;background-size:cover}.swiper-slide.slide-bg-2{background:url(img/bg-2.jpg) no-repeat bottom center;background-size:cover}.swiper-slide.slide-bg-3{background:url(img/bg-3.jpg) no-repeat bottom center;background-size:cover}.swiper-slide.slide-bg-3 .slide-text{width:100%}@media (max-width: 767.98px){.swiper-slide .container{max-width:100%;height:100%;overflow-y:scroll}}.slide-text{width:600px;padding-bottom:160px;position:relative}@media (max-width: 1199.98px){.slide-text{width:100%}}@media (max-width: 1199.98px){.slide-text{padding-bottom:80px}}.slide-text .slide-index{display:inline-block;padding-bottom:20px;font-size:72px;font-weight:800}@media (max-width: 575.98px){.slide-text .slide-index{padding-bottom:10px;font-size:48px}}.slide-text .btn-primary{z-index:1000}.slide-text .btn-primary:after{content:"";display:inline-block;width:10px;height:16px;position:absolute;top:16px;right:20px;background:url(img/btn-play.svg) no-repeat top right}.slide-text p{font-size:18px;font-weight:600}@media (max-width: 575.98px){.slide-text p{font-size:16px}}.slide-text p a{text-decoration:underline}.slide-text p a:hover{text-decoration:none}.swiper-pagination-buttons{width:200px;height:56px;padding:6px;position:absolute;right:150px;bottom:20px;z-index:1;border-radius:100px;background:#0b155a}@media (max-width: 1199.98px){.swiper-pagination-buttons{right:20px}}@media (max-width: 767.98px){.swiper-pagination-buttons{width:120px}}@media (max-width: 575.98px){.swiper-pagination-buttons{bottom:10px}}.swiper-pagination-buttons .btn-prev,.swiper-pagination-buttons .btn-next{display:inline-block;width:45px;height:45px;position:relative;border-radius:50%;border:2px solid #fff}.swiper-pagination-buttons .btn-prev:after,.swiper-pagination-buttons .btn-next:after{content:"";display:inline-block;width:7px;height:12px;position:absolute;top:calc(50% - 6px);left:calc(50% - 4px)}.swiper-pagination-buttons .btn-prev:after{background:url(img/btn-prev.svg) no-repeat top right}.swiper-pagination-buttons .btn-next{float:right}.swiper-pagination-buttons .btn-next:after{background:url(img/btn-next.svg) no-repeat top right}.swiper-pagination-bullets{display:inline-block;width:auto !important;height:30px;position:absolute;left:20px !important;bottom:30px !important;border-radius:100px;opacity:0.8;background:#0b155a}@media (min-width: 992px){.swiper-pagination-bullets{left:50% !important;-webkit-transform:translateX(-50%);transform:translateX(-50%)}}@media (max-width: 575.98px){.swiper-pagination-bullets{display:none}}.swiper-pagination-bullets:before{content:"";width:100px;position:absolute;top:13px;left:15px;z-index:1;border-top:2px solid #fff}.swiper-pagination-bullets .swiper-pagination-bullet{width:20px;height:20px;position:relative;margin:4px 15px !important;opacity:1;border:2px solid #0b155a;background:#0b155a}.swiper-pagination-bullets .swiper-pagination-bullet:before{content:"";display:inline-block;width:6px;height:6px;position:absolute;top:5px;left:5px;border-radius:50%;background:#fff}.swiper-pagination-bullets .swiper-pagination-bullet:first-of-type{margin-left:5px !important}.swiper-pagination-bullets .swiper-pagination-bullet:last-of-type{margin-right:5px !important}.swiper-pagination-bullets .swiper-pagination-bullet-active{border:2px solid #fff}.map-point{width:50px;height:50px;z-index:1;position:absolute;border-radius:50%;cursor:pointer;border:2px solid #fff}@media not all and (min-width: 1900px) and (max-width: 2100px){.map-point{display:none}}.map-point:before{content:"";display:inline-block;width:16px;height:16px;position:absolute;top:calc(50% - 8px);left:calc(50% - 8px);background:url(img/map-point-plus.svg) no-repeat}.map-point:hover:before{z-index:1;top:calc(50% - 1px);background:url(img/map-point-minus.svg) no-repeat}.map-point:hover .desc{display:block;width:220px;padding:10px 20px;z-index:0;opacity:0.75;border-radius:25px 0 25px 0;background:#0b155a}.map-point .desc{display:none}.map-point p{font-size:14px;font-weight:400}.map-point p:last-of-type{margin-bottom:10px}.map-point strong{display:inline-block;width:100%;padding:0 0 20px 40px;font-size:16px}.map-point .circle{width:50px;height:50px;position:absolute;top:-2px;left:-2px;border-radius:50%;border:2px solid #fff}.map-point.point-1{top:470px;right:32.2%}.map-point.point-2{top:688px;right:21.2%}.map-point.point-3{bottom:430px;right:20.8%}.slide-background{width:650px;height:290px;z-index:1;position:absolute;bottom:0;left:0;background:url(img/footer-bg.svg) no-repeat}@media (max-width: 991.98px){.slide-background{width:320px;height:143px;background-size:100%}}div.copyright p{font-weight:400}div.copyright span{font-size:17px}p.logos-partners{margin-top:40px;text-align:right}p.logos-partners a{text-decoration:none}p.logos-partners a img{margin-left:50px;height:80px}@media only screen and (max-width: 430px){p.logos-partners a img{height:70px;margin-left:0}}@media only screen and (max-width: 340px){p.logos-partners a img{height:60px}}p.logos-partners a:first-of-type img{margin-left:0}#heatmap{height:100%}@media (max-width: 991.98px){header.map .navbar-nav{width:100%}}header.map form{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;margin:auto}header.map label{margin:0 0 0 15px;font-size:16px;letter-spacing:.4px}header.map input{letter-spacing:.4px;color:#fff;border-color:#0048A9;background:#0048A9}header.map input::-webkit-input-placeholder{color:#fff;opacity:1}header.map input::-moz-placeholder{color:#fff;opacity:1}header.map input:-ms-input-placeholder{color:#fff;opacity:1}header.map input::-ms-input-placeholder{color:#fff;opacity:1}header.map input::placeholder{color:#fff;opacity:1}header.map input:hover{background:rgba(255,255,255,0.1)}header.map input:focus{background:#fff;color:#000}header.map .nav-item{padding:5px;margin:auto 0;border-right:1px solid #1c6cc0}@media (max-width: 991.98px){header.map .nav-item{margin-bottom:10px;border:0}}header.map .nav-item:nth-of-type(1),header.map .nav-item:nth-of-type(2),header.map .nav-item:nth-of-type(3){width:300px}@media (max-width: 1500px){header.map .nav-item:nth-of-type(1),header.map .nav-item:nth-of-type(2),header.map .nav-item:nth-of-type(3){width:280px}}@media (max-width: 1400px){header.map .nav-item:nth-of-type(1),header.map .nav-item:nth-of-type(2),header.map .nav-item:nth-of-type(3){width:230px}}@media (max-width: 1400px){header.map .nav-item:nth-of-type(1),header.map .nav-item:nth-of-type(2),header.map .nav-item:nth-of-type(3){max-width:230px;width:auto}}@media (max-width: 991.98px){header.map .nav-item:nth-of-type(1),header.map .nav-item:nth-of-type(2),header.map .nav-item:nth-of-type(3){max-width:none}}header.map .nav-item .dropdown{min-width:200px}header.map .nav-item .dropdown .btn{width:100%;text-align:left;letter-spacing:.4px;color:#fff;background:#0048A9}header.map .nav-item .dropdown .btn:hover{background:rgba(255,255,255,0.1)}header.map .nav-item .dropdown .dropdown-menu{width:100%;max-height:300px;overflow-y:auto;z-index:1001}header.map .nav-item .dropdown .dropdown-menu .dropdown-item:active{background:#0048A9}header.map .nav-item .dropdown .dropdown-menu .dropdown-item label{width:100% !important;margin-left:0;cursor:pointer}header.map .nav-item .dropdown #dropdown-time .dropdown-item:nth-of-type(even){background:rgba(0,0,0,0.05)}header.map .nav-item .dropdown #dropdown-time .dropdown-item:nth-of-type(even):active{background:#0048A9}header.map .nav-item .dropdown input[type="radio"]:checked ~ label{font-weight:800}header.map .nav-item:nth-child(3){padding:0;margin:0;border:0}@media (max-width: 991.98px){header.map .nav-item:nth-child(3){padding:5px}}header.map .nav-item:nth-child(3) .dropdown{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;height:100%}@media (max-width: 991.98px){header.map .nav-item:nth-child(3) .dropdown{-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}}header.map .nav-item:nth-child(3) .dropdown .dropdown-menu{top:calc(100% - 5px)}header.map .nav-item:nth-child(3) .dropdown button{padding-left:20px;border-radius:0}@media (max-width: 991.98px){header.map .nav-item:nth-child(3) .dropdown button{padding-left:12px;border-radius:5px}}header.map .nav-item:last-child{margin-top:0;margin-bottom:0;border-left:1px solid #1c6cc0;background:#0057be}@media (max-width: 991.98px){header.map .nav-item:last-child{margin:10px 5px 0 5px;border-radius:5px;border:0}}header.map .nav-item:last-child:hover{background:#004fb3}header.map .nav-item:last-child .btn-secondary{display:inline-block;outline:none;padding:0 30px;background:url(img/btn-refresh.svg) no-repeat center center}header.map .nav-item:last-child .btn-secondary:hover{background:url(img/btn-refresh.svg) no-repeat center center !important}@media (max-width: 991.98px){header.map .nav-item:last-child .btn-secondary{width:100%;padding:20px;border-radius:5px}}.map-locations{position:absolute;top:95px;right:25px;z-index:1000;border-radius:22px;background-color:rgba(11,21,90,0.7)}.map-locations .mobile{display:none}@media (max-width: 991.98px){.map-locations{top:90px;right:20px}.map-locations .desktop{display:none}.map-locations .mobile{display:inline-block}}@media (max-width: 575.98px){.map-locations{max-width:240px;max-height:70%;top:80px;right:10px}}.map-locations .menu{width:100%;display:inline-block;position:relative;padding:10px 60px 13px 16px;text-decoration:none;border-radius:50px;opacity:1;background:#0b155a}.map-locations .menu .circle{display:inline-block;width:40px;height:40px;position:absolute;top:3px;right:3px;border-radius:50%;border:2px solid #fff}.map-locations .menu .circle:after{content:"";display:inline-block;width:16px;height:16px;position:absolute;top:calc(50% - 8px);left:calc(50% - 8px);background:url(img/map-point-plus.svg) no-repeat}.map-locations .locations{z-index:1000}.map-locations .locations ul{margin-top:10px;padding-left:36px;padding-right:20px;font-size:15px;list-style-image:url(img/li-bullet.svg)}@media (max-width: 575.98px){.map-locations .locations ul{max-height:244px;overflow-y:scroll;font-size:13.5px}}.map-locations .locations li{margin-bottom:10px;cursor:pointer}.map-locations .locations li:hover{text-decoration:underline}.player{width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;position:absolute;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);bottom:-40px;z-index:1000}@media (max-width: 575.98px){.player{bottom:-30px}}.player .next-btn,.player .prev-btn,.player .animate-btn,.player .lock-btn{display:inline-block;width:42px;height:42px;margin-right:20px;position:relative;border-radius:50%;opacity:.7;border:2px solid #fff;-webkit-box-shadow:0 0 0 4px #0b155a;box-shadow:0 0 0 4px #0b155a;background:#0b155a;cursor:pointer}@media (max-width: 320px){.player .next-btn,.player .prev-btn,.player .animate-btn,.player .lock-btn{margin-right:15px}}.player .next-btn:hover,.player .prev-btn:hover,.player .animate-btn:hover,.player .lock-btn:hover{opacity:1}.player .next-btn:after,.player .prev-btn:after,.player .animate-btn:after,.player .lock-btn:after{content:"";display:inline-block;width:7px;height:12px;position:absolute;top:calc(50% - 6px);left:calc(50% - 4px)}.player .animate-btn{width:57px;height:57px;opacity:1}@media (max-width: 991.98px){.player .animate-btn{-webkit-box-ordinal-group:4;-webkit-order:3;-ms-flex-order:3;order:3}}.player .animate-btn:hover{opacity:.7}.player .animate-btn:after{width:10px;height:16px;top:calc(50% - 8px)}.player .animate-btn.play:after{left:calc(50% - 3px);background:url(img/btn-play.svg) no-repeat}.player .animate-btn.pause:after{left:calc(50% - 5px);background:url(img/btn-pause.png) no-repeat}@media (max-width: 991.98px){.player .next-btn{-webkit-box-ordinal-group:4;-webkit-order:3;-ms-flex-order:3;order:3}}@media (max-width: 575.98px){.player .next-btn{margin-right:0}}.player .next-btn:after{background:url(img/btn-next.svg) no-repeat}@media (max-width: 991.98px){.player .prev-btn{-webkit-box-ordinal-group:3;-webkit-order:2;-ms-flex-order:2;order:2}}.player .prev-btn:after{background:url(img/btn-prev.svg) no-repeat}.player .timeline-wrapper{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}@media (max-width: 390px){.player .timeline-wrapper{display:block;text-align:center;margin-right:10px}}.player .date{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;font-size:15px;margin-right:10px;text-align:center;border-radius:15px;cursor:pointer;background:rgba(11,21,90,0.7)}@media (max-width: 390px){.player .date{margin-right:0}}.player .date:hover .lock-btn:after{background-position:0 0}.player .date.lock{background:#0b155a}.player .date.lock:hover .lock-btn:after{background-position:-8px 0}.player .date.lock .lock-btn{border:0;-webkit-box-shadow:none;box-shadow:none}.player .date.lock .lock-btn:after{background-position:0 0}.player .date span{padding:3px 6px 4px 12px}.player .date .lock-btn{width:26px;height:26px;margin-right:2px;opacity:1;border:1px solid #fff}.player .date .lock-btn:after{content:'';display:inline-block;width:8px;height:11px;background:url(img/btn-lock.svg) no-repeat -8px 0;background-size:cover}.player .date .lock-btn:hover:after{background-position:0 0}.player .timeline{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;height:30px;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;position:relative;border-radius:100px;background-color:rgba(11,21,90,0.7)}@media (max-width: 991.98px){.player .timeline{padding:0;-webkit-box-ordinal-group:2;-webkit-order:1;-ms-flex-order:1;order:1;margin-right:20px}}@media (max-width: 390px){.player .timeline{display:inline-block;margin:4px 0 0 0}}.player .timeline .hour{cursor:pointer}@media (max-width: 991.98px){.player .timeline .hour{display:none}}.player .timeline .hour:nth-of-type(2){margin-left:10px}.player .timeline .hour:before{content:"";display:inline-block;width:6px;height:6px;border-radius:50%;background:#fff}.player .timeline .hour:after{content:"";display:inline-block;position:relative;top:-2px;width:20px;height:2px;background:#fff}.player .timeline .end-dot:before{content:"";display:inline-block;width:6px;height:6px;margin-right:10px;border-radius:50%;background:#fff}@media (max-width: 991.98px){.player .timeline .end-dot:before{display:none}}.player .time{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;width:64px;padding:3px 0 4px 0;z-index:1000;font-size:15px;border-radius:15px;cursor:ew-resize;background:#0b155a}@media (min-width: 992px){.player .time{position:absolute}.player .time.hour-0{left:0}.player .time.hour-1{left:25px}.player .time.hour-2{left:50px}.player .time.hour-3{left:75px}.player .time.hour-4{left:100px}.player .time.hour-5{left:125px}.player .time.hour-6{left:150px}.player .time.hour-7{left:175px}.player .time.hour-8{left:200px}.player .time.hour-9{left:225px}.player .time.hour-10{left:250px}.player .time.hour-11{left:275px}.player .time.hour-12{left:300px}.player .time.hour-13{left:325px}.player .time.hour-14{left:350px}.player .time.hour-15{left:375px}.player .time.hour-16{left:400px}.player .time.hour-17{left:425px}.player .time.hour-18{left:450px}.player .time.hour-19{left:475px}.player .time.hour-20{left:500px}.player .time.hour-21{left:525px}.player .time.hour-22{left:550px}.player .time.hour-23{left:585px}}.player .time .custom-spinner-border{margin:3px 0 4px 0}.leaflet-popup-content-wrapper,.leaflet-popup-tip{font-family:'Be Vietnam', sans-serif;text-align:center;color:#fff}.leaflet-popup-content-wrapper strong,.leaflet-popup-tip strong{display:inline-block;margin-bottom:8px}.leaflet-popup-content-wrapper #place-intesity,.leaflet-popup-tip #place-intesity{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;font-family:monospace;font-size:24pt;font-weight:700}.leaflet-popup-content-wrapper #place-intesity #sum-number,.leaflet-popup-tip #place-intesity #sum-number{margin-left:5px;font-size:11pt}.leaflet-popup-content-wrapper .popup-pagination,.leaflet-popup-tip .popup-pagination{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;margin:1em 0 1em 0}.leaflet-popup-content-wrapper .popup-pagination button,.leaflet-popup-tip .popup-pagination button{height:32px;width:32px;border:0;border-radius:24px;background:url(img/btn-prev.svg) rgba(255,255,255,0.1) no-repeat center center;-webkit-transition:background 0.3s ease-out;transition:background 0.3s ease-out;outline:none !important}.leaflet-popup-content-wrapper .popup-pagination button:hover,.leaflet-popup-tip .popup-pagination button:hover{background:url(img/btn-prev.svg) rgba(125,125,125,0.1) no-repeat center center}.leaflet-popup-content-wrapper .popup-pagination button.next,.leaflet-popup-tip .popup-pagination button.next{background:url(img/btn-next.svg) rgba(255,255,255,0.1) no-repeat center center}.leaflet-popup-content-wrapper .popup-pagination button.next:hover,.leaflet-popup-tip .popup-pagination button.next:hover{background:url(img/btn-next.svg) rgba(125,125,125,0.1) no-repeat center center}.leaflet-popup-content-wrapper .popup-pagination p,.leaflet-popup-tip .popup-pagination p{margin:auto}
 
-@media (max-width: 767.98px) {
-  html.intro, body.intro {
-    height: calc(100% - 45px);
-  }
-}
-
-a {
-  color: #ffffff;
-  outline: none;
-}
-
-a:hover {
-  color: #ffffff;
-}
-
-h1 {
-  text-transform: uppercase;
-  font-size: 21px;
-  font-weight: 700;
-  letter-spacing: .53px;
-}
-
-@media (min-width: 992px) {
-  h1 {
-    line-height: 55px;
-  }
-}
-
-h2 {
-  font-size: 16px;
-  font-weight: 400;
-  letter-spacing: .4px;
-}
-
-@media (min-width: 992px) {
-  h2 {
-    margin: 0;
-  }
-}
-
-h3 {
-  font-size: 38px;
-  font-weight: 800;
-  letter-spacing: .95px;
-}
-
-@media (max-width: 575.98px) {
-  h3 {
-    font-size: 28px;
-  }
-}
-
-hr {
-  width: 300px;
-  text-align: left;
-  margin: 30px 0;
-  border-top: 2px solid #ffffff;
-}
-
-@media (max-width: 1199.98px) {
-  hr {
-    width: 90%;
-  }
-}
-
-@media (max-width: 575.98px) {
-  hr {
-    margin: 15px 0;
-  }
-}
-
-header {
-  height: 70px;
-  background: #0048A9;
-}
-
-header .logo {
-  width: 450px;
-  height: 181px;
-  position: absolute;
-  top: 70px;
-  left: 0;
-  z-index: 2;
-  background: url("../img/header-bg.png");
-}
-
-@media (max-width: 1400px) {
-  header .logo {
-    display: none;
-  }
-}
-
-header .logo img {
-  height: 100px;
-  position: relative;
-  top: -50px;
-  left: 50px;
-}
-
-header nav.navbar {
-  height: 70px;
-  position: relative;
-}
-
-header .navbar-toggler-icon {
-  background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgb(255,255,255)' stroke-width='3' stroke-linecap='round' stroke-miterlimit='10' d='M4 8h24M4 16h24M4 24h24'/%3E%3C/svg%3E");
-}
-
-@media (min-width: 1401px) {
-  header .navbar-brand.shifted {
-    position: absolute;
-    top: 0;
-    left: 450px;
-  }
-}
-
-@media (max-width: 1199.98px) {
-  header .navbar-brand.shifted {
-    left: 20px;
-  }
-}
-
-@media (max-width: 991.98px) {
-  header #navigation {
-    width: 100%;
-    padding: 20px;
-    position: absolute;
-    top: 70px;
-    left: 0;
-    z-index: 1001;
-    opacity: .98;
-    background: #004fb3;
-  }
-}
-
-header .nav-item {
-  margin-right: 40px;
-}
-
-@media (max-width: 1480px) {
-  header .nav-item {
-    margin-right: 30px;
-  }
-}
-
-@media (max-width: 1440px) {
-  header .nav-item {
-    margin-right: 15px;
-  }
-}
-
-@media (max-width: 1199.98px) {
-  header .nav-item {
-    margin-right: 30px;
-  }
-}
-
-@media (max-width: 991.98px) {
-  header .nav-item {
-    margin-right: 0;
-  }
-}
-
-header .nav-item .active {
-  font-weight: 800;
-}
-
-header .nav-item.button .nav-link {
-  padding-left: 22px;
-  padding-right: 22px;
-  border: 1px solid #ffffff;
-  border-radius: 20px;
-}
-
-header .nav-item.button .nav-link:hover {
-  background: #004fb3;
-}
-
-header .navbar-button {
-  padding: .5rem 22px;
-  margin-right: 40px;
-  border: 1px solid #ffffff;
-  border-radius: 20px;
-  text-decoration: none;
-}
-
-@media (max-width: 991.98px) {
-  header .navbar-button {
-    display: none;
-  }
-}
-
-header .navbar-button:hover {
-  background: #004fb3;
-}
-
-header .btn-secondary {
-  height: 100%;
-  font-size: 16px;
-  text-transform: uppercase;
-  font-weight: 800;
-  letter-spacing: .4px;
-  border-radius: 0;
-  border: #0048A9;
-  background: #004fb3;
-}
-
-header .btn-secondary:hover {
-  background: #0048A9 !important;
-}
-
-.swiper-container {
-  height: 100%;
-  position: relative;
-}
-
-.swiper-container .btn-primary {
-  width: 300px;
-  height: 50px;
-  line-height: 32px;
-  position: relative;
-  padding-left: 30px;
-  margin-top: 40px;
-  text-align: left;
-  font-size: 16px;
-  text-transform: uppercase;
-  letter-spacing: 0.4px;
-  border-radius: 30px;
-  border: 1px solid #0b155a;
-  background: #0b155a;
-}
-
-.swiper-container .btn-primary:hover, .swiper-container .btn-primary:focus, .swiper-container .btn-primary:active {
-  border: 1px solid #0048A9;
-  background: #0048A9 !important;
-}
-
-@media (max-width: 575.98px) {
-  .swiper-container .btn-primary {
-    width: 100%;
-    max-width: 270px;
-    height: auto;
-    margin-top: 20px;
-  }
-}
-
-.swiper-container .btn-secondary {
-  background: #0b155a;
-}
-
-.swiper-container .btn-secondary:hover, .swiper-container .btn-secondary:focus, .swiper-container .btn-secondary:active {
-  background: rgba(255, 255, 255, 0.1) !important;
-}
-
-.swiper-slide {
-  display: flex;
-  justify-content: center;
-  align-items: center;
-}
-
-.swiper-slide.slide-bg-1 {
-  background: url("../img/bg-1.jpg") no-repeat top center;
-  background-size: cover;
-}
-
-.swiper-slide.slide-bg-2 {
-  background: url("../img/bg-2.jpg") no-repeat bottom center;
-  background-size: cover;
-}
-
-.swiper-slide.slide-bg-3 {
-  background: url("../img/bg-3.jpg") no-repeat bottom center;
-  background-size: cover;
-}
-
-.swiper-slide.slide-bg-3 .slide-text {
-  width: 100%;
-}
-
-@media (max-width: 767.98px) {
-  .swiper-slide .container {
-    max-width: 100%;
-    height: 100%;
-    overflow-y: scroll;
-  }
-}
-
-.slide-text {
-  width: 600px;
-  padding-bottom: 160px;
-  position: relative;
-}
-
-@media (max-width: 1199.98px) {
-  .slide-text {
-    width: 100%;
-  }
-}
-
-@media (max-width: 1199.98px) {
-  .slide-text {
-    padding-bottom: 80px;
-  }
-}
-
-.slide-text .slide-index {
-  display: inline-block;
-  padding-bottom: 20px;
-  font-size: 72px;
-  font-weight: 800;
-}
-
-@media (max-width: 575.98px) {
-  .slide-text .slide-index {
-    padding-bottom: 10px;
-    font-size: 48px;
-  }
-}
-
-.slide-text .btn-primary {
-  z-index: 1000;
-}
-
-.slide-text .btn-primary:after {
-  content: "";
-  display: inline-block;
-  width: 10px;
-  height: 16px;
-  position: absolute;
-  top: 16px;
-  right: 20px;
-  background: url("../img/btn-play.svg") no-repeat top right;
-}
-
-.slide-text p {
-  font-size: 18px;
-  font-weight: 600;
-}
-
-@media (max-width: 575.98px) {
-  .slide-text p {
-    font-size: 16px;
-  }
-}
-
-.slide-text p a {
-  text-decoration: underline;
-}
-
-.slide-text p a:hover {
-  text-decoration: none;
-}
-
-.swiper-pagination-buttons {
-  width: 200px;
-  height: 56px;
-  padding: 6px;
-  position: absolute;
-  right: 150px;
-  bottom: 20px;
-  z-index: 1;
-  border-radius: 100px;
-  background: #0b155a;
-}
-
-@media (max-width: 1199.98px) {
-  .swiper-pagination-buttons {
-    right: 20px;
-  }
-}
-
-@media (max-width: 767.98px) {
-  .swiper-pagination-buttons {
-    width: 120px;
-  }
-}
-
-@media (max-width: 575.98px) {
-  .swiper-pagination-buttons {
-    bottom: 10px;
-  }
-}
-
-.swiper-pagination-buttons .btn-prev, .swiper-pagination-buttons .btn-next {
-  display: inline-block;
-  width: 45px;
-  height: 45px;
-  position: relative;
-  border-radius: 50%;
-  border: 2px solid #ffffff;
-}
-
-.swiper-pagination-buttons .btn-prev:after, .swiper-pagination-buttons .btn-next:after {
-  content: "";
-  display: inline-block;
-  width: 7px;
-  height: 12px;
-  position: absolute;
-  top: calc(50% - 6px);
-  left: calc(50% - 4px);
-}
-
-.swiper-pagination-buttons .btn-prev:after {
-  background: url("../img/btn-prev.svg") no-repeat top right;
-}
-
-.swiper-pagination-buttons .btn-next {
-  float: right;
-}
-
-.swiper-pagination-buttons .btn-next:after {
-  background: url("../img/btn-next.svg") no-repeat top right;
-}
-
-.swiper-pagination-bullets {
-  display: inline-block;
-  width: auto !important;
-  height: 30px;
-  position: absolute;
-  left: 20px !important;
-  bottom: 30px !important;
-  border-radius: 100px;
-  opacity: 0.8;
-  background: #0b155a;
-}
-
-@media (min-width: 992px) {
-  .swiper-pagination-bullets {
-    left: 50% !important;
-    transform: translateX(-50%);
-  }
-}
-
-@media (max-width: 575.98px) {
-  .swiper-pagination-bullets {
-    display: none;
-  }
-}
-
-.swiper-pagination-bullets:before {
-  content: "";
-  width: 100px;
-  position: absolute;
-  top: 13px;
-  left: 15px;
-  z-index: 1;
-  border-top: 2px solid #ffffff;
-}
-
-.swiper-pagination-bullets .swiper-pagination-bullet {
-  width: 20px;
-  height: 20px;
-  position: relative;
-  margin: 4px 15px !important;
-  opacity: 1;
-  border: 2px solid #0b155a;
-  background: #0b155a;
-}
-
-.swiper-pagination-bullets .swiper-pagination-bullet:before {
-  content: "";
-  display: inline-block;
-  width: 6px;
-  height: 6px;
-  position: absolute;
-  top: 5px;
-  left: 5px;
-  border-radius: 50%;
-  background: #ffffff;
-}
-
-.swiper-pagination-bullets .swiper-pagination-bullet:first-of-type {
-  margin-left: 5px !important;
-}
-
-.swiper-pagination-bullets .swiper-pagination-bullet:last-of-type {
-  margin-right: 5px !important;
-}
-
-.swiper-pagination-bullets .swiper-pagination-bullet-active {
-  border: 2px solid #ffffff;
-}
-
-.map-point {
-  width: 50px;
-  height: 50px;
-  z-index: 1;
-  position: absolute;
-  border-radius: 50%;
-  cursor: pointer;
-  border: 2px solid #ffffff;
-}
-
-@media not all and (min-width: 1900px) and (max-width: 2100px) {
-  .map-point {
-    display: none;
-  }
-}
-
-.map-point:before {
-  content: "";
-  display: inline-block;
-  width: 16px;
-  height: 16px;
-  position: absolute;
-  top: calc(50% - 8px);
-  left: calc(50% - 8px);
-  background: url("../img/map-point-plus.svg") no-repeat;
-}
-
-.map-point:hover:before {
-  z-index: 1;
-  top: calc(50% - 1px);
-  background: url("../img/map-point-minus.svg") no-repeat;
-}
-
-.map-point:hover .desc {
-  display: block;
-  width: 220px;
-  padding: 10px 20px;
-  z-index: 0;
-  opacity: 0.75;
-  border-radius: 25px 0 25px 0;
-  background: #0b155a;
-}
-
-.map-point .desc {
-  display: none;
-}
-
-.map-point p {
-  font-size: 14px;
-  font-weight: 400;
-}
-
-.map-point p:last-of-type {
-  margin-bottom: 10px;
-}
-
-.map-point strong {
-  display: inline-block;
-  width: 100%;
-  padding: 0 0 20px 40px;
-  font-size: 16px;
-}
-
-.map-point .circle {
-  width: 50px;
-  height: 50px;
-  position: absolute;
-  top: -2px;
-  left: -2px;
-  border-radius: 50%;
-  border: 2px solid #ffffff;
-}
-
-.map-point.point-1 {
-  top: 470px;
-  right: 32.2%;
-}
-
-.map-point.point-2 {
-  top: 688px;
-  right: 21.2%;
-}
-
-.map-point.point-3 {
-  bottom: 430px;
-  right: 20.8%;
-}
-
-.slide-background {
-  width: 650px;
-  height: 290px;
-  z-index: 1;
-  position: absolute;
-  bottom: 0;
-  left: 0;
-  background: url("../img/footer-bg.svg") no-repeat;
-}
-
-@media (max-width: 991.98px) {
-  .slide-background {
-    width: 320px;
-    height: 143px;
-    background-size: 100%;
-  }
-}
-
-div.copyright p {
-  font-weight: 400;
-}
-
-div.copyright span {
-  font-size: 17px;
-}
-
-p.logos-partners {
-  margin-top: 40px;
-  text-align: right;
-}
-
-p.logos-partners a {
-  text-decoration: none;
-}
-
-p.logos-partners a img {
-  margin-left: 50px;
-  height: 80px;
-}
-
-@media only screen and (max-width: 430px) {
-  p.logos-partners a img {
-    height: 70px;
-    margin-left: 0;
-  }
-}
-
-@media only screen and (max-width: 340px) {
-  p.logos-partners a img {
-    height: 60px;
-  }
-}
-
-p.logos-partners a:first-of-type img {
-  margin-left: 0;
-}
-
-#heatmap {
-  height: 100%;
-}
-
-@media (max-width: 991.98px) {
-  header.map .navbar-nav {
-    width: 100%;
-  }
-}
-
-header.map form {
-  display: flex;
-  margin: auto;
-}
-
-header.map label {
-  margin: 0 0 0 15px;
-  font-size: 16px;
-  letter-spacing: .4px;
-}
-
-header.map input {
-  letter-spacing: .4px;
-  color: #ffffff;
-  border-color: #0048A9;
-  background: #0048A9;
-}
-
-header.map input::placeholder {
-  color: #ffffff;
-  opacity: 1;
-}
-
-header.map input:hover {
-  background: rgba(255, 255, 255, 0.1);
-}
-
-header.map input:focus {
-  background: #ffffff;
-  color: #000;
-}
-
-header.map .nav-item {
-  padding: 5px;
-  margin: auto 0;
-  border-right: 1px solid #1c6cc0;
-}
-
-@media (max-width: 991.98px) {
-  header.map .nav-item {
-    margin-bottom: 10px;
-    border: 0;
-  }
-}
-
-header.map .nav-item:nth-of-type(1), header.map .nav-item:nth-of-type(2), header.map .nav-item:nth-of-type(3) {
-  width: 300px;
-}
-
-@media (max-width: 1500px) {
-  header.map .nav-item:nth-of-type(1), header.map .nav-item:nth-of-type(2), header.map .nav-item:nth-of-type(3) {
-    width: 280px;
-  }
-}
-
-@media (max-width: 1400px) {
-  header.map .nav-item:nth-of-type(1), header.map .nav-item:nth-of-type(2), header.map .nav-item:nth-of-type(3) {
-    width: 230px;
-  }
-}
-
-@media (max-width: 1400px) {
-  header.map .nav-item:nth-of-type(1), header.map .nav-item:nth-of-type(2), header.map .nav-item:nth-of-type(3) {
-    max-width: 230px;
-    width: auto;
-  }
-}
-
-@media (max-width: 991.98px) {
-  header.map .nav-item:nth-of-type(1), header.map .nav-item:nth-of-type(2), header.map .nav-item:nth-of-type(3) {
-    max-width: none;
-  }
-}
-
-header.map .nav-item .dropdown {
-  min-width: 200px;
-}
-
-header.map .nav-item .dropdown .btn {
-  width: 100%;
-  text-align: left;
-  letter-spacing: .4px;
-  color: #ffffff;
-  background: #0048A9;
-}
-
-header.map .nav-item .dropdown .btn:hover {
-  background: rgba(255, 255, 255, 0.1);
-}
-
-header.map .nav-item .dropdown .dropdown-menu {
-  width: 100%;
-  max-height: 300px;
-  overflow-y: auto;
-  z-index: 1001;
-}
-
-header.map .nav-item .dropdown .dropdown-menu .dropdown-item:active {
-  background: #0048A9;
-}
-
-header.map .nav-item .dropdown .dropdown-menu .dropdown-item label {
-  width: 100% !important;
-  margin-left: 0;
-  cursor: pointer;
-}
-
-header.map .nav-item .dropdown #dropdown-time .dropdown-item:nth-of-type(even) {
-  background: rgba(0, 0, 0, 0.05);
-}
-
-header.map .nav-item .dropdown #dropdown-time .dropdown-item:nth-of-type(even):active {
-  background: #0048A9;
-}
-
-header.map .nav-item .dropdown input[type="radio"]:checked ~ label {
-  font-weight: 800;
-}
-
-header.map .nav-item:nth-child(3) {
-  padding: 0;
-  margin: 0;
-  border: 0;
-}
-
-@media (max-width: 991.98px) {
-  header.map .nav-item:nth-child(3) {
-    padding: 5px;
-  }
-}
-
-header.map .nav-item:nth-child(3) .dropdown {
-  display: flex;
-  height: 100%;
-}
-
-@media (max-width: 991.98px) {
-  header.map .nav-item:nth-child(3) .dropdown {
-    flex-direction: column;
-  }
-}
-
-header.map .nav-item:nth-child(3) .dropdown .dropdown-menu {
-  top: calc(100% - 5px);
-}
-
-header.map .nav-item:nth-child(3) .dropdown button {
-  padding-left: 20px;
-  border-radius: 0;
-}
-
-@media (max-width: 991.98px) {
-  header.map .nav-item:nth-child(3) .dropdown button {
-    padding-left: 12px;
-    border-radius: 5px;
-  }
-}
-
-header.map .nav-item:last-child {
-  margin-top: 0;
-  margin-bottom: 0;
-  border-left: 1px solid #1c6cc0;
-  background: #0057be;
-}
-
-@media (max-width: 991.98px) {
-  header.map .nav-item:last-child {
-    margin: 10px 5px 0 5px;
-    border-radius: 5px;
-    border: 0;
-  }
-}
-
-header.map .nav-item:last-child:hover {
-  background: #004fb3;
-}
-
-header.map .nav-item:last-child .btn-secondary {
-  display: inline-block;
-  outline: none;
-  padding: 0 30px;
-  background: url("../img/btn-refresh.svg") no-repeat center center;
-}
-
-header.map .nav-item:last-child .btn-secondary:hover {
-  background: url("../img/btn-refresh.svg") no-repeat center center !important;
-}
-
-@media (max-width: 991.98px) {
-  header.map .nav-item:last-child .btn-secondary {
-    width: 100%;
-    padding: 20px;
-    border-radius: 5px;
-  }
-}
-
-.map-locations {
-  position: absolute;
-  top: 95px;
-  right: 25px;
-  z-index: 1000;
-  border-radius: 22px;
-  background-color: rgba(11, 21, 90, 0.7);
-}
-
-.map-locations .mobile {
-  display: none;
-}
-
-@media (max-width: 991.98px) {
-  .map-locations {
-    top: 90px;
-    right: 20px;
-  }
-  .map-locations .desktop {
-    display: none;
-  }
-  .map-locations .mobile {
-    display: inline-block;
-  }
-}
-
-@media (max-width: 575.98px) {
-  .map-locations {
-    max-width: 240px;
-    max-height: 70%;
-    top: 80px;
-    right: 10px;
-  }
-}
-
-.map-locations .menu {
-  width: 100%;
-  display: inline-block;
-  position: relative;
-  padding: 10px 60px 13px 16px;
-  text-decoration: none;
-  border-radius: 50px;
-  opacity: 1;
-  background: #0b155a;
-}
-
-.map-locations .menu .circle {
-  display: inline-block;
-  width: 40px;
-  height: 40px;
-  position: absolute;
-  top: 3px;
-  right: 3px;
-  border-radius: 50%;
-  border: 2px solid #ffffff;
-}
-
-.map-locations .menu .circle:after {
-  content: "";
-  display: inline-block;
-  width: 16px;
-  height: 16px;
-  position: absolute;
-  top: calc(50% - 8px);
-  left: calc(50% - 8px);
-  background: url("../img/map-point-plus.svg") no-repeat;
-}
-
-.map-locations .locations {
-  z-index: 1000;
-}
-
-.map-locations .locations ul {
-  margin-top: 10px;
-  padding-left: 36px;
-  padding-right: 20px;
-  font-size: 15px;
-  list-style-image: url("../img/li-bullet.svg");
-}
-
-@media (max-width: 575.98px) {
-  .map-locations .locations ul {
-    max-height: 244px;
-    overflow-y: scroll;
-    font-size: 13.5px;
-  }
-}
-
-.map-locations .locations li {
-  margin-bottom: 10px;
-  cursor: pointer;
-}
-
-.map-locations .locations li:hover {
-  text-decoration: underline;
-}
-
-.player {
-  width: 100%;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  position: absolute;
-  left: 50%;
-  transform: translateX(-50%);
-  bottom: -40px;
-  z-index: 1000;
-}
-
-@media (max-width: 575.98px) {
-  .player {
-    bottom: -30px;
-  }
-}
-
-.player .next-btn, .player .prev-btn, .player .animate-btn, .player .lock-btn {
-  display: inline-block;
-  width: 42px;
-  height: 42px;
-  margin-right: 20px;
-  position: relative;
-  border-radius: 50%;
-  opacity: .7;
-  border: 2px solid #ffffff;
-  box-shadow: 0 0 0 4px #0b155a;
-  background: #0b155a;
-  cursor: pointer;
-}
-
-@media (max-width: 320px) {
-  .player .next-btn, .player .prev-btn, .player .animate-btn, .player .lock-btn {
-    margin-right: 15px;
-  }
-}
-
-.player .next-btn:hover, .player .prev-btn:hover, .player .animate-btn:hover, .player .lock-btn:hover {
-  opacity: 1;
-}
-
-.player .next-btn:after, .player .prev-btn:after, .player .animate-btn:after, .player .lock-btn:after {
-  content: "";
-  display: inline-block;
-  width: 7px;
-  height: 12px;
-  position: absolute;
-  top: calc(50% - 6px);
-  left: calc(50% - 4px);
-}
-
-.player .animate-btn {
-  width: 57px;
-  height: 57px;
-  opacity: 1;
-}
-
-@media (max-width: 991.98px) {
-  .player .animate-btn {
-    order: 3;
-  }
-}
-
-.player .animate-btn:hover {
-  opacity: .7;
-}
-
-.player .animate-btn:after {
-  width: 10px;
-  height: 16px;
-  top: calc(50% - 8px);
-}
-
-.player .animate-btn.play:after {
-  left: calc(50% - 3px);
-  background: url("../img/btn-play.svg") no-repeat;
-}
-
-.player .animate-btn.pause:after {
-  left: calc(50% - 5px);
-  background: url("../img/btn-pause.png") no-repeat;
-}
-
-@media (max-width: 991.98px) {
-  .player .next-btn {
-    order: 3;
-  }
-}
-
-@media (max-width: 575.98px) {
-  .player .next-btn {
-    margin-right: 0;
-  }
-}
-
-.player .next-btn:after {
-  background: url("../img/btn-next.svg") no-repeat;
-}
-
-@media (max-width: 991.98px) {
-  .player .prev-btn {
-    order: 2;
-  }
-}
-
-.player .prev-btn:after {
-  background: url("../img/btn-prev.svg") no-repeat;
-}
-
-.player .timeline-wrapper {
-  display: flex;
-}
-
-@media (max-width: 390px) {
-  .player .timeline-wrapper {
-    display: block;
-    text-align: center;
-    margin-right: 10px;
-  }
-}
-
-.player .date {
-  display: flex;
-  align-items: center;
-  align-content: center;
-  font-size: 15px;
-  margin-right: 10px;
-  text-align: center;
-  border-radius: 15px;
-  cursor: pointer;
-  background: rgba(11, 21, 90, 0.7);
-}
-
-@media (max-width: 390px) {
-  .player .date {
-    margin-right: 0;
-  }
-}
-
-.player .date:hover .lock-btn:after {
-  background-position: 0 0;
-}
-
-.player .date.lock {
-  background: #0b155a;
-}
-
-.player .date.lock:hover .lock-btn:after {
-  background-position: -8px 0;
-}
-
-.player .date.lock .lock-btn {
-  border: 0;
-  box-shadow: none;
-}
-
-.player .date.lock .lock-btn:after {
-  background-position: 0 0;
-}
-
-.player .date span {
-  padding: 3px 6px 4px 12px;
-}
-
-.player .date .lock-btn {
-  width: 26px;
-  height: 26px;
-  margin-right: 2px;
-  opacity: 1;
-  border: 1px solid #ffffff;
-}
-
-.player .date .lock-btn:after {
-  content: '';
-  display: inline-block;
-  width: 8px;
-  height: 11px;
-  background: url("../img/btn-lock.svg") no-repeat -8px 0;
-  background-size: cover;
-}
-
-.player .date .lock-btn:hover:after {
-  background-position: 0 0;
-}
-
-.player .timeline {
-  display: flex;
-  height: 30px;
-  flex-direction: row;
-  position: relative;
-  border-radius: 100px;
-  background-color: rgba(11, 21, 90, 0.7);
-}
-
-@media (max-width: 991.98px) {
-  .player .timeline {
-    padding: 0;
-    order: 1;
-    margin-right: 20px;
-  }
-}
-
-@media (max-width: 390px) {
-  .player .timeline {
-    display: inline-block;
-    margin: 4px 0 0 0;
-  }
-}
-
-.player .timeline .hour {
-  cursor: pointer;
-}
-
-@media (max-width: 991.98px) {
-  .player .timeline .hour {
-    display: none;
-  }
-}
-
-.player .timeline .hour:nth-of-type(2) {
-  margin-left: 10px;
-}
-
-.player .timeline .hour:before {
-  content: "";
-  display: inline-block;
-  width: 6px;
-  height: 6px;
-  border-radius: 50%;
-  background: #ffffff;
-}
-
-.player .timeline .hour:after {
-  content: "";
-  display: inline-block;
-  position: relative;
-  top: -2px;
-  width: 20px;
-  height: 2px;
-  background: #ffffff;
-}
-
-.player .timeline .end-dot:before {
-  content: "";
-  display: inline-block;
-  width: 6px;
-  height: 6px;
-  margin-right: 10px;
-  border-radius: 50%;
-  background: #ffffff;
-}
-
-@media (max-width: 991.98px) {
-  .player .timeline .end-dot:before {
-    display: none;
-  }
-}
-
-.player .time {
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  width: 64px;
-  padding: 3px 0 4px 0;
-  z-index: 1000;
-  font-size: 15px;
-  border-radius: 15px;
-  cursor: ew-resize;
-  background: #0b155a;
-}
-
-@media (min-width: 992px) {
-  .player .time {
-    position: absolute;
-  }
-  .player .time.hour-0 {
-    left: 0;
-  }
-  .player .time.hour-1 {
-    left: 25px;
-  }
-  .player .time.hour-2 {
-    left: 50px;
-  }
-  .player .time.hour-3 {
-    left: 75px;
-  }
-  .player .time.hour-4 {
-    left: 100px;
-  }
-  .player .time.hour-5 {
-    left: 125px;
-  }
-  .player .time.hour-6 {
-    left: 150px;
-  }
-  .player .time.hour-7 {
-    left: 175px;
-  }
-  .player .time.hour-8 {
-    left: 200px;
-  }
-  .player .time.hour-9 {
-    left: 225px;
-  }
-  .player .time.hour-10 {
-    left: 250px;
-  }
-  .player .time.hour-11 {
-    left: 275px;
-  }
-  .player .time.hour-12 {
-    left: 300px;
-  }
-  .player .time.hour-13 {
-    left: 325px;
-  }
-  .player .time.hour-14 {
-    left: 350px;
-  }
-  .player .time.hour-15 {
-    left: 375px;
-  }
-  .player .time.hour-16 {
-    left: 400px;
-  }
-  .player .time.hour-17 {
-    left: 425px;
-  }
-  .player .time.hour-18 {
-    left: 450px;
-  }
-  .player .time.hour-19 {
-    left: 475px;
-  }
-  .player .time.hour-20 {
-    left: 500px;
-  }
-  .player .time.hour-21 {
-    left: 525px;
-  }
-  .player .time.hour-22 {
-    left: 550px;
-  }
-  .player .time.hour-23 {
-    left: 585px;
-  }
-}
-
-.player .time .custom-spinner-border {
-  margin: 3px 0 4px 0;
-}
-
-.leaflet-popup-content-wrapper, .leaflet-popup-tip {
-  font-family: 'Be Vietnam', sans-serif;
-  text-align: center;
-  color: #ffffff;
-}
-
-.leaflet-popup-content-wrapper strong, .leaflet-popup-tip strong {
-  display: inline-block;
-  margin-bottom: 8px;
-}
-
-.leaflet-popup-content-wrapper #place-intesity, .leaflet-popup-tip #place-intesity {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  font-family: monospace;
-  font-size: 24pt;
-  font-weight: 700;
-}
-
-.leaflet-popup-content-wrapper #place-intesity #sum-number, .leaflet-popup-tip #place-intesity #sum-number {
-  margin-left: 5px;
-  font-size: 11pt;
-}
-
-.leaflet-popup-content-wrapper .popup-pagination, .leaflet-popup-tip .popup-pagination {
-  display: flex;
-  margin: 1em 0 1em 0;
-}
-
-.leaflet-popup-content-wrapper .popup-pagination button, .leaflet-popup-tip .popup-pagination button {
-  height: 32px;
-  width: 32px;
-  border: 0;
-  border-radius: 24px;
-  background: url("../img/btn-prev.svg") rgba(255, 255, 255, 0.1) no-repeat center center;
-  transition: background 0.3s ease-out;
-  outline: none !important;
-}
-
-.leaflet-popup-content-wrapper .popup-pagination button:hover, .leaflet-popup-tip .popup-pagination button:hover {
-  background: url("../img/btn-prev.svg") rgba(125, 125, 125, 0.1) no-repeat center center;
-}
-
-.leaflet-popup-content-wrapper .popup-pagination button.next, .leaflet-popup-tip .popup-pagination button.next {
-  background: url("../img/btn-next.svg") rgba(255, 255, 255, 0.1) no-repeat center center;
-}
-
-.leaflet-popup-content-wrapper .popup-pagination button.next:hover, .leaflet-popup-tip .popup-pagination button.next:hover {
-  background: url("../img/btn-next.svg") rgba(125, 125, 125, 0.1) no-repeat center center;
-}
-
-.leaflet-popup-content-wrapper .popup-pagination p, .leaflet-popup-tip .popup-pagination p {
-  margin: auto;
-}
diff --git a/website/public/js/index.bundle.js b/website/public/js/index.bundle.js
index 9a2f100..f218225 100644
--- a/website/public/js/index.bundle.js
+++ b/website/public/js/index.bundle.js
@@ -1 +1 @@
-var js;js=function(){var t={157:function(t){var e,a,n=null,o=null,s=49.7248,p=13.3521,r=17;let l,i,c;var u,d=!1,m=[],h=[];let g=0;const y={};var b=[];let f=!1,v=0;const w={},x={};let k={};const C={coord:{lat:0,lng:0},_popup:null},N=async(t,e)=>{const a=new Headers,n=new Request(t,{method:e,headers:a});return(await fetch(n)).json()},P=async(t,e,a,n)=>await N(t+"/"+e+"/"+a+"/"+n,"GET");preload=async(t,e,n)=>{it();for(let o=t+e;o>=0&&o<=23;o+=e)m[o]||(m[o]={}),b.forEach((async t=>{m[o][t]||(m[o][t]=await P(a,t,n,o))}));ct()};const j=async(t,e,n=1e3)=>{pt(),a=t,l=e;const o=[];m[i]={};b.forEach((t=>{o.push((async t=>{t in w||(w[t]=await(async(t,e)=>await N(t+"/"+e,"GET"))(e,t));const n=await P(a,t,W(),i);m[i][t]=n})(t))})),it(n),await Promise.all(o).then((()=>{ct(0),O(w),I(m[i]),preload(i,1,W()),preload(i,-1,W())}))},I=t=>{const a=t,o=[];let s=0;Object.keys(k).length&&(Object.keys(k).forEach((function(t){k[t][0].bindPopup(k[t][1])})),k={}),Object.keys(a).forEach((t=>{const p=a[t];if(s=Math.max(s,p.max),null!=p){const t=p.items.map((t=>{const{x:e,y:a,number:n}=t,o=e+""+a,s=x[o];return!k[o]&&n&&(s[0]=s[0].unbindPopup(),k[o]=s),[e,a,n]}));o.push(...t)}else null!=n&&e.removeLayer(n)})),null!=n&&e.removeLayer(n),o.length&&(n=L.heatLayer(o,{max:s,minOpacity:.5,radius:35,blur:30}).addTo(e)),Z()},O=t=>{null!=o&&e.removeLayer(o),o=L.layerGroup(),Object.keys(t).forEach((e=>{for(var a in t[e]){const{x:n,y:s,name:p}=t[e][a],r=G(n,s,1,`popup-${e}`).setContent(A(y[e],p,0,0,1,1)),l=L.circle([n,s],{radius:2,fillOpacity:.8,color:"#004fb3",fillColor:"#004fb3",bubblingMouseEvents:!0}).bindPopup(r);x[n+""+s]=[l,r],o.addLayer(l)}})),o.setZIndex(-1).addTo(e)},S=(t=null)=>{i=null!==t?t:parseInt($('#dropdown-time input[type="radio"]:checked').val())},T=(t=null)=>{const e=$("#date");c=new Date(t||e.val()),e.val(W()),$("#player-date span").html(`${c.getDate()}. ${c.getMonth()+1}. ${c.getFullYear()}`),m=[]},D=t=>{C._popup.setContent(t),C._popup.openOn(e)},E=(t,e)=>({previousButton:'<button type="button" id="btn-popup-previous-page" onclick="js.setPreviousPageInPopup()"></button>',pagesList:`<p id="pages">${t} z ${e}</p>`,nextButton:'<button type="button" id="btn-popup-next-page" class="next" onclick="js.setNextPageInPopup()"></button>'}),M=()=>{$("#btn-popup-previous-page").prop("disabled",!0),$("#btn-popup-next-page").prop("disabled",!0),$(".popup-pagination").hide()},_=t=>`<div class="popup-pagination">${t?t.reduce(((t,e)=>t+e),""):""}</div>`,B=()=>{const t=Object.keys(h);return 1===t.length?h[t[0]].items.length:t.length},z=t=>{const e=Object.keys(h);return h[e[t]]},G=(t,e,a,n)=>L.popup({autoPan:!1,className:n}).setLatLng([t/a,e/a]),A=(t,e,a,n,o,s)=>{const p=`\n    <strong>${t}</strong>\n    <div id="place-name">${e}</div>`,r=`\n    <div id="place-intesity">\n      <span id="current-number">${a}</span>\n      <span id="sum-number">${n&&n!==Number(a)?"/"+n:""}</span>\n    </div>`,{previousButton:l,nextButton:i,pagesList:c}=E(o,s);return`\n  ${p}\n  ${r}\n  ${_(s>1?[l,c,i]:null)}\n  `},F=()=>Object.keys(h).length>1,H=t=>{const e=$(".leaflet-popup");e.removeClass((function(t,e){return(e.match(/(^|\s)popup-\S+/g)||[]).join(" ")})),e.addClass("popup-"+t)},V=t=>{h=[],g=0;const a=40075016.686*Math.cos(s)/Math.pow(2,e.getZoom())*25/256;let n=0,o=0,p=0,r=0;const l={},c={lng:t.latlng.lng,lat:t.latlng.lat};Object.keys(m[i]).forEach((e=>{m[i][e].items.forEach((s=>{t.latlng.distanceTo(new L.LatLng(s.x,s.y))<a&&(o+=s.x,p+=s.y,h[n]={place:s.place,number:s.number,datasetName:e},r+=parseInt(s.number),n++,l[e]=!0)}))})),h=h.reduce(((t,e)=>(t[e.datasetName]||(t[e.datasetName]={items:[],number:0,datasetName:e.datasetName}),t[e.datasetName].items.push(e),t[e.datasetName].number+=Number(e.number),t)),{});const u=Object.keys(l).length;if(u)if(1===u){const t=z(0),e=t.items,{place:a,number:s}=e[g];C._popup&&tt(C.coord,c)?H(t.datasetName):(C._popup=G(o,p,n,`popup-${t.datasetName}`),C.coord=c),D(A(y[t.datasetName],a,s,r,1,e.length)),1===e.length&&M()}else{const{datasetName:t,number:e}=z(g);C._popup&&tt(C.coord,c)?H(t):(C._popup=G(o,p,n,`popup-${t}`),C.coord=c),D(((t,e,a,n)=>{const o=`<strong id="dataset-name">${n}</strong>`,s=`<div id="place-intesity"><span id="current-number">${t}</span></div>`,{previousButton:p,nextButton:r,pagesList:l}=E(1,a);return`\n  ${o}\n  ${s}\n  ${_([p,l,r])}\n  `})(e,0,B(),y[t]))}else e._popup&&($("#sum-number").text(""),$("#current-number").html(0),M())},Y=t=>{const e=F()?z(t):z(0).items[t],a=F()?z(g):z(0).items[g],n=$("#dataset-name");n&&n.html(y[a.datasetName]),$("#place-name").html(a.place?a.place:a.datasetName),$("#current-number").html(a.number),$("#pages").html(g+1+" z "+B()),$(".leaflet-popup").removeClass(`popup-${e.datasetName}`).addClass(`popup-${a.datasetName}`)},Z=()=>{const{_popup:t}=e;t&&V({latlng:t.getLatLng()})},R=async()=>{v||(i=(i+23)%24,K(i),f||23!==i?I(m[i]):(X(-1),await j(a,l)),Z())},q=async()=>{v||(i=(i+1)%24,K(i),f||0!==i?I(m[i]):(X(1),await j(a,l)),Z())},J=t=>{K(t),I(m[i])},K=t=>{$("#player-time").removeAttr("style"),S(t),at(),nt(),Q(),Z()},Q=()=>{window.history.pushState("",document.title,window.location.origin+window.location.pathname+`?date=${W()}&time=${i}${b.reduce(((t,e)=>t+"&type[]="+e),"")}`)},U=(t,e=!1)=>(e&&t<10?"0":"")+t+":00",W=()=>`${c.getFullYear()}-${(()=>{const t=c.getMonth()+1;return t>9?`${t}`:`0${t}`})()}-${(()=>{const t=c.getDate();return t>9?`${t}`:`0${t}`})()}`,X=t=>{c.setDate(c.getDate()+t),T(c)},tt=(t,e)=>t.lat===e.lat&&t.lng===e.lng,et=(t,e)=>{let a;return function(){const n=this,o=arguments;clearTimeout(a),a=setTimeout((()=>t.apply(n,o)),e)}},at=()=>{$(`#time_${i}`).prop("checked",!0),$("#dropdownMenuButtonTime").html(U(i,!0))},nt=()=>{$("#player-time > span").text(U(i)),$("#player-time").attr("class","time hour-"+i)},ot=()=>{$("#date").change((function(){T($(this).val()),j(a,l,0),Q()})),$('#dropdown-time input[type="radio"]').each((function(){$(this).change((function(){K(parseInt($(this).val())),I(m[i])}))})),$('#dropdown-dataset input[type="checkbox"]').each((function(){$(this).change(et((()=>st(this)),1e3))}))},st=async t=>{$(t).prop("checked")?await j(a,l,0):(pt(),m.forEach(((e,a)=>{Object.keys(e).forEach((e=>{e===$(t).val()&&delete m[a][e]})),I(m[i])}))),Z(),Q()},pt=()=>{b=[],$("#dropdown-dataset .dropdown-item").each((function(){const t=$(this).find("input"),e=t[0].value;t[0].checked&&b.push(e),y[e]=$(t).data("dataset-display-name")}))},rt=async t=>{let e=!1;$("#dropdown-dataset .dropdown-item").each((function(){const a=$(this).find("input");a[0].value in t?(e=!0,$(this).removeClass("disabled")):($(this).addClass("disabled"),$(a).prop("checked",!1))})),$("#btn-update-heatmap").prop("disabled",!e)},lt=(t,e)=>{setTimeout((()=>{t(v),v&&lt(t,e)}),e)},it=(t=1e3)=>{v++,lt((()=>lt((t=>ut(t)),t)))},ct=(t=1e3)=>{v--,lt((()=>lt((t=>ut(t)))),t)},ut=t=>{t?dt():mt()},dt=()=>{$("#player-time > .spinner-border").removeClass("d-none"),$("#player-time > span").text("")},mt=()=>{$("#player-time > .spinner-border").addClass("d-none"),nt()};t.exports={initDatepicker:async t=>{var e="";const a=await N(t,"GET");e=String(a).split(","),$("#date").datepicker({format:"yyyy-mm-dd",language:"cs",beforeShowDay:function(t){return e.indexOf((t=>{var e=String(t.getDate()),a=String(t.getMonth()+1);return 1===e.length&&(e="0"+e),1===a.length&&(a="0"+a),t.getFullYear()+"-"+a+"-"+e})(t))<0?{enabled:!1,tooltip:"Žádná data"}:{enabled:!0}},autoclose:!0})},initLocationsMenu:()=>{const t=$(".locations"),e="show";$(window).width()<=480?t.removeClass(e):t.addClass(e)},initMap:()=>{s=localStorage.getItem("lat")||s,p=localStorage.getItem("lng")||p,r=localStorage.getItem("zoom")||r,e=L.map("heatmap").setView([s,p],r),L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",{attribution:"",maxZoom:19}).addTo(e),e.on("click",(function(t){V(t)}))},onDocumentReady:()=>{$("#dropdown-dataset").on("click",(function(t){t.stopPropagation()})),$("#btn-update-heatmap").prop("name",""),S(),T(),ot(),$(document).keydown((function(t){const{which:e}=t;37===e?(R(),t.preventDefault()):39===e&&(q(),t.preventDefault())}))},checkDataSetsAvailability:async t=>{const e=await N(t+"/"+W(),"POST");rt(e)},loadCurrentTimeHeatmap:j,dragTimeline:()=>{const t=$("#player-time"),e=t.offset().left-t.parent().offset().left;if(e>=0&&e<=t.parent().width()){const a=Math.round(e/26);a!==i&&(t.attr("class","time hour-"+a),$("#player-time span").html(U(a)),J(a))}},setPreviousPageInPopup:()=>{const t=B(),e=g;g=(g+t-1)%t,Y(e)},setNextPageInPopup:()=>{const t=B(),e=g;g=(g+1)%t,Y(e)},previous:R,next:q,toggleDayLock:()=>{f=!f,$("#player-date").toggleClass("lock")},changeAnimationState:()=>{const t=$("#animate-btn");(d=!d)?(t.removeClass("play").addClass("pause"),u=setInterval((function(){q()}),800)):(clearTimeout(u),t.removeClass("pause").addClass("play"))},onChangeHour:J,setMapView:(t,a,n)=>{localStorage.setItem("lat",t),localStorage.setItem("lng",a),localStorage.setItem("zoom",n),e.setView([t,a],n)}}}},e={};return function a(n){if(e[n])return e[n].exports;var o=e[n]={exports:{}};return t[n](o,o.exports,a),o.exports}(157)}();
\ No newline at end of file
+var js;js=function(){var t={157:function(t){var e,a,n=null,o=null,s=49.7248,p=13.3521,r=17;let l,i,c;var u,d=!1,m=[],h=[];let g=0;const y={};var b=[];let f=!1,v=0;const w={},x={};let k={};const C={coord:{lat:0,lng:0},_popup:null},N=async(t,e)=>{const a=new Headers,n=new Request(t,{method:e,headers:a});return(await fetch(n)).json()},P=async(t,e,a,n)=>await N(t+"/"+e+"/"+a+"/"+n,"GET");preload=async(t,e,n)=>{it();for(let o=t+e;o>=0&&o<=23;o+=e)m[o]||(m[o]={}),b.forEach((async t=>{m[o][t]||(m[o][t]=await P(a,t,n,o))}));ct()};const j=async(t,e,n=1e3)=>{pt(),a=t,l=e;const o=[];m[i]={};b.forEach((t=>{o.push((async t=>{t in w||(w[t]=await(async(t,e)=>await N(t+"/"+e,"GET"))(e,t));const n=await P(a,t,W(),i);m[i][t]=n})(t))})),it(n),await Promise.all(o).then((()=>{ct(0),O(w),I(m[i]),preload(i,1,W()),preload(i,-1,W())}))},I=t=>{const a=t,o=[];let s=0;Object.keys(k).length&&(Object.keys(k).forEach((function(t){k[t][0].bindPopup(k[t][1])})),k={}),Object.keys(a).forEach((t=>{const p=a[t];if(s=Math.max(s,p.max),null!=p){const t=p.items.map((t=>{const{x:e,y:a,number:n}=t,o=e+""+a,s=x[o];return!k[o]&&n&&(s[0]=s[0].unbindPopup(),k[o]=s),[e,a,n]}));o.push(...t)}else null!=n&&e.removeLayer(n)})),null!=n&&e.removeLayer(n),o.length&&(n=L.heatLayer(o,{max:s,minOpacity:.5,radius:35,blur:30}).addTo(e)),Z()},O=t=>{null!=o&&e.removeLayer(o),o=L.layerGroup(),Object.keys(t).forEach((e=>{for(var a in t[e]){const{x:n,y:s,name:p}=t[e][a],r=G(n,s,1,`popup-${e}`).setContent(A(y[e],p,0,0,1,1)),l=L.circle([n,s],{radius:2,fillOpacity:.8,color:"#004fb3",fillColor:"#004fb3",bubblingMouseEvents:!0}).bindPopup(r);x[n+""+s]=[l,r],o.addLayer(l)}})),o.setZIndex(-1).addTo(e)},S=(t=null)=>{i=null!==t?t:parseInt($('#dropdown-time input[type="radio"]:checked').val())},T=(t=null)=>{const e=$("#date");c=new Date(t||e.val()),e.val(W()),$("#player-date span").html(`${c.getDate()}. ${c.getMonth()+1}. ${c.getFullYear()}`),m=[]},D=t=>{C._popup.setContent(t),C._popup.openOn(e)},E=(t,e)=>({previousButton:'<button type="button" id="btn-popup-previous-page" onclick="js.setPreviousPageInPopup()"></button>',pagesList:`<p id="pages">${t} z ${e}</p>`,nextButton:'<button type="button" id="btn-popup-next-page" class="next" onclick="js.setNextPageInPopup()"></button>'}),M=()=>{$("#btn-popup-previous-page").prop("disabled",!0),$("#btn-popup-next-page").prop("disabled",!0),$(".popup-pagination").hide()},_=t=>`<div class="popup-pagination">${t?t.reduce(((t,e)=>t+e),""):""}</div>`,B=()=>{const t=Object.keys(h);return 1===t.length?h[t[0]].items.length:t.length},z=t=>{const e=Object.keys(h);return h[e[t]]},G=(t,e,a,n)=>L.popup({autoPan:!1,className:n}).setLatLng([t/a,e/a]),A=(t,e,a,n,o,s)=>{const p=`\n    <strong>${t}</strong>\n    <div id="place-name">${e}</div>`,r=`\n    <div id="place-intesity">\n      <span id="current-number">${a}</span>\n      <span id="sum-number">${n&&n!==Number(a)?"/"+n:""}</span>\n    </div>`,{previousButton:l,nextButton:i,pagesList:c}=E(o,s);return`\n  ${p}\n  ${r}\n  ${_(s>1?[l,c,i]:null)}\n  `},F=()=>Object.keys(h).length>1,H=t=>{const e=$(".leaflet-popup");e.removeClass((function(t,e){return(e.match(/(^|\s)popup-\S+/g)||[]).join(" ")})),e.addClass("popup-"+t)},V=t=>{h=[],g=0;const a=40075016.686*Math.cos(s)/Math.pow(2,e.getZoom())*25/256;let n=0,o=0,p=0,r=0;const l={},c={lng:t.latlng.lng,lat:t.latlng.lat};Object.keys(m[i]).forEach((e=>{m[i][e].items.forEach((s=>{t.latlng.distanceTo(new L.LatLng(s.x,s.y))<a&&(o+=s.x,p+=s.y,h[n]={place:s.place,number:s.number,datasetName:e},r+=parseInt(s.number),n++,l[e]=!0)}))})),h=h.reduce(((t,e)=>(t[e.datasetName]||(t[e.datasetName]={items:[],number:0,datasetName:e.datasetName}),t[e.datasetName].items.push(e),t[e.datasetName].number+=Number(e.number),t)),{});const u=Object.keys(l).length;if(u)if(1===u){const t=z(0),e=t.items,{place:a,number:s}=e[g];C._popup&&tt(C.coord,c)?H(t.datasetName):(C._popup=G(o,p,n,`popup-${t.datasetName}`),C.coord=c),D(A(y[t.datasetName],a,s,r,1,e.length)),1===e.length&&M()}else{const{datasetName:t,number:e}=z(g);C._popup&&tt(C.coord,c)?H(t):(C._popup=G(o,p,n,`popup-${t}`),C.coord=c),D(((t,e,a,n)=>{const o=`<strong id="dataset-name">${n}</strong>`,s=`<div id="place-intesity"><span id="current-number">${t}</span></div>`,{previousButton:p,nextButton:r,pagesList:l}=E(1,a);return`\n  ${o}\n  ${s}\n  ${_([p,l,r])}\n  `})(e,0,B(),y[t]))}else e._popup&&($("#sum-number").text(""),$("#current-number").html(0),M())},Y=t=>{const e=F()?z(t):z(0).items[t],a=F()?z(g):z(0).items[g],n=$("#dataset-name");n&&n.html(y[a.datasetName]),$("#place-name").html(a.place?a.place:a.datasetName),$("#current-number").html(a.number),$("#pages").html(g+1+" z "+B()),$(".leaflet-popup").removeClass(`popup-${e.datasetName}`).addClass(`popup-${a.datasetName}`)},Z=()=>{const{_popup:t}=e;t&&V({latlng:t.getLatLng()})},R=async()=>{v||(i=(i+23)%24,K(i),f||23!==i?I(m[i]):(X(-1),await j(a,l)),Z())},q=async()=>{v||(i=(i+1)%24,K(i),f||0!==i?I(m[i]):(X(1),await j(a,l)),Z())},J=t=>{K(t),I(m[i])},K=t=>{$("#player-time").removeAttr("style"),S(t),at(),nt(),Q(),Z()},Q=()=>{window.history.pushState("",document.title,window.location.origin+window.location.pathname+`?date=${W()}&time=${i}${b.reduce(((t,e)=>t+"&type[]="+e),"")}`)},U=(t,e=!1)=>(e&&t<10?"0":"")+t+":00",W=()=>`${c.getFullYear()}-${(()=>{const t=c.getMonth()+1;return t>9?`${t}`:`0${t}`})()}-${(()=>{const t=c.getDate();return t>9?`${t}`:`0${t}`})()}`,X=t=>{c.setDate(c.getDate()+t),T(c)},tt=(t,e)=>t.lat===e.lat&&t.lng===e.lng,et=(t,e)=>{let a;return function(){const n=this,o=arguments;clearTimeout(a),a=setTimeout((()=>t.apply(n,o)),e)}},at=()=>{$(`#time_${i}`).prop("checked",!0),$("#dropdownMenuButtonTime").html(U(i,!0))},nt=()=>{$("#player-time > span").text(U(i)),$("#player-time").attr("class","time hour-"+i)},ot=()=>{$("#date").change((function(){T($(this).val()),j(a,l,0),Q()})),$('#dropdown-time input[type="radio"]').each((function(){$(this).change((function(){K(parseInt($(this).val())),I(m[i])}))})),$('#dropdown-dataset input[type="checkbox"]').each((function(){$(this).change(et((()=>st(this)),1e3))}))},st=async t=>{$(t).prop("checked")?await j(a,l,0):(pt(),m.forEach(((e,a)=>{Object.keys(e).forEach((e=>{e===$(t).val()&&delete m[a][e]})),I(m[i])}))),Z(),Q()},pt=()=>{b=[],$("#dropdown-dataset .dropdown-item").each((function(){const t=$(this).find("input"),e=t[0].value;t[0].checked&&b.push(e),y[e]=$(t).data("dataset-display-name")}))},rt=async t=>{let e=!1;$("#dropdown-dataset .dropdown-item").each((function(){const a=$(this).find("input");a[0].value in t?(e=!0,$(this).removeClass("disabled")):($(this).addClass("disabled"),$(a).prop("checked",!1))})),$("#btn-update-heatmap").prop("disabled",!e)},lt=(t,e)=>{setTimeout((()=>{t(v),v&&lt(t,e)}),e)},it=(t=1e3)=>{v++,lt((()=>lt((t=>ut(t)),t)))},ct=(t=1e3)=>{v--,lt((()=>lt((t=>ut(t)))),t)},ut=t=>{t?dt():mt()},dt=()=>{$("#player-time > .spinner-border").removeClass("d-none"),$("#player-time > span").text("")},mt=()=>{$("#player-time > .spinner-border").addClass("d-none"),nt()};t.exports={initDatepicker:async t=>{const e=await N(t,"GET"),a=String(e).split(",");$("#date").datepicker({format:"yyyy-mm-dd",language:"cs",beforeShowDay:function(t){return a.indexOf((t=>{var e=String(t.getDate()),a=String(t.getMonth()+1);return 1===e.length&&(e="0"+e),1===a.length&&(a="0"+a),t.getFullYear()+"-"+a+"-"+e})(t))<0?{enabled:!1,tooltip:"Žádná data"}:{enabled:!0}},autoclose:!0})},initLocationsMenu:()=>{const t=$(".locations"),e="show";$(window).width()<=480?t.removeClass(e):t.addClass(e)},initMap:()=>{s=localStorage.getItem("lat")||s,p=localStorage.getItem("lng")||p,r=localStorage.getItem("zoom")||r,e=L.map("heatmap").setView([s,p],r),L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",{attribution:"",maxZoom:19}).addTo(e),e.on("click",(function(t){V(t)}))},onDocumentReady:()=>{$("#dropdown-dataset").on("click",(function(t){t.stopPropagation()})),$("#btn-update-heatmap").prop("name",""),S(),T(),ot(),$(document).keydown((function(t){const{which:e}=t;37===e?(R(),t.preventDefault()):39===e&&(q(),t.preventDefault())}))},checkDataSetsAvailability:async t=>{const e=await N(t+"/"+W(),"POST");rt(e)},loadCurrentTimeHeatmap:j,dragTimeline:()=>{const t=$("#player-time"),e=t.offset().left-t.parent().offset().left;if(e>=0&&e<=t.parent().width()){const a=Math.round(e/26);a!==i&&(t.attr("class","time hour-"+a),$("#player-time span").html(U(a)),J(a))}},setPreviousPageInPopup:()=>{const t=B(),e=g;g=(g+t-1)%t,Y(e)},setNextPageInPopup:()=>{const t=B(),e=g;g=(g+1)%t,Y(e)},previous:R,next:q,toggleDayLock:()=>{f=!f,$("#player-date").toggleClass("lock")},changeAnimationState:()=>{const t=$("#animate-btn");(d=!d)?(t.removeClass("play").addClass("pause"),u=setInterval((function(){q()}),800)):(clearTimeout(u),t.removeClass("pause").addClass("play"))},onChangeHour:J,setMapView:(t,a,n)=>{localStorage.setItem("lat",t),localStorage.setItem("lng",a),localStorage.setItem("zoom",n),e.setView([t,a],n)}}}},e={};return function a(n){if(e[n])return e[n].exports;var o=e[n]={exports:{}};return t[n](o,o.exports,a),o.exports}(157)}();
\ No newline at end of file
