Begin implementing Bulma

This commit is contained in:
Mark Veidemanis 2022-07-21 13:49:01 +01:00
parent ae8da03c3c
commit 5b77858c78
Signed by: m
GPG Key ID: 5ACFCEED46C0904F
10 changed files with 358 additions and 320 deletions

View File

@ -21,21 +21,23 @@ def initialise_opensearch():
return client
def construct_query(query):
def construct_query(query, fields):
if not fields:
fields = settings.OPENSEARCH_MAIN_SEARCH_FIELDS
query = {
"size": 5,
"query": {
"multi_match": {
"query": query,
"fields": settings.OPENSEARCH_MAIN_SEARCH_FIELDS,
"fields": fields,
}
},
}
return query
def run_main_query(client, query):
search_query = construct_query(query)
def run_main_query(client, query, fields=None):
search_query = construct_query(query, fields)
# fmt: off
response = client.search(body=search_query,
index=settings.OPENSEARCH_INDEX_MAIN)

View File

@ -1,98 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="800"
height="800"
viewBox="0 0 211.66668 211.66666"
version="1.1"
id="svg1340"
inkscape:version="1.0.1 (unknown)"
sodipodi:docname="flat_red.svg">
<defs
id="defs1334" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.7"
inkscape:cx="464.32422"
inkscape:cy="460.67991"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
units="px"
inkscape:window-width="2041"
inkscape:window-height="1153"
inkscape:window-x="2912"
inkscape:window-y="293"
inkscape:window-maximized="0" />
<metadata
id="metadata1337">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(38.553562,-16.229317)">
<g
id="g836"
transform="translate(0,0.00154829)">
<path
style="fill:none;fill-opacity:0.993921;stroke:#000000;stroke-width:15.3714;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.995052;paint-order:markers fill stroke"
id="path934-5"
sodipodi:type="arc"
sodipodi:cx="67.27977"
sodipodi:cy="183.1049"
sodipodi:rx="114.39342"
sodipodi:ry="122.54984"
sodipodi:start="3.6651914"
sodipodi:end="5.7595865"
sodipodi:arc-type="arc"
sodipodi:open="true"
d="M -31.787837,121.82999 A 114.39342,122.54984 0 0 1 67.27977,60.555061 114.39342,122.54984 0 0 1 166.34737,121.82998" />
<path
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:16.2002;stroke-linecap:butt;stroke-miterlimit:3.4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path944-5"
sodipodi:type="arc"
sodipodi:cx="67.281319"
sodipodi:cy="140.7709"
sodipodi:rx="42.092651"
sodipodi:ry="42.391029"
sodipodi:start="4.712389"
sodipodi:end="3.1415927"
sodipodi:arc-type="arc"
sodipodi:open="true"
d="m 67.281319,98.379875 a 42.092651,42.391029 0 0 1 38.888541,26.168685 42.092651,42.391029 0 0 1 -9.124543,46.19733 42.092651,42.391029 0 0 1 -45.87216,9.18922 42.092651,42.391029 0 0 1 -25.98449,-39.16421" />
<ellipse
style="fill:#740101;fill-opacity:1;stroke:none;stroke-width:2.7459;stroke-linecap:butt"
id="path10-8"
cx="67.27977"
cy="140.76773"
rx="13.063272"
ry="13.155873" />
<svg width="800" height="800" version="1.1" viewBox="0 0 211.7 211.7" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(38.55 -16.23)">
<g transform="translate(0 .001548)">
<path d="m-31.79 121.8a114.4 122.5 0 0 1 99.07-61.27 114.4 122.5 0 0 1 99.07 61.27" fill="none" stroke="#000" stroke-linejoin="round" stroke-opacity=".9951" stroke-width="15.37" style="paint-order:markers fill stroke"/>
<path d="m67.28 98.38a42.09 42.39 0 0 1 38.89 26.17 42.09 42.39 0 0 1-9.125 46.2 42.09 42.39 0 0 1-45.87 9.189 42.09 42.39 0 0 1-25.98-39.16" fill="none" stroke="#000" stroke-miterlimit="3.4" stroke-width="16.2"/>
<ellipse cx="67.28" cy="140.8" rx="13.06" ry="13.16" fill="#740101"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 719 B

View File

