Attention une mise à jour du serveur va être effectuée le lundi 17 mai entre 13h et 13h30. Cette mise à jour va générer une interruption du service de quelques minutes.

Commit aa812094 authored by Robin Tissot's avatar Robin Tissot

Merge branch 'feature/goto-page' into 'develop'

Feature/goto page

See merge request !69
parents ac1696c1 718c65b0
......@@ -102,11 +102,15 @@ class DocumentSerializer(serializers.ModelSerializer):
transcriptions = TranscriptionSerializer(many=True, read_only=True)
valid_block_types = BlockTypeSerializer(many=True, read_only=True)
valid_line_types = LineTypeSerializer(many=True, read_only=True)
parts_count = serializers.SerializerMethodField()
class Meta:
model = Document
fields = ('pk', 'name', 'transcriptions',
'valid_block_types', 'valid_line_types')
'valid_block_types', 'valid_line_types', 'parts_count')
def get_parts_count(self, document):
return document.parts.count()
class PartSerializer(serializers.ModelSerializer):
......@@ -127,6 +131,7 @@ class PartSerializer(serializers.ModelSerializer):
'image',
'bw_image',
'workflow',
'order',
'recoverable',
'transcription_progress'
)
......
......@@ -4,7 +4,9 @@ import logging
from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.db.models import Prefetch
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.urls import reverse
from rest_framework.decorators import action
from rest_framework.response import Response
......@@ -193,6 +195,22 @@ class PartViewSet(DocumentPermissionMixin, ModelViewSet):
else: # list & create
return PartSerializer
@action(detail=False, methods=['get'])
def byorder(self, request, document_pk=None):
try:
order = int(request.GET.get('order'))
except ValueError:
return Response({'error': 'invalid order.'})
if not order:
return Response({'error': 'pass order as an url parameter.'})
try:
part = self.get_queryset().get(order=order)
except DocumentPart.DoesNotExist:
return Response({'error': 'Out of bounds.'})
return HttpResponseRedirect(reverse('api:part-detail',
kwargs={'document_pk': self.kwargs.get('document_pk'),
'pk': part.pk}))
@action(detail=True, methods=['post'])
def move(self, request, document_pk=None, pk=None):
part = DocumentPart.objects.get(document=document_pk, pk=pk)
......
......@@ -244,6 +244,13 @@ class Document(models.Model):
else:
return 'ltr'
@cached_property
def last_edited_part(self):
try:
return self.parts.order_by('-updated_at')[0]
except IndexError:
return None
@property
def training_model(self):
return self.ocr_models.filter(training=True).first()
......
......@@ -76,6 +76,7 @@
<div id="image-card-{pk}" class="card">
<div style="position: relative;">
<button class="js-card-select-hdl"><i class="fas fa-square"></i></button>
<span class="js-card-order"></span>
<button title="{% trans 'Delete this image and all its data (segmentation, transcriptions)' %}" class="close mr-1 js-card-delete" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<img style="object-fit: cover;" width="180" height="180" class="card-img-top lazy">
<div class="card-btns">
......
......@@ -13,11 +13,11 @@
<div class="col-md-12 col-md-offset-4">
<a href="{% url 'document-create' %}" class="btn btn-success float-sm-right">{% trans 'Create new' %}</a>
<h2>{% trans "My Documents" %}</h2>
<table class="table table-hover">
<table id="document-list" class="table table-hover">
<tbody>
{% for document in object_list %}
<tr>
<tr onclick="document.location='{% url 'document-images' pk=document.pk %}'" role="button">
<td>
{% with part=document.parts.first %}
{% if part %}
......@@ -47,16 +47,26 @@
</td>
{% endcomment %}
<td class="text-right">
{# <a href="{% url 'document-detail' pk=document.pk %}" class="btn btn-info disabled" title="{% trans 'View' %}"><i class="fas fa-eye"></i></a> #}
<a href="{% url 'document-update' pk=document.pk %}" class="btn btn-info" title="{% trans 'Edit' %}"><i class="fas fa-edit"></i></a>
{# <a href="{% url 'document-detail' pk=document.pk %}" class="btn btn-info disabled" title="{% trans 'View' %}"><i class="fas fa-eye"></i></a> #}
{# Note that doing one query per row is a lot faster than a subquery for some reason #}
{% if document.last_edited_part %}
<a href="{% url 'document-part-edit' pk=document.pk part_pk=document.last_edited_part.pk %}"
class="btn btn-info"
title="{% trans 'Edit last updated Element' %}">
<i class="fas fa-edit"></i>
</a>
{% endif %}
<form method="post" class="inline-form" action="{% url 'document-publish' pk=document.pk %}" onsubmit="return confirm('{% trans "Do you really want to delete the document?" %}');">{% csrf_token %}
<input type="hidden" name="workflow_state" value="{{ document.WORKFLOW_STATE_ARCHIVED }}">
<button type="submit" value="" class="nav-item btn btn-danger" title="{% trans 'Delete' %}"><i class="fas fa-trash"></i></button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% include 'includes/pagination.html' %}
{% endblock %}
......@@ -14,7 +14,7 @@
<a href="{% if document and models_count %}{% url 'document-models' document_pk=document.pk %}{% else %}#{% endif %}" class="nav-item nav-link {% if not document or not models_count %}disabled{% endif %}" id="nav-models-tab" role="tab" aria-controls="nav-doc" aria-selected="true">{% trans "Models" %}</a>
{% endwith %}
{% endif %}
{% block extra_nav %}{% endblock %}
{% block extra_nav %}<div class="nav-div nav-item ml-5 ">{{document.name}}</div>{% endblock %}
</div>
</nav>
......
......@@ -134,6 +134,7 @@ form.inline-form {
#cards-container {
overflow: auto;
counter-reset: card;
}
#cards-container .card {
......@@ -192,6 +193,16 @@ form.inline-form {
border: 0;
}
.js-card-order {
position: absolute;
left: 50%;
}
.js-card-order:after {
counter-increment: card;
content: counter(card);
}
.process-part-form h5 {
color: var(--secondary);
text-align: center;
......@@ -381,6 +392,10 @@ form.inline-form {
color: grey;
}
#gotoModal .modal-dialog {
max-width: 355px;
}
.col-sides {
width: 30px;
margin: 0 15px;
......
......@@ -10,6 +10,8 @@ export const retrieveDocument = async document_id => (await axios.get(`/document
export const retrieveDocumentPart = async (document_id, part_id) => (await axios.get(`/documents/${document_id}/parts/${part_id}/`))
export const retrieveDocumentPartByOrder = async (document_id, order) => (await axios.get(`/documents/${document_id}/parts/byorder/?order=${order}`))
export const retrievePage = async (document_id, part_id, transcription, page) => (await axios.get(`/documents/${document_id}/parts/${part_id}/transcriptions/?transcription=${transcription}&page=${page}`))
export const createContent = async (document_id, part_id, data) => (await axios.post(`/documents/${document_id}/parts/${part_id}/transcriptions/`, data))
......@@ -40,4 +42,4 @@ export const bulkCreateLineTranscriptions = async (document_id, part_id, data) =
export const bulkUpdateLineTranscriptions = async (document_id, part_id, data) => (await axios.put(`/documents/${document_id}/parts/${part_id}/transcriptions/bulk_update/`, data))
export const moveLines = async (document_id, part_id, data) => (await axios.post(`/documents/${document_id}/parts/${part_id}/lines/move/`, data))
\ No newline at end of file
export const moveLines = async (document_id, part_id, data) => (await axios.post(`/documents/${document_id}/parts/${part_id}/lines/move/`, data))
......@@ -4,6 +4,7 @@ import * as api from '../api'
export const initialState = () => ({
id: null,
name: "",
partsCount: 0,
defaultTextDirection: null,
mainTextDirection: null,
readDirection: null,
......@@ -39,6 +40,9 @@ export const mutations = {
setTypes (state, types) {
state.types = types
},
setPartsCount(state, count) {
state.partsCount = count
},
setBlockShortcuts(state, block) {
state.blockShortcuts = block
},
......@@ -56,6 +60,7 @@ export const actions = {
let data = resp.data
commit('transcriptions/set', data.transcriptions, {root: true})
commit('setTypes', { 'regions': data.valid_block_types, 'lines': data.valid_line_types })
commit('setPartsCount', data.parts_count)
},
async togglePanel ({state, commit}, panel) {
......
......@@ -31,12 +31,19 @@ export const mutations = {
}
export const actions = {
async fetchPart ({commit, dispatch, rootState}, pk) {
commit('setPartPk', pk)
async fetchPart ({commit, dispatch, rootState}, {pk, order}) {
if (!rootState.transcriptions.all.length) {
await dispatch('document/fetchDocument', rootState.document.id, {root: true})
}
const resp = await api.retrieveDocumentPart(rootState.document.id, pk)
var resp
if (pk) {
commit('setPartPk', pk)
resp = await api.retrieveDocumentPart(rootState.document.id, pk)
} else {
resp = await api.retrieveDocumentPartByOrder(rootState.document.id, order)
commit('setPartPk', resp.data.pk)
}
let data = resp.data
data.lines.forEach(function(line) {
......@@ -63,7 +70,20 @@ export const actions = {
commit('regions/reset', {}, {root: true})
commit('lines/reset', {}, {root: true})
commit('reset')
await dispatch('fetchPart', pk)
await dispatch('fetchPart', {pk: pk})
},
async loadPartByOrder({state, commit, dispatch, rootState}, order) {
commit('regions/reset', {}, {root: true})
commit('lines/reset', {}, {root: true})
commit('reset')
try {
await dispatch('fetchPart', {order: order})
await dispatch('transcriptions/getCurrentContent', rootState.transcriptions.selectedTranscription, {root: true})
await dispatch('transcriptions/getComparisonContent', {}, {root: true})
} catch (err) {
console.log('couldnt fetch part data!', err)
}
},
async loadPart({state, commit, dispatch, rootState}, direction) {
......@@ -73,7 +93,7 @@ export const actions = {
commit('lines/reset', {}, {root: true})
commit('reset')
try {
await dispatch('fetchPart', part)
await dispatch('fetchPart', {pk: part})
await dispatch('transcriptions/getCurrentContent', rootState.transcriptions.selectedTranscription, {root: true})
await dispatch('transcriptions/getComparisonContent', {}, {root: true})
} catch (err) {
......
......@@ -36,6 +36,7 @@ function openWizard(proc) {
class partCard {
constructor(part) {
this.pk = part.pk;
this.order = part.order;
this.name = part.name;
this.title = part.title;
this.typology = part.typology;
......
......@@ -81,7 +81,7 @@ export default {
this.$store.commit('document/setMainTextDirection', this.mainTextDirection);
this.$store.commit('document/setReadDirection', this.readDirection);
try {
await this.$store.dispatch('parts/fetchPart', this.partId);
await this.$store.dispatch('parts/fetchPart', {pk: this.partId});
let tr = userProfile.get('initialTranscriptions')
&& userProfile.get('initialTranscriptions')[this.$store.state.document.id]
|| this.$store.state.transcriptions.all[0].pk;
......
......@@ -2,18 +2,59 @@
<div>
<div class="nav-div nav-item ml-2">
<span v-if="$store.state.document.name" id="part-name">{{ $store.state.document.name }}</span>
<span id="part-title" v-if="$store.state.parts.loaded">{{ $store.state.parts.title }} - {{ $store.state.parts.filename }} - ({{ imageSize }})</span>
<span id="part-title"
v-if="$store.state.parts.loaded"
title="Click to go to another Element (Ctrl+Home)."
data-toggle="modal"
data-target="#gotoModal"
role="button">{{ $store.state.parts.title }} - {{ $store.state.parts.filename }} - ({{ imageSize }})</span>
<span class="loading" v-if="!$store.state.parts.loaded">Loading&#8230;</span>
</div>
<div id="gotoModal"
class="modal ui-draggable show"
tabindex="-1"
role="dialog">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-body">
Element #
<input type="number"
v-if="$store.state.parts.loaded"
min="1"
:max="$store.state.document.partsCount"
width="100%"
v-bind:value="$store.state.parts.order+1"
@change.lazy="goTo"/>
/ {{$store.state.document.partsCount}}
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
async created() {
document.addEventListener('keyup', async function(event) {
if (event.ctrlKey && event.keyCode == 36) { // Home
$('#gotoModal').modal('show');
}
});
},
computed: {
imageSize() {
return this.$store.state.parts.image.size[0]+'x'+this.$store.state.parts.image.size[1];
},
},
methods: {
async goTo(ev) {
if (ev.target.value > 0 && ev.target.value <= parseInt(ev.target.attributes.max.value)) {
await this.$store.dispatch('parts/loadPartByOrder', ev.target.value-1);
$('#gotoModal').modal('hide');
}
}
}
}
</script>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment