import urllib.request from enum import Enum from socket import timeout from urllib.error import URLError from urllib.request import Request from bs4 import BeautifulSoup from django.db import models from django.utils.functional import cached_property from ordered_model.models import OrderedModel class ServiceStatus(Enum): OK = 'ok' DOWN = 'down' UNKNOWN = 'unknown' class Service(OrderedModel): short_name = models.CharField(null=False, max_length=64) image = models.ImageField(null=True, blank=True, upload_to='services') description = models.TextField(null=True, blank=True) url = models.URLField() def __str__(self): return f"{self.short_name} ({self.url})" @cached_property def index_request(self): try: request = Request(self.url, headers={'User-Agent': 'its just me humble status page'}) return urllib.request.urlopen(request, timeout=1) except (URLError, timeout): return None def get_status(self): if self.index_request and self.index_request.getcode() == 200: return ServiceStatus.OK else: return ServiceStatus.DOWN @cached_property def image_or_favicon(self): if self.image: return self.image.url if self.index_request: parsed_html = BeautifulSoup(self.index_request, features="html.parser") link_tags = parsed_html.find_all('link') for rel in ['apple-touch-icon', 'shortcut', 'icon']: for link_tag in link_tags: if rel in link_tag.attrs['rel']: result = link_tag.attrs['href'] if self.url not in result: return self.url + (result if result.startswith("/") else f"/{result}") else: return result