@ -5,56 +5,108 @@
<html lang="en-GB">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pathogen - {{ request.path_info }}</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
<link rel="stylesheet" href="https://site-assets.fontawesome.com/releases/v6.1.1/css/all.css" />
<script>
document.addEventListener('DOMContentLoaded', () => {
<link rel="stylesheet" href="{% static 'style.css' %}" />
// Get all "navbar-burger" elements
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
// Add a click event on each of them
$navbarBurgers.forEach( el => {
el.addEventListener('click', () => {
// Get the target from the "data-target" attribute
const target = el.dataset.target;
const $target = document.getElementById(target);
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
el.classList.toggle('is-active');
$target.classList.toggle('is-active');
});
});
});
</script>
</head>
<body>
<section class="hero is-primary is-fullheight">
<div class="hero-head">
<nav class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="{% url 'home' %}">Pathogen</a>
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#base-nav" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item" href="{% url 'home' %}">
<img src="{% static 'logo.svg' %}" width="112" height="28" alt="logo">
</a>
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="bar">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div class="collapse navbar-collapse" id="base-nav">
<ul class="nav navbar-nav navbar-right">
<div id="bar" class="navbar-menu">
<div class="navbar-start">
<a class="navbar-item" href="{% url 'home' %}">
Home
</a>
{% if user.is_authenticated %}
<li><a href="{% url 'billing' %}">Billing</a></li>
<a class="navbar-item" href="{% url 'billing' %}">
Billing
</a>
{% endif %}
{% if user.is_authenticated %}
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">
Plans
</a>
<div class="navbar-dropdown">
{% if user|has_plan:'drilldown' %}
<li><a href="{% url 'drilldown' %}">Drilldown</a></li>
<a class="navbar-item" href="{% url 'drilldown' %}">
Drilldown
</a>
{% endif %}
<hr class="navbar-divider">
<a class="navbar-item" href="mailto:help@pathogen.is">
Report an issue
</a>
</div>
</div>
{% endif %}
</div>
<div class="navbar-end">
<div class="navbar-item">
<div class="buttons">
{% if not user.is_authenticated %}
<li><a href="{% url 'login' %}">Login</a></li>
<li><a href="{% url 'signup' %}">Sign Up</a></li>
<a class="button is-primary" href="{% url 'signup' %}">
<strong>Sign up</strong>
</a>
<a class="button is-light" href="{% url 'login' %}">
Log in
</a>
{% endif %}
{% if user.is_authenticated %}
<li><a href="{% url 'logout' %}">Logout</a></li>
<a class="button is-dark" href="{% url 'logout' %}">Logout</a>
{% endif %}
</ul>
</div>
</div>
</div>
</div>
</nav>
</div>
<div class="hero-body">
<div class="container has-text-centered">
<section class="section">
<div class="container">
{% block content %}
{% endblock %}
</div>
</div>
</section>
</body>
</html>

View File

