Finishing touches before presentation

This commit is contained in:
Sijmen 2018-10-26 15:15:49 +02:00
parent 1e2040ddd9
commit 8167ecded5
5 changed files with 143 additions and 30 deletions

View File

@ -4,6 +4,7 @@ from elasticsearch_dsl.connections import connections
from elasticsearch.exceptions import NotFoundError
from tqdm import tqdm
from beeprint import pp
from datetime import datetime
import csv
import click
import requests
@ -23,6 +24,7 @@ def index():
query = request.args.get("q")
categories = request.args.get("categories", None)
years = request.args.get("years", None)
page = int(request.args.get("page", 1)) - 1
search_dict = {
@ -31,6 +33,7 @@ def index():
"bool": {
"must": [
{"query_string": {"query": query}},
{"term": {"dead": False}},
]
}
},
@ -38,28 +41,55 @@ def index():
"category": {
"terms": {"field": "category"},
},
"date": {
"date_histogram": {
"field": "date", "interval": "year",
},
},
"chips": {
"significant_terms": {
"field": "body",
"mutual_information": {
"include_negatives": True,
},
"mutual_information": {"include_negatives": True},
"size": 40,
},
}
},
}
if categories is not None or years is not None:
search_dict["post_filter"] = {"bool": {"must": []}}
if categories is not None:
category_list = categories.split(",")
search_dict["post_filter"] = {
search_dict["post_filter"]["bool"]["must"].append({
"terms": {"category": category_list},
})
if years is not None:
year_list = years.split(",")
search_dict["post_filter"]["bool"]["must"].append({
"bool": {
"should": [
{
"range": {
"date": {
"gte": f"{year}||/y",
"lte": f"{year}||/y",
"format": "yyyy"
}
}
} for year in year_list if year
]
}
})
search = Search.from_dict(search_dict)
response = search.execute()
pp(response.to_dict())
date_facets = [{"key": datetime.fromtimestamp(bucket.key / 1000).year,
"count": bucket.doc_count}
for bucket in response.aggregations.date.buckets]
category_facets = [
{"category": bucket.key, "count": round_sigfig(bucket.doc_count, 3)}
for bucket in response.aggregations.category.buckets
@ -68,8 +98,6 @@ def index():
chips = [{"key": bucket.key, "count": bucket.doc_count}
for bucket in response.aggregations.chips.buckets]
date_facets = []
results = []
for hit in response:
summary = Question.summary(hit)
@ -87,10 +115,7 @@ def index():
})
return jsonify(
facets={
"months": date_facets,
"categories": category_facets,
},
facets={"dates": date_facets, "categories": category_facets},
chips=chips,
results=results,
hits=round_sigfig(response.hits.total, 4),

View File

@ -12,10 +12,13 @@
},
"dependencies": {
"babel-plugin-transform-decorators-legacy": "^1.3.5",
"chart.js": "^2.7.3",
"vue": "^2.5.2",
"vue-chartjs": "^3.4.0",
"vue-class-component": "^6.3.2",
"vue-property-decorator": "^7.2.0",
"vue-router": "^3.0.1"
"vue-router": "^3.0.1",
"vuetrend": "^0.3.2"
},
"devDependencies": {
"autoprefixer": "^7.1.2",

View File

@ -1,8 +1,8 @@
<template>
<div class="body">
<div class="chips">
<div v-for="chip in chips" class="chip" @click="appendToQuery(chip.key)">
{{chip.key}}
<div v-for="chip in chips" v-bind:key="chip.key" class="chip" @click="appendToQuery(chip.key)">
{{chip.key}} ({{chip.count}})
</div>
</div>
@ -12,6 +12,7 @@
<div class="body__inner">
<div class="body__facets">
<div class="body__categories">
<div v-for="facet in facets.categories" v-bind:key="facet.category"
v-bind:class="{active: activeCategories.includes(facet.category)}"
class="category facet" @click="toggleCategory(facet)">
@ -22,6 +23,18 @@
</div>
</div>
<div class="body__dates">
<div v-for="{count, key} in facets.dates" v-bind:key="key"
v-bind:class="{active: activeYears.includes(key)}"
class="facet date" @click="toggleYear(key)">
<div class="date__facet">{{key}}</div>
<div v-if="count < 10000" class="date__count">{{count}}</div>
<div v-else class="date__count">{{(count / 1000).toFixed()}}K</div>
</div>
</div>
</div>
<div class="body__results">
<div v-for="result in results" v-bind:key="result.id" class="result">
<a :href="result.url" target="_blank">
@ -46,7 +59,8 @@
<div v-for="page in pages" class="pagination__page"
v-if="page > currentPage - 5 && page < currentPage + 5"
v-bind:class="{active: page === currentPage}" @click="switchPage(page)">
v-bind:class="{active: page === currentPage}" @click="switchPage(page)"
v-bind:key="page">
<img v-if="page === currentPage" src="@/assets/pages_active.png" />
<img v-else src="@/assets/pages_inactive.png" />
@ -71,12 +85,15 @@ export default class ResultBody extends Vue {
@Prop() value;
json = null;
activeCategories = [];
hits = 0;
results = [];
responseTime = 0;
facets = {};
facets = {dates: [], categories: []};
chips = [];
dates = {}
activeCategories = [];
activeYears = [];
currentPage = 1;
@ -107,6 +124,17 @@ export default class ResultBody extends Vue {
await this.search();
}
async toggleYear(year) {
const index = this.activeYears.indexOf(year);
if (index === -1) {
this.activeYears.push(year);
} else {
this.activeYears.splice(index, 1);
}
await this.search();
}
async search() {
const query = encodeURIComponent(this.value);
let queryString = `?q=${query}&page=${this.currentPage}`;
@ -115,6 +143,10 @@ export default class ResultBody extends Vue {
const categories = encodeURIComponent(this.activeCategories.join(","));
queryString += `&categories=${categories}`;
}
if (this.activeYears.length > 0) {
const years = encodeURIComponent(this.activeYears.join(","));
queryString += `&years=${years}`;
}
this.$router.history.push(`/search${queryString}`);
const url = `/api/${queryString}`;
@ -127,10 +159,12 @@ export default class ResultBody extends Vue {
this.hits = this.json.hits;
this.responseTime = this.json.took;
this.results = this.json.results;
console.log({...this.facets});
}
appendToQuery(value) {
this.$emit("input", `${this.value} AND ${value}`)
this.$emit("input", `${this.value} AND ${value}`);
}
async switchPage(page) {
@ -206,12 +240,14 @@ export default class ResultBody extends Vue {
font-weight: bold;
}
.category {
.category,
.date {
display: flex;
justify-content: space-between;
}
.category__facet {
.category__facet,
.date__facet {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@ -219,7 +255,8 @@ export default class ResultBody extends Vue {
padding-right: 8px;
}
.category__count {
.category__count,
.date__count {
text-align: right;
}
@ -267,4 +304,9 @@ export default class ResultBody extends Vue {
color: blue;
cursor: pointer;
}
.body__categories,
.body__dates {
margin-bottom: 16px;
}
</style>

View File

@ -35,11 +35,11 @@ import {Component, Prop} from "vue-property-decorator";
@Component
export default class TopBar extends Vue {
@Prop() value = "";
@Prop() value;
search (event) {
search(event) {
if (event.target.value.trim().length > 0) {
this.$emit("input", event.target.value)
this.$emit("input", event.target.value);
}
}
}

View File

@ -1440,6 +1440,29 @@ chardet@^0.4.0:
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=
chart.js@^2.7.3:
version "2.7.3"
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.7.3.tgz#cdb61618830bf216dc887e2f7b1b3c228b73c57e"
integrity sha512-3+7k/DbR92m6BsMUYP6M0dMsMVZpMnwkUyNSAbqolHKsbIzH2Q4LWVEHHYq7v0fmEV8whXE0DrjANulw9j2K5g==
dependencies:
chartjs-color "^2.1.0"
moment "^2.10.2"
chartjs-color-string@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/chartjs-color-string/-/chartjs-color-string-0.5.0.tgz#8d3752d8581d86687c35bfe2cb80ac5213ceb8c1"
integrity sha512-amWNvCOXlOUYxZVDSa0YOab5K/lmEhbFNKI55PWc4mlv28BDzA7zaoQTGxSBgJMHIW+hGX8YUrvw/FH4LyhwSQ==
dependencies:
color-name "^1.0.0"
chartjs-color@^2.1.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/chartjs-color/-/chartjs-color-2.2.0.tgz#84a2fb755787ed85c39dd6dd8c7b1d88429baeae"
integrity sha1-hKL7dVeH7YXDndbdjHsdiEKbrq4=
dependencies:
chartjs-color-string "^0.5.0"
color-convert "^0.5.3"
check-types@^7.3.0:
version "7.4.0"
resolved "https://registry.yarnpkg.com/check-types/-/check-types-7.4.0.tgz#0378ec1b9616ec71f774931a3c6516fad8c152f4"
@ -1577,6 +1600,11 @@ collection-visit@^1.0.0:
map-visit "^1.0.0"
object-visit "^1.0.0"
color-convert@^0.5.3:
version "0.5.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd"
integrity sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=
color-convert@^1.3.0, color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
@ -4405,6 +4433,11 @@ mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@
dependencies:
minimist "0.0.8"
moment@^2.10.2:
version "2.22.2"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66"
integrity sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=
move-concurrently@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
@ -6818,6 +6851,11 @@ vm-browserify@0.0.4:
dependencies:
indexof "0.0.1"
vue-chartjs@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/vue-chartjs/-/vue-chartjs-3.4.0.tgz#669e4453be0676605fc9290b3b581867ccd15c88"
integrity sha512-uikAXl66g49rawH7Uto3gKh/7vxflcd5xyYbnQVGKSYEh9VI9JGMZ1KNPAEr+8ViRd2FX1hPDVevKBONK6v1fw==
vue-class-component@^6.2.0, vue-class-component@^6.3.2:
version "6.3.2"
resolved "https://registry.yarnpkg.com/vue-class-component/-/vue-class-component-6.3.2.tgz#e6037e84d1df2af3bde4f455e50ca1b9eec02be6"
@ -6897,6 +6935,11 @@ vue@^2.5.2:
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.17.tgz#0f8789ad718be68ca1872629832ed533589c6ada"
integrity sha512-mFbcWoDIJi0w0Za4emyLiW72Jae0yjANHbCVquMKijcavBGypqlF7zHRgMa5k4sesdv7hv2rB4JPdZfR+TPfhQ==
vuetrend@^0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/vuetrend/-/vuetrend-0.3.2.tgz#13100862ded562c78d3704c3865cd38c582e586a"
integrity sha512-nXza0yeoynEdrdV8RKStHA0KykPyVXNHjcQOqxc+5rW5051V3RMrsI9ob1+kiJM8azoww6t6XiC/+AS7aiqEew==
watchpack@^1.4.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00"