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

View File

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

View File

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

View File

@ -35,11 +35,11 @@ import {Component, Prop} from "vue-property-decorator";
@Component @Component
export default class TopBar extends Vue { export default class TopBar extends Vue {
@Prop() value = ""; @Prop() value;
search (event) { search(event) {
if (event.target.value.trim().length > 0) { 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" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I= 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: check-types@^7.3.0:
version "7.4.0" version "7.4.0"
resolved "https://registry.yarnpkg.com/check-types/-/check-types-7.4.0.tgz#0378ec1b9616ec71f774931a3c6516fad8c152f4" 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" map-visit "^1.0.0"
object-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: color-convert@^1.3.0, color-convert@^1.9.0:
version "1.9.3" version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 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: dependencies:
minimist "0.0.8" 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: move-concurrently@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
@ -6818,6 +6851,11 @@ vm-browserify@0.0.4:
dependencies: dependencies:
indexof "0.0.1" 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: vue-class-component@^6.2.0, vue-class-component@^6.3.2:
version "6.3.2" version "6.3.2"
resolved "https://registry.yarnpkg.com/vue-class-component/-/vue-class-component-6.3.2.tgz#e6037e84d1df2af3bde4f455e50ca1b9eec02be6" 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" resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.17.tgz#0f8789ad718be68ca1872629832ed533589c6ada"
integrity sha512-mFbcWoDIJi0w0Za4emyLiW72Jae0yjANHbCVquMKijcavBGypqlF7zHRgMa5k4sesdv7hv2rB4JPdZfR+TPfhQ== 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: watchpack@^1.4.0:
version "1.6.0" version "1.6.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00"