@ -1,57 +1,36 @@
{% extends "base.html" %}
{% block content %}
<h1 class="title">Billing/Plan Management</h1>
<div class="container">
<div class="row">
<div class="col-xs-10 col-md-4 col-lg-4">
<div class="panel panel-default">
<div class="panel-heading">
Products
</div>
<div class="panel-body">
<ul class="list-group">
<li class="list-group-item">
<span class="badge">{{ user.first_name }}</span>
First name
</li>
<li class="list-group-item">
<span class="badge">{{ user.last_name }}</span>
Last name
</li>
<li class="list-group-item">
<article class="panel is-primary">
<p class="panel-heading">
User information
</p>
<a class="panel-block is-active">
<span class="panel-icon">
<i class="fas fa-id-card" aria-hidden="true"></i>
</span>
<span class="tag is-primary">{{ user.first_name }} {{ user.last_name }}</span>
</a>
<a class="panel-block">
<span class="panel-icon">
<i class="fas fa-binary" aria-hidden="true"></i>
</span>
{% for plan in user.plans.all %}
<span class="badge">{{ plan.name }}</span>
<span class="tag is-primary">{{ plan.name }}</span>
{% endfor %}
Plans
</li>
<li class="list-group-item">
<span class="badge">{{ user.paid }}</span>
Paid
</li>
<li class="list-group-item">
<span class="badge">{{ user.last_payment }}</span>
Last payment
</li>
</ul>
<form action="{% url 'portal' %}">
<input class="btn btn-lg btn-dark btn-block" type="submit" value="Subscription management">
</form>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
Available plans
</div>
<div class="panel-body">
</a>
<a class="panel-block">
<span class="panel-icon">
<i class="fas fa-credit-card" aria-hidden="true"></i>
</span>
<span class="tag">{{ user.last_payment }}</span>
</a>
<a class="panel-block" href="{% url 'portal' %}">
<span class="panel-icon">
<i class="fa-brands fa-stripe-s" aria-hidden="true"></i>
</span>
Subscription management
</a>
</article>
{% include "partials/product-list.html" %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,50 +1,52 @@
{% load static %}
{% for plan in plans %}
{% if plan not in user_plans %}
<a href="/order/{{ plan.product_id}}">
{% if plan in user_plans %}
<div class="box has-background-info-light">
{% else %}
<div class="box">
{% endif %}
<div class="product">
<img src="{% static plan.image %}" alt="Data image"/>
<div class="description">
<h3>{{ plan.description }}</h3>
<h5>£{{ plan.cost }}</h5>
<article class="media">
<div class="media-left">
<figure class="image is-64x64">
<img src="{% static plan.image %}" alt="Image">
</figure>
</div>
<div class="media-content">
<div class="content">
<p>
<strong>{{ plan.name }}</strong> <small>£{{ plan.cost }}</small>
{% if plan in user_plans %}
<h5> (purchased) </h5>
<i class="fas fa-check" aria-hidden="true"></i>
{% endif %}
<br>
{{ plan.description }}
</p>
</div>
</div>
<nav class="level is-mobile">
<div class="level-left">
{% if plan not in user_plans %}
<a class="level-item" href="/order/{{ plan.name }}">
<span class="icon is-small has-text-success">
<i class="fas fa-plus" aria-hidden="true"></i>
</span>
</a>
{% endif %}
{% if plan in user_plans %}
<a class="level-item" href="/cancel/{{ plan.name }}">
<span class="icon is-small has-text-info">
<i class="fas fa-cancel" aria-hidden="true"></i>
</span>
</a>
{% endif %}
</div>
</nav>
</div>
</article>
</div>
{% endfor %}
<script type="text/javascript">
// Create an instance of the Stripe object with your publishable API key
var stripe = Stripe("pk_test_51HbqYzAKLUD9ELc0KSyiQ9YohsfiUCeBpAfpflAIg2Uu2RFecx3sfWYXzM1xDtI5XlQihqHMnaPKd45JzDuqXdGP00pYWvRvRe");
var setupButton = document.getElementById('setup-button');
setupButton.addEventListener("click", function () {
fetch("/setup-bacs", {
method: "POST",
})
.then(function (response) {
return response.json();
})
.then(function (session) {
return stripe.redirectToCheckout({ sessionId: session.id });
})
.then(function (result) {
// If redirectToCheckout fails due to a browser or network
// error, you should display the localized error message to your
// customer using error.message.
if (result.error) {
alert(result.error.message);
}
})
.catch(function (error) {
console.error("Error:", error);
});
});
</script>

View File

@ -3,29 +3,23 @@
{% load crispy_forms_tags %}
{% block content %}
<section class="hero is-fullheight">
<div class="hero-body">
<div class="container">
<div class="row vertical-offset-100">
<div class="col-md-4 col-md-offset-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Please sign in</h3>
</div>
<div class="panel-body">
<img class="logo" src="{% static 'logo.svg' %}">
<form method="POST">
<fieldset>
<div class="columns is-centered">
<div class="column is-5-tablet is-4-desktop is-3-widescreen">
<form method="POST" class="box">
{% csrf_token %}
{{ form|crispy }}
<input class="btn btn-lg btn-dark btn-block" type="submit" value="Login">
<div class="register">
<p>Don't have an account? <a href="{% url 'signup' %}">Register here</a></p>
<div class="field">
<button class="button is-success">
Login
</button>
</div>
</fieldset>
</form>
</div>
</div>
</div>
</div>
</div>
</section>
{% endblock %}

View File

@ -3,25 +3,23 @@
{% load crispy_forms_tags %}
{% block content %}
<section class="hero is-fullheight">
<div class="hero-body">
<div class="container">
<div class="row vertical-offset-100">
<div class="col-md-4 col-md-offset-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Sign up</h3>
</div>
<div class="panel-body">
<img class="logo" src="{% static 'logo.svg' %}">
<form method="POST">
<fieldset>
<div class="columns is-centered">
<div class="column is-5-tablet is-4-desktop is-3-widescreen">
<form method="POST" class="box">
{% csrf_token %}
{{ form|crispy }}
<input class="btn btn-lg btn-dark btn-block" type="submit" value="Sign up">
</fieldset>
<div class="field">
<button class="button is-success">
Sign up
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</section>
{% endblock %}

View File

@ -1,31 +1,116 @@
{% extends "base.html" %}
{% load static %}
{% block content %}
<h1 class="title">Welcome to search, {{ user.first_name }}!</h1>
<div class="container">
<div class="row">
<div class="col-lg-4 col-md-4 col-xs-12">
<div class="block">
<form method="POST">
<div class="field-body">
<div class="field">
<p class="control is-expanded has-icons-left">
{% csrf_token %}
<div class="input-group search-bar">
<input name="query" type="text" class="form-control" placeholder="Search" id="txtSearch"/>
<div class="input-group-btn">
<button class="btn btn-primary" type="submit">
<span class="glyphicon glyphicon-search"></span>
<input name="query" class="input" type="text" placeholder="Query">
<span class="icon is-small is-left">
<i class="fas fa-user"></i>
</span>
</p>
</div>
<div class="field is-horizontal">
<div class="field-body">
<div class="field is-narrow">
<div class="control">
<div class="select is-fullwidth">
<select name="fields">
{% for field in fields %}
<option>{{ field }}</option>
{% endfor %}
</select>
</div>
</div>
</div>
</div>
</div>
<div class="field-body">
<div class="field is-narrow">
<div class="control">
<div class="select is-fullwidth">
<select>
<option>day</option>
<option>week</option>
<option>month</option>
<option>year</option>
</select>
</div>
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-body">
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Search all fields</label>
</div>
<div class="field-body">
<div class="field is-narrow">
<div class="control">
<label class="radio">
<input type="radio" name="member">
Yes
</label>
<label class="radio">
<input type="radio" name="member">
No
</label>
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Results</label>
</div>
<div class="field-body">
<div class="field is-narrow">
<div class="control">
<div class="select is-fullwidth">
<select name="results">
<option>5</option>
<option>10</option>
<option>25</option>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="field-body">
<div class="field is-horizontal">
<div class="field-label">
<!-- Left empty for spacing -->
</div>
<div class="field-body">
<div class="field">
<div class="control">
<button class="button is-primary">
Send message
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
<div class="block">
<table class="table">
{% if results is not None %}
<div class="row">
<div class="col-lg-12 col-md-12 col-xs-12">
<div class="panel panel-default">
<div class="panel-heading">Results for {{ query }}</div>
<div class="table-responsive">
<table class="table table-bordered table-light">
<thead>
<tr>
<th>TS</th>
@ -36,6 +121,9 @@
<th>net</th>
</tr>
</thead>
{% endif %}
<tbody>
{% for item in results %}
<tr>
<td>{{ item.ts }}</td>
@ -46,14 +134,9 @@
<td>{{ item.net }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
{% endif %}
</div>
<p>{{ card }} hits</p>
{% endblock %}

View File

@ -1,3 +1,6 @@
import pprint
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import render
from django.views import View
@ -5,6 +8,7 @@ from django.views import View
from core.lib.opensearch import initialise_opensearch, run_main_query
client = initialise_opensearch()
pp = pprint.PrettyPrinter(indent=4)
class Drilldown(LoginRequiredMixin, View):
@ -19,14 +23,26 @@ class Drilldown(LoginRequiredMixin, View):
def post(self, request):
if not request.user.has_plan(self.plan_name):
return render(request, "denied.html")
fields = None
if "fields" in request.POST:
fields = request.POST["fields"]
print("FIELD", fields)
if "query" in request.POST:
query = request.POST["query"]
results = run_main_query(client, query)
# field = results.POST["field"]
# print("FIELD ", field)
results = run_main_query(client, query, fields)
pp.pprint(results)
results_parsed = []
if "hits" in results.keys():
if "hits" in results["hits"]:
for item in results["hits"]["hits"]:
results_parsed.append(item["_source"])
context = {"query": query, "results": results_parsed}
context = {
"query": query,
"results": results_parsed,
"card": results["hits"]["total"]["value"],
"fields": settings.OPENSEARCH_MAIN_SEARCH_FIELDS,
}
return render(request, self.template_name, context)
return render(request, self.template_name)

View File

@ -39,13 +39,14 @@ class Billing(LoginRequiredMixin, View):
class Order(LoginRequiredMixin, View):
def get(self, request, product_id):
def get(self, request, plan_name):
plan = Plan.objects.get(name=plan_name)
try:
session = stripe.checkout.Session.create(
payment_method_types=settings.ALLOWED_PAYMENT_METHODS,
mode="subscription",
customer=request.user.stripe_id,
line_items=assemble_plan_map(product_id_filter=product_id),
line_items=assemble_plan_map(product_id_filter=plan.product_id),
success_url=request.build_absolute_uri(reverse("success")),
cancel_url=request.build_absolute_uri(reverse("cancel")),
)