Skip to content

Commit

Permalink
Feature/improved jobs logging (#1372)
Browse files Browse the repository at this point in the history
*  add mini button to JobItem vue, now in Typescript
* adapt Job model (typescript) to the message coming from IML

Additionally
* replace ArticleService with simpler Search
  • Loading branch information
danieleguido authored Nov 18, 2024
1 parent d6197ba commit eff1c1d
Show file tree
Hide file tree
Showing 8 changed files with 278 additions and 262 deletions.
2 changes: 1 addition & 1 deletion src/components/CollectionDetailPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
</b-tabs>

<b-navbar
class="px-0 py-0 border-bottom"
class="px-0 py-2 border-bottom"
v-if="
tab.name !== TAB_RECOMMENDATIONS &&
(tab.name !== TAB_OVERVIEW || $route.params.collection_uid)
Expand Down
14 changes: 14 additions & 0 deletions src/components/base/Icon.vue
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,20 @@ const Icons: Record<string, IconData> = {
}
]
},
search: {
width: 24,
height: 24,
paths: [
{
style: `fill:transparent;stroke-width:${props.strokeWidth}px;`,
d: 'M17 17L21 21'
},
{
style: `fill:transparent;stroke-width:${props.strokeWidth}px;`,
d: 'M3 11C3 15.4183 6.58172 19 11 19C13.213 19 15.2161 18.1015 16.6644 16.6493C18.1077 15.2022 19 13.2053 19 11C19 6.58172 15.4183 3 11 3C6.58172 3 3 6.58172 3 11Z'
}
]
},
play: {
width: 24,
height: 24,
Expand Down
288 changes: 114 additions & 174 deletions src/components/modules/lists/JobItem.vue
Original file line number Diff line number Diff line change
@@ -1,181 +1,127 @@
<template>
<div class="job-item">
<div class="JobItem" :class="className">
<h2 class="sans mt-3 mb-1 font-weight-medium font-size-inherit">
<span
>{{
$t(`jobs.type.${item.type}`, {
v-html="
$t(`jobs_type_${item.type}`, {
total: item.extra.total > -1 ? $n(item.extra.total) : ''
})
}}&nbsp;</span
>
<router-link
class="text-white text-decoration-underline"
v-if="item.extra.collection.name && item.extra.collection.status !== 'DEL'"
:to="{
name: 'collection',
params: {
collection_uid: item.extra.collection.id
}
}"
>
{{ item.extra.collection.name }} </router-link
><span class="small-caps ml-2" :class="[item.status]">{{
$t(`jobs.status.${item.status}`)
"
/>
<span v-if="item.extra.collection">: {{ item.extra.collection.name }} </span>&nbsp;
<span class="small-caps ml-2" :class="[item.status]">{{
$t(`jobs_status_${item.status}`)
}}</span>
</h2>
<div class="date">{{ $d(item.creationDate, 'precise') }} (#{{ item.id }})</div>
<div>
<blockquote v-if="hasSearchQuery" class="pl-2 my-1 border-left">
<search-query-summary class="m-0" :search-query="{ filters: jobSearchFilters }" />
</blockquote>
<blockquote v-else-if="item.extra.collection" class="pl-2 my-1 border-left">
<span style="line-height: 0.8" v-html="item.extra.collection.description" />
</blockquote>
<blockquote v-else v-html="item.description" class="pl-2 my-1 border-left small"></blockquote>

<div class="text-white number" v-if="item.isRunning()">&nbsp;{{ percentage }} %</div>
<div class="p-2 position-relative" v-if="item.isRunning()">
<div class="progress">
<div
class="progress-bar bg-success progress-bar-animated"
role="progressbar"
aria-valuemin="0"
aria-valuemax="100"
:aria-valuenow="percentage"
:style="`width: ${percentage}%; height: 4px;`"
></div>
</div>
</div>
<div class="date">
{{ $d(item.creationDate, 'precise') }}
(#{{ item.id }})
</div>
<div class="d-flex justify-content-between" v-if="isExportable || hasSearchQuery">
<b-button v-if="isExportable" variant="outline-success" size="sm" v-on:click="onExport()">
<div class="d-flex align-items-center">
<div>
{{ $t('actions.downloadCsv') }}
</div>
<div class="d-flex dripicons dripicons-download ml-2" />
</div>
</b-button>
<b-button
:to="searchPageLink"
<blockquote v-if="item.extra.collection" class="pl-2 my-1 border-left">
<span>
<router-link
class="text-white text-decoration-underline"
v-if="item.extra.collection.name && item.extra.collection.status !== 'DEL'"
:to="{ name: 'collection', params: { collection_uid: item.extra.collection.id } }"
>
{{ item.extra.collection.name }}
</router-link>
</span>
<span v-html="item.extra.collection.description" class="small" />
</blockquote>
<blockquote v-else class="pl-2 my-1 border-left">
<span v-html="item.description" class="small" />

&nbsp;
<button
v-if="hasSearchQuery"
variant="transparent"
class="text-white"
size="sm"
class="btn btn-xs text-white btn-outline-white d-inline-flex align-items-center"
@click="gotoSearchPage"
>
<div class="d-flex align-items-center">
<div>
{{ $t('actions.searchMore') }}
</div>
<div class="d-flex dripicons dripicons-search ml-2" />
{{ $t('actions.searchMore') }}<Icon name="search" :scale="0.5" :stroke-width="2" />
</button>
</blockquote>
<b-button
v-if="props.item.isExportable()"
variant="outline-success"
size="sm"
v-on:click="onExport()"
>
<div class="d-flex align-items-center">
<div>
{{ $t('actions.downloadCsv') }}
</div>
</b-button>
<b-button v-if="item.status === 'RUN'" size="sm" variant="outline-white" @click="stopJob">
Stop</b-button
>
</div>
<div class="d-flex dripicons dripicons-download ml-2" />
</div>
</b-button>
<b-button v-if="item.status === 'RUN'" size="sm" variant="outline-white" @click="stopJob">
Stop</b-button
>
</div>
</template>
<script setup lang="ts">
/**
* Model value is used to control the visibility of the panel.
*/
<script>
import Job from '@/models/Job'
import SearchQuery from '@/models/SearchQuery'
import { CommonQueryParameters } from '@/router/util'
import SearchQuerySummary from '@/components/modules/SearchQuerySummary.vue'
import Icon from '@/components/base/Icon.vue'
import { MIDDLELAYER_MEDIA_URL, getAuthenticationBearer, jobs as jobsService } from '@/services'
import { computed } from 'vue'
import router from '@/router'
import type { PropType } from 'vue'
export default {
props: {
item: {
type: Job
}
const props = defineProps({
item: {
type: Object as PropType<Job>,
required: true
},
computed: {
jobMediaUrl() {
return `${MIDDLELAYER_MEDIA_URL}/jobs/${this.item.id}`
},
jobSearchFilters() {
const sq = this.item.getSearchQueryHash()
if (!sq.length) {
return []
}
try {
return SearchQuery.deserialize(sq).filters
} catch (err) {
console.warn('Unable to deserialize job searchquery hash, skipping. Error received:', err)
return []
}
},
isExportable() {
return this.item.isExportable()
},
hasSearchQuery() {
return this.jobSearchFilters.length
},
searchPageLink() {
return {
name: 'search',
query: {
[CommonQueryParameters.SearchFilters]: this.item.getSearchQueryHash()
}
}
},
percentage() {
return this.item.getProgressAsPercentage()
}
},
methods: {
onExport() {
const today = new Date().toISOString().split('T').shift()
const anchor = document.createElement('a')
document.body.appendChild(anchor)
const headers = new Headers()
headers.append('Authorization', `Bearer ${getAuthenticationBearer()}`)
fetch(this.jobMediaUrl, { headers })
.then(res => res.blob())
.then(blobby => {
const objectUrl = window.URL.createObjectURL(blobby)
anchor.href = objectUrl
anchor.download = `export-${today}-${this.item.id}.zip`
anchor.click()
window.URL.revokeObjectURL(objectUrl)
})
},
async stopJob() {
console.debug('stopJob', this.item.id)
const result = await jobsService.patch(this.item.id, { status: 'stop' })
console.debug('stopJob result', result)
}
},
components: {
SearchQuerySummary
className: {
type: String,
default: ''
}
}
</script>
})
<style lang="scss" scoped>
.progress {
background-color: var(--clr-grey-200);
border-radius: 0;
height: 4px;
const hasSearchQuery = computed(() => {
return !!props.item.extra.query
})
const gotoSearchPage = () => {
router.push({
name: 'search',
query: {
sq: props.item.extra.sq
}
})
}
.job {
color: #c6ccd2;
const stopJob = async () => {
console.info('[JobItem] stopJob requested for:', props.item.id)
const result = await jobsService.patch(props.item.id, { status: 'stop' })
console.info('[JobItem] stopJob result:', result)
}
blockquote,
h3,
h3 a {
color: white;
const onExport = () => {
const today = new Date().toISOString().split('T').shift()
const anchor = document.createElement('a')
document.body.appendChild(anchor)
const headers = new Headers()
headers.append('Authorization', `Bearer ${getAuthenticationBearer()}`)
fetch(`${MIDDLELAYER_MEDIA_URL}/jobs/${props.item.id}`, { headers })
.then(res => res.blob())
.then(blobby => {
const objectUrl = window.URL.createObjectURL(blobby)
anchor.href = objectUrl
anchor.download = `export-${today}-${props.item.id}.zip`
anchor.click()
window.URL.revokeObjectURL(objectUrl)
})
}
blockquote,
</script>

<style scoped>
.date {
font-size: var(--impresso-font-size-smaller);
opacity: 0.65;
}
.bg-dark h2 {
color: inherit;
}
span.RUN {
background-color: var(--clr-grey-200);
color: white;
Expand All @@ -193,29 +139,23 @@ span.DON {
{
"en": {
"no-jobs-yet": "Here you will find notifications about your newly created collections and recent downloads.",
"jobs": {
"type": {
"ITR": "sync collection to related text reuse passages",
"EXP": "export search results as csv",
"DCO": "Deleting a collection",
"IDX": "Indexing {total} collection items",
"store_collectable_items": "Indexing collection items",
"TES": "Echo (TEST)",
"RTR": "Remove text reuse passages from your collection",
"test": "Echo (TEST)",
"BCQ": "Saving {total} item(s) in your collection",
"RDX": "Remove {total} item(s) from your collection",
"execute_solr_query": "Saving items in your collection",
"BCT": "Add {total} item(s) to your collection from Text Reuse"
},
"status": {
"DON": "done",
"RUN": "in progress",
"ERR": "error",
"STO": "stopped",
"RIP": "removed"
}
}
"jobs_type_ITR": "sync collection to related text reuse passages",
"jobs_type_EXP": "export search results as csv",
"jobs_type_DCO": "Deleting a collection",
"jobs_type_IDX": "Indexing {total} collection items",
"jobs_type_store_collectable_items": "Indexing collection items",
"jobs_type_TES": "Echo (TEST)",
"jobs_type_RTR": "Remove text reuse passages from your collection",
"jobs_type_test": "Echo (TEST)",
"jobs_type_BCQ": "Saving {total} item(s) in your collection",
"jobs_type_RDX": "Remove {total} item(s) from your collection",
"jobs_type_BCT": "Add {total} item(s) to your collection from Text Reuse",
"jobs_status_DON": "done",
"jobs_status_RUN": "in progress",
"jobs_status_ERR": "error",
"jobs_status_STO": "stopped",
"jobs_status_RIP": "removed"
}
}
</i18n>
Loading

0 comments on commit eff1c1d

Please sign in to comment.