Browse Source

added possibility to copy results from previously annotated file

Dimitri Korsch 3 năm trước cách đây
mục cha
commit
aeb3d90d4a

+ 5 - 0
pycs/frontend/WebServer.py

@@ -44,6 +44,7 @@ from pycs.frontend.endpoints.projects.ListProjectCollections import ListProjectC
 from pycs.frontend.endpoints.projects.ListProjectFiles import ListProjectFiles
 from pycs.frontend.endpoints.projects.RemoveProject import RemoveProject
 from pycs.frontend.endpoints.results.ConfirmResult import ConfirmResult
+from pycs.frontend.endpoints.results.CopyResults import CopyResults
 from pycs.frontend.endpoints.results.CreateResult import CreateResult
 from pycs.frontend.endpoints.results.EditResultData import EditResultData
 from pycs.frontend.endpoints.results.EditResultLabel import EditResultLabel
@@ -267,6 +268,10 @@ class WebServer:
             '/data/<int:file_id>/results',
             view_func=CreateResult.as_view('create_result', self.notifications)
         )
+        self.app.add_url_rule(
+            '/data/<int:file_id>/copy_results',
+            view_func=CopyResults.as_view('copy_results', self.notifications)
+        )
         self.app.add_url_rule(
             '/data/<int:file_id>/reset',
             view_func=ResetResults.as_view('reset_results', self.notifications)

+ 3 - 1
pycs/frontend/endpoints/results/ConfirmResult.py

@@ -1,4 +1,6 @@
-from flask import make_response, request, abort
+from flask import abort
+from flask import make_response
+from flask import request
 from flask.views import View
 
 from pycs.database.Result import Result

+ 51 - 0
pycs/frontend/endpoints/results/CopyResults.py

@@ -0,0 +1,51 @@
+from flask import abort
+from flask import jsonify
+from flask import make_response
+from flask import request
+from flask.views import View
+
+from pycs import db
+from pycs.database.File import File
+from pycs.database.File import Result
+from pycs.frontend.notifications.NotificationManager import NotificationManager
+
+
+class CopyResults(View):
+    """
+    copy all results for one file to another
+    """
+    # pylint: disable=arguments-differ
+    methods = ['POST']
+
+    def __init__(self, nm: NotificationManager):
+        # pylint: disable=invalid-name
+        self.nm = nm
+
+    def dispatch_request(self, file_id: int):
+
+        file = File.get_or_404(file_id)
+        request_data = request.get_json(force=True)
+
+        if 'copy_from' not in request_data:
+            abort(400, "copy_from argument is missing")
+
+        other_file = File.get_or_404(request_data.get('copy_from'))
+
+        new = []
+        # start transaction
+        with db.session.begin_nested():
+
+            for result in other_file.results.all():
+                new_result = file.create_result(
+                    origin='pipeline',
+                    result_type=result.type,
+                    label=result.label,
+                    data=result.data,
+                    commit=False)
+                new.append(new_result)
+
+
+        for new_result in new:
+            self.nm.create_result(new_result)
+
+        return make_response()

+ 64 - 0
webui/src/assets/icons/double-chevron-left.svg

@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   viewBox="0 0 16 16"
+   width="16"
+   height="16"
+   version="1.1"
+   id="svg4"
+   sodipodi:docname="double-chevron-left.svg"
+   inkscape:version="0.92.3 (2405546, 2018-03-11)">
+  <metadata
+     id="metadata10">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs8" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1920"
+     inkscape:window-height="1135"
+     id="namedview6"
+     showgrid="false"
+     inkscape:zoom="14.75"
+     inkscape:cx="-6.6779661"
+     inkscape:cy="8"
+     inkscape:window-x="1200"
+     inkscape:window-y="536"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg4" />
+  <g
+     id="g834"
+     transform="matrix(-1,0,0,1,18.776483,0.00902426)">
+    <path
+       id="path2"
+       d="m 6.22,3.22 a 0.75,0.75 0 0 1 1.06,0 l 4.25,4.25 a 0.75,0.75 0 0 1 0,1.06 L 7.28,12.78 A 0.75,0.75 0 0 1 6.22,11.72 L 9.94,8 6.22,4.28 a 0.75,0.75 0 0 1 0,-1.06 z"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd" />
+    <path
+       id="path2-3"
+       d="m 10.022966,3.2199998 a 0.75,0.75 0 0 1 1.06,0 l 4.25,4.25 a 0.75,0.75 0 0 1 0,1.06 l -4.25,4.2500002 a 0.75,0.75 0 0 1 -1.06,-1.06 l 3.72,-3.7200002 -3.72,-3.72 a 0.75,0.75 0 0 1 0,-1.06 z"
+       style="fill-rule:evenodd"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>

+ 64 - 0
webui/src/assets/icons/double-chevron-right.svg

@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   viewBox="0 0 16 16"
+   width="16"
+   height="16"
+   version="1.1"
+   id="svg4"
+   sodipodi:docname="double-chevron-right.svg"
+   inkscape:version="0.92.3 (2405546, 2018-03-11)">
+  <metadata
+     id="metadata10">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs8" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1920"
+     inkscape:window-height="1135"
+     id="namedview6"
+     showgrid="false"
+     inkscape:zoom="14.75"
+     inkscape:cx="8"
+     inkscape:cy="8"
+     inkscape:window-x="1200"
+     inkscape:window-y="536"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg4" />
+  <g
+     id="g834"
+     transform="translate(-2.776483,0.00902426)">
+    <path
+       id="path2"
+       d="m 6.22,3.22 a 0.75,0.75 0 0 1 1.06,0 l 4.25,4.25 a 0.75,0.75 0 0 1 0,1.06 L 7.28,12.78 A 0.75,0.75 0 0 1 6.22,11.72 L 9.94,8 6.22,4.28 a 0.75,0.75 0 0 1 0,-1.06 z"
+       inkscape:connector-curvature="0"
+       style="fill-rule:evenodd" />
+    <path
+       id="path2-3"
+       d="m 10.022966,3.2199998 a 0.75,0.75 0 0 1 1.06,0 l 4.25,4.25 a 0.75,0.75 0 0 1 0,1.06 l -4.25,4.2500002 a 0.75,0.75 0 0 1 -1.06,-1.06 l 3.72,-3.7200002 -3.72,-3.72 a 0.75,0.75 0 0 1 0,-1.06 z"
+       style="fill-rule:evenodd"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>

+ 2 - 4
webui/src/components/media/media-control.vue

@@ -6,8 +6,7 @@
                   style="color: var(--on_error)"
                   :class="{disabled: !hasPreviousPage}"
                   @click="$emit('previousPage', true)">
-      <img alt="next" :class="{disabled: !hasPreviousPage}" src="@/assets/icons/chevron-left.svg">
-      <img alt="next" :class="{disabled: !hasPreviousPage}" src="@/assets/icons/chevron-left.svg">
+      <img alt="next" :class="{disabled: !hasPreviousPage}" src="@/assets/icons/double-chevron-left.svg">
     </button-input>
 
     <button-input ref="previousElement"
@@ -53,8 +52,7 @@
                   style="color: var(--on_error)"
                   :class="{disabled: !hasNextPage}"
                   @click="$emit('nextPage', true)">
-      <img alt="next" :class="{disabled: !hasNextPage}" src="@/assets/icons/chevron-right.svg">
-      <img alt="next" :class="{disabled: !hasNextPage}" src="@/assets/icons/chevron-right.svg">
+      <img alt="next" :class="{disabled: !hasNextPage}" src="@/assets/icons/double-chevron-right.svg">
 
     </button-input>
   </div>

+ 31 - 11
webui/src/components/media/paginated-media.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="paginated-media">
     <div class="media" ref="media">
-      <div v-for="image in images"
+      <div v-for="(image, i) in images"
            v-bind:key="image.path"
            class="image"
            @click="$emit('click', image)">
@@ -10,15 +10,18 @@
         <div v-if="current && current.path === image.path"
              class="active"/>
 
-        <div v-if="deletable"
-             class="delete"
-             @click="deleteElement(image)">
-
+        <div v-if="deletable" class="media-control delete" @click="deleteElement(image)">
           <img alt="remove" src="@/assets/icons/x-circle.svg">
         </div>
-        <div v-if="image.has_annotations" class="annotated">
+
+        <div title="Annotated file" v-if="image.has_annotations" class="annotated">
           <img alt="annotated" src="@/assets/icons/tag.svg">
         </div>
+
+        <div title="Copy results from previous file" v-if="inline && i != 0" class="media-control copy" @click="copyFrom(i, image)">
+          <img alt="copy" src="@/assets/icons/paper-airplane.svg">
+        </div>
+
       </div>
     </div>
 
@@ -125,6 +128,12 @@ export default {
     deleteElement: function (element) {
       this.$root.socket.post(`/data/${element.identifier}/remove`, {remove: true});
     },
+    copyFrom: function (fromIdx, element) {
+      if (fromIdx === undefined || fromIdx === 0)
+        return;
+      let copy_from = this.images[fromIdx-1].identifier;
+      this.$root.socket.post(`/data/${element.identifier}/copy_results`, {copy_from});
+    },
     prevPage: function (callback) {
       if (this.page > 1)
         this.page -= 1;
@@ -323,15 +332,21 @@ export default {
   box-shadow: 0 0 20px -5px var(--primary) inset;
 }
 
+.media .media-control {
+  background-color: rgba(255, 255, 255, 0.4);
+  border-radius: 2rem;
+  filter: invert(1);
+  padding: 0.3rem 0.3rem 0.1rem;
+}
+
+.media .media-control:hover {
+  background-color: rgba(255, 255, 255, 0.8);
+}
+
 .media .delete {
   position: absolute;
   top: 0.15rem;
   right: 0.15rem;
-
-  background-color: rgba(255, 255, 255, 0.4);
-  border-radius: 2rem;
-  padding: 0.3rem 0.3rem 0.1rem;
-  filter: invert(1);
 }
 
 .media .annotated {
@@ -341,6 +356,11 @@ export default {
 
   padding: 0.3rem 0.3rem 0.3rem;
 }
+.media .copy {
+  position: absolute;
+  bottom: 0.10rem;
+  left: 0.10rem;
+}
 
 .media .delete img {
   width: 1rem;