Browse Source

Merge branch '95-predict-unpredicted' into 'master'

Resolve "predict unpredicted"

Closes #95

See merge request troebs/pycs!103
Eric Tröbs 4 years ago
parent
commit
a3da8777ef

+ 2 - 0
pycs/frontend/WebServer.py

@@ -97,6 +97,8 @@ class WebServer:
                 app_status['projects'].fit(identifier)
             elif 'predictAll' in data.keys():
                 app_status['projects'].predict(identifier)
+            elif 'predictUnlabeled' in data.keys():
+                app_status['projects'].predict(identifier, unlabeled=True)
             elif 'predict' in data.keys():
                 app_status['projects'].predict(identifier, data['predict'])
             else:

+ 6 - 3
pycs/projects/Project.py

@@ -163,7 +163,7 @@ class Project(ObservableDict):
         # remove label from list
         del self['labels'][identifier]
 
-    def predict(self, identifiers):
+    def predict(self, identifiers, unlabeled=False):
         # create pipeline
         pipeline = self.__create_pipeline()
 
@@ -171,11 +171,14 @@ class Project(ObservableDict):
         if self['unmanaged'] is None:
             for file_id in identifiers:
                 if file_id in self['data'].keys():
-                    pipeline.run(self['data'][file_id])
+                    if not unlabeled or len(self['data'][file_id]['predictionResults'].keys()) == 0:
+                        pipeline.run(self['data'][file_id])
+
         else:
             for file_id in identifiers:
                 if file_id in self.unmanaged_files:
-                    pipeline.run(self.unmanaged_files[file_id])
+                    if not unlabeled or len(self.unmanaged_files[file_id].get_data()['predictionResults']) == 0:
+                        pipeline.run(self.unmanaged_files[file_id])
 
         # schedule timeout thread
         self.quit_pipeline_thread = spawn_after(self.DEFAULT_PIPELINE_TIMEOUT, self.__quit_pipeline)

+ 2 - 2
pycs/projects/ProjectManager.py

@@ -101,7 +101,7 @@ class ProjectManager(ObservableDict):
         # delete project data
         del self[uuid]
 
-    def predict(self, uuid, identifiers=None):
+    def predict(self, uuid, identifiers=None, unlabeled=False):
         # abort if uuid is no valid key
         if uuid not in self.keys():
             return
@@ -116,7 +116,7 @@ class ProjectManager(ObservableDict):
                 identifiers = project.unmanaged_files_keys
 
         # run prediction
-        project.predict(identifiers)
+        project.predict(identifiers, unlabeled=unlabeled)
 
     def fit(self, uuid):
         # abort if uuid is no valid key

+ 12 - 0
webui/src/App.vue

@@ -215,4 +215,16 @@ export default {
   width: 100%;
   height: 100%;
 }
+
+/deep/ .headline {
+  width: 100%;
+  font-family: "Roboto Condensed";
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  overflow: hidden;
+
+  margin: 0 0 1rem;
+  padding-bottom: 0.5rem;
+  border-bottom: 1px solid rgba(0, 0, 0, 0.2);
+}
 </style>

+ 3 - 4
webui/src/components/projects/project-data-add-window.vue

@@ -1,11 +1,10 @@
 <template>
   <div class="project-data-add-window">
     <!-- TODO use valid url -->
+    <h1 class="headline">Data</h1>
     <file-input :socket="socket" :name="'/projects/' + currentProject.id + '/data'"></file-input>
 
     <template v-if="uploads.length > 0">
-      <h1>Uploads</h1>
-
       <div class="uploads">
         <template v-for="up in uploads">
           <div :key="up.id + '-div'">{{ up.filename }}</div>
@@ -15,8 +14,6 @@
     </template>
 
     <template v-if="this.data.length > 0">
-      <h1>Data</h1>
-
       <div class="wrapper">
         <div class="data">
           <div v-for="d in this.data"
@@ -100,6 +97,7 @@ h1 {
   grid-template-columns: auto 1fr;
   max-height: 15vh;
   overflow: auto;
+  margin-top: 1rem;
 }
 
 .progress-bar {
@@ -110,6 +108,7 @@ h1 {
   flex-basis: 0;
   flex-grow: 1;
   overflow-y: auto;
+  margin-top: 2rem;
 }
 
 .data {

+ 2 - 0
webui/src/components/projects/project-labels-window.vue

@@ -1,5 +1,7 @@
 <template>
   <div class="project-labels-window">
+    <h1 class="headline">Labels</h1>
+
     <div class="label" v-for="label in labels" :key="label.id">
       <editable-headline :value="label.name"
                          @change="editLabel(label.id, $event)"

+ 22 - 10
webui/src/components/projects/project-model-interaction-window.vue

@@ -1,20 +1,24 @@
 <template>
   <div class="project-model-interaction-window">
-    <div>
+    <h1 class="headline">Model</h1>
+
+    <h2>Predictions</h2>
+    <button-row>
       <button-input type="primary"
-                    @click="predict">predict all images
+                    @click="predictUnlabeled">predict unlabeled
       </button-input>
-    </div>
-
-    <div>
       <button-input type="primary"
-                    @click="fit">fit model with new data
+                    @click="predictAll">predict all
       </button-input>
-    </div>
+      <button-input type="primary"
+                    @click="download">download predictions
+      </button-input>
+    </button-row>
 
+    <h2>Fit</h2>
     <div>
       <button-input type="primary"
-                    @click="download">download predicted data
+                    @click="fit">fit model with new data
       </button-input>
     </div>
   </div>
@@ -22,13 +26,17 @@
 
 <script>
 import ButtonInput from "@/components/base/button-input";
+import ButtonRow from "@/components/base/button-row";
 
 export default {
   name: "project-model-interaction-window",
-  components: {ButtonInput},
+  components: {ButtonRow, ButtonInput},
   props: ['currentProject', 'socket'],
   methods: {
-    predict: function () {
+    predictUnlabeled: function () {
+      this.socket.post('/projects/' + this.currentProject.id, {'predictUnlabeled': true});
+    },
+    predictAll: function () {
       this.socket.post('/projects/' + this.currentProject.id, {'predictAll': true});
     },
     fit: function () {
@@ -49,4 +57,8 @@ export default {
 .project-model-interaction-window > div {
   margin-bottom: 1rem;
 }
+
+h2 {
+  margin-bottom: 0.5rem;
+}
 </style>

+ 1 - 11
webui/src/components/projects/project-settings-window.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="project-settings-window">
-    <h1>Project Settings</h1>
+    <h1 class="headline">Project Settings</h1>
 
     <div class="content">
       <text-input placeholder="Name"
@@ -115,16 +115,6 @@ export default {
   padding: 1rem;
 }
 
-h1 {
-  margin: 0;
-  font-family: "Roboto Condensed";
-
-  text-overflow: ellipsis;
-  width: 100%;
-  white-space: nowrap;
-  overflow: hidden;
-}
-
 .content {
   overflow: auto;
   flex-grow: 1;