mirror of
https://github.com/Xevion/the-office.git
synced 2025-12-14 18:13:25 -06:00
new SearchResult component, using proper table structure, click functionality for expanding to nearby quotes in scene
This commit is contained in:
104
client/src/components/SearchResult.vue
Normal file
104
client/src/components/SearchResult.vue
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
<template>
|
||||||
|
<b-card class="mb-1" body-class="p-0 expandable-result" footer-class="my-1" v-on:click="toggleExpansion()">
|
||||||
|
<b-card-text class="mu-2 py-1 mb-1">
|
||||||
|
<table v-if="expanded" class="quote-list px-3 py-1 w-100">
|
||||||
|
<tr v-for="(quote, index) in above" :key="`quote-a-${index}`">
|
||||||
|
<td class="quote-speaker my-3 pl-3">{{ quote.speaker }}</td>
|
||||||
|
<td class="quote-text w-100 pr-3">{{ quote.text }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="quote-speaker my-3 pl-3" v-html="item._highlightResult.speaker.value"></td>
|
||||||
|
<td class="quote-text w-100 pr-3" v-html="item._highlightResult.text.value"></td>
|
||||||
|
</tr>
|
||||||
|
<tr v-for="(quote, index) in below" :key="`quote-b-${index}`">
|
||||||
|
<td class="quote-speaker my-3 pl-3">{{ quote.speaker }}</td>
|
||||||
|
<td class="quote-text w-100 pr-3">{{ quote.text }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table v-else class="quote-list px-3 py-1 w-100">
|
||||||
|
<tr v-for="(quote, index) in above" :key="`quote-a-${index}`">
|
||||||
|
<tr>
|
||||||
|
<td class="quote-speaker my-3 pl-3" v-html="item._highlightResult.speaker.value"></td>
|
||||||
|
<td class="quote-text w-100 pr-3" v-html="item._highlightResult.text.value"></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<router-link v-if="expanded" class="no-link search-result-link w-100 text-muted mb-2 ml-2"
|
||||||
|
:to="`/${item.season}/${item.episode_rel}#${item.section_rel}`">
|
||||||
|
Season {{ item.season }} Episode {{ item.episode_rel }} Scene {{ item.section_rel }}
|
||||||
|
</router-link>
|
||||||
|
</b-card-text>
|
||||||
|
</b-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.expandable-result {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-result-link {
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 0.75em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote-list > tr {
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&:hover { background-color: #242424; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote-text {
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote-speaker {
|
||||||
|
min-width: 75px;
|
||||||
|
padding-right: 1em;
|
||||||
|
font-weight: 600;
|
||||||
|
vertical-align: text-top;
|
||||||
|
text-align: right;
|
||||||
|
font-family: 'Montserrat', sans-serif;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: ['item'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
expanded: false,
|
||||||
|
hasExpanded: false,
|
||||||
|
above: null,
|
||||||
|
below: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleExpansion() {
|
||||||
|
this.expanded = !this.expanded;
|
||||||
|
// if first time expanding, fetch quotes
|
||||||
|
if (!this.hasExpanded && this.expanded) {
|
||||||
|
this.hasExpanded = true;
|
||||||
|
this.fetchQuotes();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fetchQuotes() {
|
||||||
|
const path = `http://${process.env.VUE_APP_HOST}:${process.env.VUE_APP_FLASK_PORT}/api/quote_surround?season=\
|
||||||
|
${this.item.season}&episode=${this.item.episode_rel}&scene=${this.item.section_rel}"e=${this.item.quote_rel}`;
|
||||||
|
axios.get(path)
|
||||||
|
.then((res) => {
|
||||||
|
this.above = res.data.above;
|
||||||
|
this.below = res.data.below;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -2,19 +2,21 @@
|
|||||||
<div>
|
<div>
|
||||||
<ais-hits>
|
<ais-hits>
|
||||||
<div slot-scope="{ items }">
|
<div slot-scope="{ items }">
|
||||||
<b-card v-for="item in items" :key="item.objectID" class="mb-1" body-class="p-0 py-2">
|
<SearchResult v-for="item in items" :item="item" :key="item.objectID"></SearchResult>
|
||||||
<b-card-text class="p-1 pl-2">
|
<!-- <b-card v-for="item in items" :key="item.objectID" class="mb-1" body-class="p-0 py-2">-->
|
||||||
<strong v-html="item._highlightResult.speaker.value"></strong>:
|
<!-- <b-card-text class="p-1 pl-2">-->
|
||||||
<span v-html="item._highlightResult.text.value" class="pl-1"></span>
|
<!-- <strong v-html="item._highlightResult.speaker.value"></strong>:-->
|
||||||
|
<!-- <span v-html="item._highlightResult.text.value" class="pl-1"></span>-->
|
||||||
<!-- <strong>{{ item.speaker }}</strong>:-->
|
<!-- <strong>{{ item.speaker }}</strong>:-->
|
||||||
<!-- <ais-highlight attribute="text" :hit="item"/>-->
|
<!-- <ais-highlight attribute="text" :hit="item"/>-->
|
||||||
</b-card-text>
|
<!-- <QuoteList :quotes="scene.quotes"></QuoteList>-->
|
||||||
<template v-slot:footer>
|
<!-- </b-card-text>-->
|
||||||
<router-link class="no-link" :to="`/${item.season}/${item.episode}#${item.section}`">
|
<!-- <template v-slot:footer>-->
|
||||||
Season {{ item.season }} Episode {{ item.episode }} Scene {{ item.section }}
|
<!-- <router-link class="no-link pl-1" :to="`/${item.season}/${item.episode}#${item.section}`">-->
|
||||||
</router-link>
|
<!-- Season {{ item.season }} Episode {{ item.episode }} Scene {{ item.section }}-->
|
||||||
</template>
|
<!-- </router-link>-->
|
||||||
</b-card>
|
<!-- </template>-->
|
||||||
|
<!-- </b-card>-->
|
||||||
</div>
|
</div>
|
||||||
</ais-hits>
|
</ais-hits>
|
||||||
</div>
|
</div>
|
||||||
@@ -28,7 +30,7 @@
|
|||||||
|
|
||||||
mark, .mark {
|
mark, .mark {
|
||||||
padding: 0.02em;
|
padding: 0.02em;
|
||||||
background-color: #fff500;
|
background-color: #d2ca00;
|
||||||
/*color: #black;*/
|
/*color: #black;*/
|
||||||
/*-webkit-filter: invert(100%);*/
|
/*-webkit-filter: invert(100%);*/
|
||||||
/*filter: invert(100%);*/
|
/*filter: invert(100%);*/
|
||||||
@@ -44,7 +46,12 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
|
import SearchResult from './SearchResult.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SearchResults',
|
name: 'SearchResults',
|
||||||
|
components: {
|
||||||
|
SearchResult,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import os
|
|||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
import flask_wtf
|
import flask_wtf
|
||||||
from flask import current_app, jsonify
|
from flask import current_app, jsonify, request
|
||||||
|
|
||||||
from server.helpers import default
|
from server.helpers import default, get_neighbors
|
||||||
|
|
||||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
with open(os.path.join(BASE_DIR, 'data', 'data.json'), 'r', encoding='utf-8') as file:
|
with open(os.path.join(BASE_DIR, 'data', 'data.json'), 'r', encoding='utf-8') as file:
|
||||||
@@ -25,7 +25,6 @@ stats = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
stats['totals']['season'] += len(default(data, []))
|
stats['totals']['season'] += len(default(data, []))
|
||||||
for season in data:
|
for season in data:
|
||||||
stats['totals']['episode'] += len(default(season.get('episodes'), []))
|
stats['totals']['episode'] += len(default(season.get('episodes'), []))
|
||||||
@@ -77,3 +76,13 @@ def api_data():
|
|||||||
Season data route
|
Season data route
|
||||||
"""
|
"""
|
||||||
return jsonify(data)
|
return jsonify(data)
|
||||||
|
|
||||||
|
|
||||||
|
@current_app.route('/api/quote_surround')
|
||||||
|
def api_quote_neighbors():
|
||||||
|
season, episode = int(request.args.get('season')), int(request.args.get('episode'))
|
||||||
|
scene, quote = int(request.args.get('scene')), int(request.args.get('quote'))
|
||||||
|
|
||||||
|
quotes = data[season - 1]['episodes'][episode - 1]['scenes'][scene - 1]['quotes']
|
||||||
|
top, below = get_neighbors(quotes, quote - 1, int(request.args.get('distance', 2)))
|
||||||
|
return jsonify({'above': top, 'below': below})
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from flask_wtf.csrf import CSRFProtect
|
|||||||
from server.config import configs
|
from server.config import configs
|
||||||
|
|
||||||
csrf = CSRFProtect()
|
csrf = CSRFProtect()
|
||||||
cors = CORS(resources={r'/*': {'origins': '*'}})
|
cors = CORS(resources={r'/api/*': {'origins': '*'}})
|
||||||
|
|
||||||
|
|
||||||
def create_app(env=None):
|
def create_app(env=None):
|
||||||
|
|||||||
@@ -1,9 +1,32 @@
|
|||||||
|
"""
|
||||||
|
helpers.py
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import List, Tuple
|
||||||
|
|
||||||
episode_counts = [6, 22, 23, 14, 26, 24, 24, 24, 23]
|
episode_counts = [6, 22, 23, 14, 26, 24, 24, 24, 23]
|
||||||
|
|
||||||
|
|
||||||
def check_validity(season: int, episode: int):
|
def check_validity(season: int, episode: int):
|
||||||
|
"""Shorthand function for checking if a specific episode is valid."""
|
||||||
return (1 <= season <= 9) and (1 <= episode <= episode_counts[season])
|
return (1 <= season <= 9) and (1 <= episode <= episode_counts[season])
|
||||||
|
|
||||||
|
|
||||||
def default(value, other):
|
def default(value, other):
|
||||||
|
"""Value default, similar to dict.get, but better."""
|
||||||
return value if value is not None else other
|
return value if value is not None else other
|
||||||
|
|
||||||
|
|
||||||
|
def get_neighbors(array: List, index: int, distance: int = 2) -> Tuple[List, List]:
|
||||||
|
"""Get neighbors above and below a specific index in an array. Returns maximum number of items possible."""
|
||||||
|
top, below = [], []
|
||||||
|
for i in range(1, distance + 1):
|
||||||
|
top_index = index - i
|
||||||
|
below_index = index + i
|
||||||
|
if top_index >= 0:
|
||||||
|
top.append(array[top_index])
|
||||||
|
if below_index < len(array):
|
||||||
|
below.append(array[below_index])
|
||||||
|
return top[::-1], below[::-1]
|
||||||
|
|||||||
Reference in New Issue
Block a user