diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 894a44c..0000000 --- a/.gitignore +++ /dev/null @@ -1,104 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ diff --git a/.gitignore b/.gitignore new file mode 120000 index 0000000..c803af3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.pscaffold/.gitignore \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..0f33f75 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,22 @@ +image: python + +test:pylama: + stage: test + only: + - master + - develop + before_script: + - if test -r requirements.txt; then pip install -r requirements.txt; fi + script: + if test -x "$(which pylama)"; then pylama *.py; fi + +test:pytest: + stage: test + only: + - master + - develop + before_script: + - if test -r requirements.txt; then pip install -r requirements.txt; fi + script: + if test -x "$(which pytest)"; then pytest .; fi + diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..09ac888 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule ".pscaffold"] + path = .pscaffold + url = ../pscaffold.git diff --git a/.pscaffold b/.pscaffold new file mode 160000 index 0000000..bc09b49 --- /dev/null +++ b/.pscaffold @@ -0,0 +1 @@ +Subproject commit bc09b49a828808ea9c0b42f80af0b06f90e207cc diff --git a/.style.yapf b/.style.yapf new file mode 120000 index 0000000..e4269e9 --- /dev/null +++ b/.style.yapf @@ -0,0 +1 @@ +.pscaffold/.style.yapf \ No newline at end of file diff --git a/httping.py b/httping.py new file mode 100755 index 0000000..e4e5553 --- /dev/null +++ b/httping.py @@ -0,0 +1,109 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- + +"""Simulates httping for `argos `.""" + +# httping +# v1.0 +# Tobias Schmidl +# schtobia +# Simulates httping. +# +# python,requests +# https://gitlab.com/schtobia/argos-httping/blob/master/httping.py + +from __future__ import annotations + +import datetime # for the whole ms → s and vice versa stuff +import statistics # for median and stdev +import sys # for redirecting print to stderr +from typing import Dict, List, Optional, Tuple + +import requests # for the central HTTP HEAD request + +__author__ = "Tobias Schmidl" +__copyright__ = "Copyright 2021, Tobias Schmidl" +__license__ = "MIT" +__version__ = "1.0" +__maintainer__ = "Tobias Schmidl" + +MAX_PING: datetime.timedelta = datetime.timedelta(milliseconds=1000) # in seconds +TARGETS: List[str] = ["https://www.google.de", "https://www.bing.com", "https://www.wikipedia.org"] + +Bucket = Tuple[int, int] +BucketList = List[Bucket] + +MAX_PING_IN_MS: int = int(MAX_PING.total_seconds() * 1000) + + +def in_bucket(element: int, bucket: Bucket) -> bool: + """Return True if element is in bucket.""" + return bucket[0] <= element <= bucket[1] + + +def generate_buckets(min_val: int, max_val: int, step_width: int = 2) -> BucketList: + """Generate a list of partitioned lists between min_val and max_val.""" + return [(x, x + step_width - 1) for x in range(min_val, max_val, step_width)] + + +class Themes: + """Maintain a list of themes and generates buckets between 0 and MAX_PING_IN_MS based on the selected theme.""" + + # Themes are from http://colorbrewer2.org/ + purple_green: List[str] = ["#762a83", "#9970ab", "#c2a5cf", "#a6dba0", "#5aae61", "#1b7837"] + red_green: List[str] = ["#d73027", "#fc8d59", "#fee08b", "#d9ef8b", "#91cf60", "#1a9850"] + original: List[str] = ["#acacac", "#ff0101", "#cc673b", "#ce8458", "#6bbb15", "#0ed812"] + selected_colors: List[str] = [] + buckets: BucketList = [] + + def __init__(self, selected_colors): + """Initialize class with a selected theme.""" + self.selected_colors = selected_colors + self.buckets = generate_buckets(0, int(MAX_PING_IN_MS), int(MAX_PING_IN_MS / (len(selected_colors) - 1))) + + def colorize(self, input_text): + """Colorized the current input according to the selected theme.""" + for index, current_bucket in enumerate(self.buckets): + if in_bucket(input_text, current_bucket): + return self.selected_colors[len(self.selected_colors) - 1 - index] + return self.selected_colors[0] + + +CURRENT_THEME: Themes = Themes(Themes.red_green) + + +def httping(targets: List[str]) -> Tuple[Dict[str, Optional[float]], Optional[float], Optional[float]]: + """Pings the supplied targets and returns a mean and a std deviation over all target responses.""" + responses: Dict[str, Optional[float]] = {} + for target in targets: + try: + responses[target] = requests.head(target, timeout=MAX_PING.total_seconds()).elapsed.total_seconds() * 1000 + except requests.exceptions.RequestException as ex: + print(str(ex), file=sys.stderr) + responses[target] = None + response_times = [value for key, value in responses.items() if isinstance(value, float)] + + if len(response_times) > 1: + return responses, statistics.median(response_times), statistics.stdev(response_times) + elif response_times: + return responses, response_times[0], 0 + else: + return responses, None, None + + +if __name__ == "__main__": + responses, median, stddev = httping(TARGETS) + if isinstance(median, float) and median != MAX_PING_IN_MS: + print("⦿ | color=" + CURRENT_THEME.colorize(median)) # type: ignore + else: + print("☠ | color=" + CURRENT_THEME.selected_colors[0]) + print("---") + for current_host, current_response in responses.items(): + if isinstance(current_response, float): + print(current_host + ": " + str(round(current_response, 2)) + " | color=" + + CURRENT_THEME.colorize(current_response) + " | bash=\"httping -K " + current_host + "\"") + else: + print(current_host + ": ☠ | color=" + CURRENT_THEME.selected_colors[0]) + print("---") + print("Average: " + str(round(median, 2)) + "±" + str(round(stddev, 2)) + " | color=" + + CURRENT_THEME.colorize(median)) diff --git a/pylama.ini b/pylama.ini new file mode 120000 index 0000000..9ad5e0b --- /dev/null +++ b/pylama.ini @@ -0,0 +1 @@ +.pscaffold/pylama.ini \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8f94722 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +requests>=2.5.0