Finishing touches before presentation
This commit is contained in:
parent
1e2040ddd9
commit
8167ecded5
5 changed files with 143 additions and 30 deletions
|
@ -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),
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue