initial commit, basic model and testviews
authorJohn Janus <mail@johnzone.org>
Wed, 10 Apr 2019 09:09:41 +0000 (11:09 +0200)
committerJohn Janus <mail@johnzone.org>
Wed, 10 Apr 2019 09:09:41 +0000 (11:09 +0200)
26 files changed:
.gitignore [new file with mode: 0644]
core/__init__.py [new file with mode: 0644]
core/admin.py [new file with mode: 0644]
core/apps.py [new file with mode: 0644]
core/migrations/0001_initial.py [new file with mode: 0644]
core/migrations/0002_auto_20190407_2011.py [new file with mode: 0644]
core/migrations/0003_bay_name.py [new file with mode: 0644]
core/migrations/0004_auto_20190409_2242.py [new file with mode: 0644]
core/migrations/0005_auto_20190409_2256.py [new file with mode: 0644]
core/migrations/0006_auto_20190409_2258.py [new file with mode: 0644]
core/migrations/__init__.py [new file with mode: 0644]
core/models.py [new file with mode: 0644]
core/templates/core/bay_detail.html [new file with mode: 0644]
core/templates/core/stablepart_detail.html [new file with mode: 0644]
core/templates/core/stablepart_list.html [new file with mode: 0644]
core/tests.py [new file with mode: 0644]
core/urls.py [new file with mode: 0644]
core/views.py [new file with mode: 0644]
manage.py [new file with mode: 0755]
stable.kdev4 [new file with mode: 0644]
stable/__init__.py [new file with mode: 0644]
stable/settings.py [new file with mode: 0644]
stable/urls.py [new file with mode: 0644]
stable/wsgi.py [new file with mode: 0644]
static/base.css [new file with mode: 0644]
templates/base.html [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..abd590a
--- /dev/null
@@ -0,0 +1,14 @@
+.eric6project/
+.ropeproject/
+.directory/
+.kdev4/
+*.pyc
+*.pyo
+*.orig
+*.bak
+*.rej
+*~
+cur/
+tmp/
+__pycache__/
+*.DS_Store
diff --git a/core/__init__.py b/core/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/core/admin.py b/core/admin.py
new file mode 100644 (file)
index 0000000..153f502
--- /dev/null
@@ -0,0 +1,9 @@
+from django.contrib import admin
+from core.models import StablePart, Bay, Horse, FodderPlan, Misc
+
+# Register your models here.
+admin.site.register(StablePart)
+admin.site.register(Bay)
+admin.site.register(Horse)
+admin.site.register(FodderPlan)
+admin.site.register(Misc)
diff --git a/core/apps.py b/core/apps.py
new file mode 100644 (file)
index 0000000..26f78a8
--- /dev/null
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class CoreConfig(AppConfig):
+    name = 'core'
diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py
new file mode 100644 (file)
index 0000000..6edecb3
--- /dev/null
@@ -0,0 +1,84 @@
+# Generated by Django 2.2 on 2019-04-06 22:06
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Bay',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('bay_number', models.IntegerField()),
+            ],
+        ),
+        migrations.CreateModel(
+            name='FodderPlan',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('fodder', models.TextField()),
+                ('valid_since', models.DateTimeField()),
+                ('valid_until', models.DateTimeField()),
+            ],
+        ),
+        migrations.CreateModel(
+            name='Horse',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=180)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='Misc',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('value', models.TextField()),
+                ('valid_since', models.DateTimeField()),
+                ('valid_until', models.DateTimeField()),
+            ],
+        ),
+        migrations.CreateModel(
+            name='StablePart',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=140)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='IsInBay',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('in_bay_since', models.DateField()),
+                ('in_bay_until', models.DateField()),
+                ('bay', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Bay')),
+                ('horse', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Horse')),
+            ],
+        ),
+        migrations.AddField(
+            model_name='horse',
+            name='bay',
+            field=models.ManyToManyField(blank=True, null=True, through='core.IsInBay', to='core.Bay'),
+        ),
+        migrations.AddField(
+            model_name='horse',
+            name='fodder',
+            field=models.ManyToManyField(blank=True, null=True, to='core.FodderPlan'),
+        ),
+        migrations.AddField(
+            model_name='horse',
+            name='misc_remarks',
+            field=models.ManyToManyField(blank=True, null=True, to='core.Misc'),
+        ),
+        migrations.AddField(
+            model_name='bay',
+            name='located_in',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.StablePart'),
+        ),
+    ]
diff --git a/core/migrations/0002_auto_20190407_2011.py b/core/migrations/0002_auto_20190407_2011.py
new file mode 100644 (file)
index 0000000..3c0de7d
--- /dev/null
@@ -0,0 +1,38 @@
+# Generated by Django 2.2 on 2019-04-07 20:11
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='horse',
+            name='colour',
+            field=models.CharField(blank=True, max_length=120, null=True),
+        ),
+        migrations.AddField(
+            model_name='horse',
+            name='markings',
+            field=models.CharField(blank=True, max_length=120, null=True),
+        ),
+        migrations.AlterField(
+            model_name='horse',
+            name='bay',
+            field=models.ManyToManyField(blank=True, through='core.IsInBay', to='core.Bay'),
+        ),
+        migrations.AlterField(
+            model_name='horse',
+            name='fodder',
+            field=models.ManyToManyField(blank=True, to='core.FodderPlan'),
+        ),
+        migrations.AlterField(
+            model_name='horse',
+            name='misc_remarks',
+            field=models.ManyToManyField(blank=True, to='core.Misc'),
+        ),
+    ]
diff --git a/core/migrations/0003_bay_name.py b/core/migrations/0003_bay_name.py
new file mode 100644 (file)
index 0000000..beec7a0
--- /dev/null
@@ -0,0 +1,18 @@
+# Generated by Django 2.2 on 2019-04-07 20:56
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0002_auto_20190407_2011'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='bay',
+            name='name',
+            field=models.CharField(blank=True, max_length=120, null=True),
+        ),
+    ]
diff --git a/core/migrations/0004_auto_20190409_2242.py b/core/migrations/0004_auto_20190409_2242.py
new file mode 100644 (file)
index 0000000..a49076a
--- /dev/null
@@ -0,0 +1,19 @@
+# Generated by Django 2.2 on 2019-04-09 22:42
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0003_bay_name'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='bay',
+            name='located_in',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bays', to='core.StablePart'),
+        ),
+    ]
diff --git a/core/migrations/0005_auto_20190409_2256.py b/core/migrations/0005_auto_20190409_2256.py
new file mode 100644 (file)
index 0000000..a0061be
--- /dev/null
@@ -0,0 +1,18 @@
+# Generated by Django 2.2 on 2019-04-09 22:56
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0004_auto_20190409_2242'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='horse',
+            name='bay',
+            field=models.ManyToManyField(blank=True, related_name='in_bay', through='core.IsInBay', to='core.Bay'),
+        ),
+    ]
diff --git a/core/migrations/0006_auto_20190409_2258.py b/core/migrations/0006_auto_20190409_2258.py
new file mode 100644 (file)
index 0000000..c8b4184
--- /dev/null
@@ -0,0 +1,18 @@
+# Generated by Django 2.2 on 2019-04-09 22:58
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0005_auto_20190409_2256'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='horse',
+            name='bay',
+            field=models.ManyToManyField(blank=True, related_name='horses', through='core.IsInBay', to='core.Bay'),
+        ),
+    ]
diff --git a/core/migrations/__init__.py b/core/migrations/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/core/models.py b/core/models.py
new file mode 100644 (file)
index 0000000..5e024af
--- /dev/null
@@ -0,0 +1,54 @@
+from django.db import models
+
+# Create your models here.
+
+
+class StablePart(models.Model):
+    name = models.CharField(max_length=140)
+    
+    def __str__(self):
+        return 'Stable Part ' + self.name
+
+
+class Bay(models.Model):
+    name = models.CharField(max_length=120, null=True, blank=True)
+    bay_number = models.IntegerField()
+    located_in = models.ForeignKey(StablePart, on_delete=models.CASCADE, related_name='bays')
+
+    def __str__(self):
+        return (self.name if self.name is not None else '')\
+            + '(number ' + str(self.bay_number) + ')'
+
+
+class FodderPlan(models.Model):
+    fodder = models.TextField()
+    valid_since = models.DateTimeField()
+    valid_until = models.DateTimeField()
+    
+    def __str__(self):
+        return self.valid_since + ' - ' + self.valid_until
+
+
+class Misc(models.Model):
+    value = models.TextField()
+    valid_since = models.DateTimeField()
+    valid_until = models.DateTimeField()
+
+
+class Horse(models.Model):
+    name = models.CharField(max_length=180)
+    colour = models.CharField(max_length=120, null=True, blank=True)
+    markings = models.CharField(max_length=120, null=True, blank=True)
+    bay = models.ManyToManyField(Bay,  through='IsInBay',   blank=True, related_name='horses')
+    fodder = models.ManyToManyField(FodderPlan,  blank=True)
+    misc_remarks = models.ManyToManyField(Misc,   blank=True)
+    
+    def __str__(self):
+        return self.name
+
+
+class IsInBay(models.Model):
+    horse = models.ForeignKey(Horse,  on_delete=models.CASCADE)
+    bay = models.ForeignKey(Bay,  on_delete=models.CASCADE)
+    in_bay_since = models.DateField()
+    in_bay_until = models.DateField()
diff --git a/core/templates/core/bay_detail.html b/core/templates/core/bay_detail.html
new file mode 100644 (file)
index 0000000..9496e41
--- /dev/null
@@ -0,0 +1,18 @@
+{% extends 'base.html' %}
+
+{% block title %}Bay {{ object }} - {{ block.super }}{% endblock %}
+
+{% block main %}
+<div class="card">
+  <h3>{{ object }}</h3>
+  <div>
+    <ul>
+    {% for inbay in object.isinbay_set.all %}
+    <li><a href="/horse/{{ inbay.horse.id }}">{{ inbay.in_bay_since }} bis {{ inbay.in_bay_until }}: {{ inbay.horse }}</a></li>
+    {% empty %}
+    <li>EMPTY</li>
+    {% endfor %}
+    </ul>
+  </div>
+</div>
+{% endblock %}
diff --git a/core/templates/core/stablepart_detail.html b/core/templates/core/stablepart_detail.html
new file mode 100644 (file)
index 0000000..3ef8b7f
--- /dev/null
@@ -0,0 +1,18 @@
+{% extends 'base.html' %}
+
+{% block title %}{{ object.name }} - {{ block.super }}{% endblock %}
+
+{% block main %}
+<div class="card">
+  <h3>{{ object.name }}</h3>
+  <div>
+    <ul>
+    {% for bay in object.bays.all %}
+    <li><a href="/bay/{{ bay.id }}">{{ bay }}</a></li>
+    {% empty %}
+    <li>EMPTY</li>
+    {% endfor %}
+    </ul>
+  </div>
+</div>
+{% endblock %}
diff --git a/core/templates/core/stablepart_list.html b/core/templates/core/stablepart_list.html
new file mode 100644 (file)
index 0000000..af24e12
--- /dev/null
@@ -0,0 +1,14 @@
+{% extends 'base.html' %}
+
+{% block title %}Stable Parts - {{ block.super }}{% endblock %}
+
+{% block main %}
+    <ul>
+      {% for sp in object_list %}
+        <li><a href="/stablepart/{{sp.id}}">{{ sp }}</a></li>
+      {% empty %}
+        <li>No Stable yet</li>
+      {% endfor %}
+    </ul>
+{% endblock %}
+  
diff --git a/core/tests.py b/core/tests.py
new file mode 100644 (file)
index 0000000..7ce503c
--- /dev/null
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/core/urls.py b/core/urls.py
new file mode 100644 (file)
index 0000000..988e327
--- /dev/null
@@ -0,0 +1,10 @@
+from django.urls import path
+
+from . import views
+
+app_name = 'core'
+urlpatterns = [
+    path('stableparts', views.StableParts.as_view(), name='StableParts'),
+    path('stablepart/<int:pk>', views.StablePartDetail.as_view(), name='StablePartDetail'),
+    path('bay/<int:pk>', views.BayDetail.as_view(), name='BayDetail'),
+]
diff --git a/core/views.py b/core/views.py
new file mode 100644 (file)
index 0000000..23b09c0
--- /dev/null
@@ -0,0 +1,17 @@
+from django.shortcuts import render
+from django.views.generic import ListView, DetailView
+from core.models import StablePart, Bay
+
+# Create your views here.
+
+
+class StableParts(ListView):
+    model = StablePart
+
+
+class StablePartDetail(DetailView):
+    model = StablePart
+    
+    
+class BayDetail(DetailView):
+    model = Bay
diff --git a/manage.py b/manage.py
new file mode 100755 (executable)
index 0000000..8da04f5
--- /dev/null
+++ b/manage.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+"""Django's command-line utility for administrative tasks."""
+import os
+import sys
+
+
+def main():
+    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'stable.settings')
+    try:
+        from django.core.management import execute_from_command_line
+    except ImportError as exc:
+        raise ImportError(
+            "Couldn't import Django. Are you sure it's installed and "
+            "available on your PYTHONPATH environment variable? Did you "
+            "forget to activate a virtual environment?"
+        ) from exc
+    execute_from_command_line(sys.argv)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/stable.kdev4 b/stable.kdev4
new file mode 100644 (file)
index 0000000..54c7a31
--- /dev/null
@@ -0,0 +1,4 @@
+[Project]
+CreatedFrom=
+Manager=KDevCustomBuildSystem
+Name=stable
diff --git a/stable/__init__.py b/stable/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/stable/settings.py b/stable/settings.py
new file mode 100644 (file)
index 0000000..5ec8e75
--- /dev/null
@@ -0,0 +1,125 @@
+"""
+Django settings for stable project.
+
+Generated by 'django-admin startproject' using Django 2.2.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/2.2/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/2.2/ref/settings/
+"""
+
+import os
+
+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+
+# Quick-start development settings - unsuitable for production
+# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
+
+# SECURITY WARNING: keep the secret key used in production secret!
+SECRET_KEY = 'on!o2^64g5lf(*avme1dbcz2y*kkv@#$rhk=gmo_%i#)c&-l)j'
+
+# SECURITY WARNING: don't run with debug turned on in production!
+DEBUG = True
+
+ALLOWED_HOSTS = []
+
+
+# Application definition
+
+INSTALLED_APPS = [
+    'core', 
+    'django.contrib.admin',
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.messages',
+    'django.contrib.staticfiles',
+]
+
+MIDDLEWARE = [
+    'django.middleware.security.SecurityMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.middleware.common.CommonMiddleware',
+    'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'stable.urls'
+
+TEMPLATES = [
+    {
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [os.path.join(BASE_DIR, 'templates'), ],
+        'APP_DIRS': True,
+        'OPTIONS': {
+            'context_processors': [
+                'django.template.context_processors.debug',
+                'django.template.context_processors.request',
+                'django.contrib.auth.context_processors.auth',
+                'django.contrib.messages.context_processors.messages',
+            ],
+            'string_if_invalid': 'INVALID_VALUE',
+        },
+    },
+]
+
+WSGI_APPLICATION = 'stable.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.mysql', 
+        'NAME': 'stable', 
+        'USER': 'stable', 
+        'PASSWORD': 'po1Ahf5eph7dogh1', 
+            }
+}
+
+
+# Password validation
+# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+    {
+        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+    },
+    {
+        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+    },
+]
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/2.2/topics/i18n/
+
+LANGUAGE_CODE = 'en-gb'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_L10N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/2.2/howto/static-files/
+
+STATIC_URL = '/static/'
+STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static'), ]
diff --git a/stable/urls.py b/stable/urls.py
new file mode 100644 (file)
index 0000000..1f13dfe
--- /dev/null
@@ -0,0 +1,23 @@
+"""stable URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+    https://docs.djangoproject.com/en/2.2/topics/http/urls/
+Examples:
+Function views
+    1. Add an import:  from my_app import views
+    2. Add a URL to urlpatterns:  path('', views.home, name='home')
+Class-based views
+    1. Add an import:  from other_app.views import Home
+    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
+Including another URLconf
+    1. Import the include() function: from django.urls import include, path
+    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.urls import path, include
+import core.urls
+
+urlpatterns = [
+    path('admin/', admin.site.urls),
+    path('', include(core.urls, namespace='core')),
+]
diff --git a/stable/wsgi.py b/stable/wsgi.py
new file mode 100644 (file)
index 0000000..536b274
--- /dev/null
@@ -0,0 +1,16 @@
+"""
+WSGI config for stable project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'stable.settings')
+
+application = get_wsgi_application()
diff --git a/static/base.css b/static/base.css
new file mode 100644 (file)
index 0000000..1342f84
--- /dev/null
@@ -0,0 +1,62 @@
+* {
+    box-sizing: border-box;
+}
+
+html {
+    height: 100%;
+}
+
+body {
+    display: grid;
+    grid-template-columns: 15em 1fr;
+    grid-template-rows: 1fr 6fr 1fr;
+    grid-template-areas:
+        "header header"
+        "nav main"
+        "footer footer";
+    margin: 0;
+    height: 100%;
+}
+
+div#header {
+    grid-area: header;
+    border: 1px solid red;
+}
+
+div#nav {
+    grid-area: nav;
+    border: 1px solid red;
+}
+
+div#main {
+    grid-area: main;
+    border: 1px solid red;
+}
+
+div#footer {
+    grid-area: footer
+    border: 1px solid red;
+}
+
+div.card {
+    box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
+    transition: 0.3s;
+    border-radius: 5px;
+}
+
+div.card:hover {
+    box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
+}
+
+div.card > img {
+    border-radius: 5px 5px 0 0;
+}
+
+div.card > h1,h2,h3,h4,div {
+    padding: 5px;
+}
+
+div.card > h1,h2,h3,h4 {
+    margin: 0;
+    border-bottom: 1px solid lightgrey;
+}
diff --git a/templates/base.html b/templates/base.html
new file mode 100644 (file)
index 0000000..7e5e1cf
--- /dev/null
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
+    {% load static %}
+    <link rel="stylesheet" type="text/css" href="{% static 'base.css' %}" />
+    <title>{% block title %} Stable Management {% endblock %}</title>
+  </head>
+  <body>
+    <div id="header">{% block header %}<div class="card"><h1>Stable Management</h1><div>content</div></div>{% endblock %}</div>
+    <div id="nav">{% block nav %}nav{% endblock %}</div>
+    <div id="main">{% block main %}{% endblock %}</div>
+    <div id="footer">{%block footer %}footer{% endblock %}</div>
+  </body>
+</html>