瀏覽代碼

"Annotated by" and minor additions

Added user identification to results.
Credentials aren't needed to access media any more. This caused problems with static images and was unnecessary.
Patched uploading images (missing credentials and content type was overwritten).
blunk 3 年之前
父節點
當前提交
b083e28899

+ 2 - 1
migrations/versions/b03df3e31b8d_.py

@@ -1,7 +1,7 @@
 """empty message
 
 Revision ID: b03df3e31b8d
-Revises: 
+Revises:
 Create Date: 2021-08-11 12:46:17.757283
 
 """
@@ -102,6 +102,7 @@ def upgrade():
     sa.Column('file_id', sa.Integer(), nullable=False),
     sa.Column('origin', sa.String(), nullable=False),
     sa.Column('type', sa.String(), nullable=False),
+    sa.Column('origin_user', sa.String(), nullable=True),
     sa.Column('label_id', sa.Integer(), nullable=True),
     sa.Column('data_encoded', sa.String(), nullable=True),
     sa.ForeignKeyConstraint(['file_id'], ['file.id'], ondelete='CASCADE'),

+ 6 - 1
pycs/database/File.py

@@ -196,6 +196,7 @@ class File(NamedBaseModel):
     def create_result(self,
                       origin: str,
                       result_type: str,
+                      origin_user: str = None,
                       label: T.Optional[T.Union[Label, int]] = None,
                       data: T.Optional[dict] = None) -> Result:
         """
@@ -203,11 +204,15 @@ class File(NamedBaseModel):
 
         :return: result object
         """
+        if origin == "pipeline" and not origin_user is None:
+            raise ValueError("If an annotation was made by the pipeline no username"\
+                "can be specified!")
 
         result = Result.new(commit=False,
                             file_id=self.id,
                             origin=origin,
-                            type=result_type)
+                            type=result_type,
+                            origin_user=origin_user)
 
         result.data = data
 

+ 9 - 2
pycs/database/Result.py

@@ -14,6 +14,7 @@ class Result(BaseModel):
         nullable=False)
 
     origin = db.Column(db.String, nullable=False)
+    origin_user = db.Column(db.String, nullable=True)
     type = db.Column(db.String, nullable=False)
 
     label_id = db.Column(
@@ -26,6 +27,7 @@ class Result(BaseModel):
     serialize_only = BaseModel.serialize_only + (
         "file_id",
         "origin",
+        "origin_user",
         "type",
         "label_id",
         "data",
@@ -58,15 +60,20 @@ class Result(BaseModel):
             raise ValueError(f"Not supported type: {type(value)}")
 
     @commit_on_return
-    def set_origin(self, origin: str):
+    def set_origin(self, origin: str, origin_user: str = None):
         """
         set this results origin
 
         :param origin: either 'user' or 'pipeline'
+        :param origin_user: None if origin is 'pipeline' else name of the user
         :return:
         """
-        self.origin = origin
+        if origin == "pipeline" and not origin_user is None:
+            raise ValueError("If an annotation was made by the pipeline no user"\
+                "can be specified!")
 
+        self.origin = origin
+        self.origin_user = origin_user
 
     @commit_on_return
     def set_label(self, label: int):

+ 5 - 6
pycs/frontend/WebServer.py

@@ -93,7 +93,7 @@ class WebServer:
                 response.headers['Access-Control-Allow-Origin'] = 'http://localhost:8080'
                 response.headers['Access-Control-Allow-Credentials'] = 'true'
                 response.headers['Access-Control-Allow-Methods'] = 'POST, GET'
-                response.headers['Access-Control-Allow-Headers'] = 'Authorization, Content-Type'
+                response.headers['Access-Control-Allow-Headers'] = 'Authorization'
                 return response
 
         # create service objects
@@ -261,20 +261,19 @@ class WebServer:
         )
         self.app.add_url_rule(
             '/data/<int:file_id>',
-            view_func=self.htpasswd.required( GetFile.as_view('get_file') )
+            view_func=GetFile.as_view('get_file')
         )
         self.app.add_url_rule(
             '/data/<int:file_id>/<resolution>',
-            view_func=self.htpasswd.required( GetResizedFile.as_view('get_resized_file') )
+            view_func=GetResizedFile.as_view('get_resized_file')
         )
         self.app.add_url_rule(
             '/data/<int:file_id>/<resolution>/<crop_box>',
-            view_func=self.htpasswd.required( GetCroppedFile.as_view('get_cropped_file') )
+            view_func=GetCroppedFile.as_view('get_cropped_file')
         )
         self.app.add_url_rule(
             '/data/<int:file_id>/previous_next',
-            view_func=self.htpasswd.required(
-                GetPreviousAndNextFile.as_view('get_previous_and_next_file') )
+            view_func=GetPreviousAndNextFile.as_view('get_previous_and_next_file')
         )
 
         # results

+ 1 - 1
pycs/frontend/endpoints/data/GetCroppedFile.py

@@ -19,7 +19,7 @@ class GetCroppedFile(View):
     methods = ['GET']
 
 
-    def dispatch_request(self, user: str, file_id: int, resolution: str, crop_box: str):
+    def dispatch_request(self, file_id: int, resolution: str, crop_box: str):
         # get file from database
         file = File.get_or_404(file_id)
 

+ 1 - 1
pycs/frontend/endpoints/data/GetFile.py

@@ -13,7 +13,7 @@ class GetFile(View):
     # pylint: disable=arguments-differ
     methods = ['GET']
 
-    def dispatch_request(self, user: str, file_id: int):
+    def dispatch_request(self, file_id: int):
         file = File.get_or_404(file_id)
 
         abs_file_path = file.absolute_path

+ 1 - 1
pycs/frontend/endpoints/data/GetPreviousAndNextFile.py

@@ -12,7 +12,7 @@ class GetPreviousAndNextFile(View):
     methods = ['GET']
 
 
-    def dispatch_request(self, user: str, file_id: int):
+    def dispatch_request(self, file_id: int):
         # get file from database
         file = File.get_or_404(file_id)
 

+ 1 - 1
pycs/frontend/endpoints/data/GetResizedFile.py

@@ -18,7 +18,7 @@ class GetResizedFile(View):
     methods = ['GET']
 
 
-    def dispatch_request(self, user: str, file_id: int, resolution: str):
+    def dispatch_request(self, file_id: int, resolution: str):
         # get file from database
         file = File.get_or_404(file_id)
 

+ 1 - 1
pycs/frontend/endpoints/pipelines/PredictModel.py

@@ -171,7 +171,7 @@ class PredictModel(View):
                 # Add the labels determined in the inference process.
                 for i, result in enumerate(result_filter[file_id]):
                     result.label_id = bbox_labels[i].identifier
-                    result.set_origin('user', commit=True)
+                    result.set_origin('user', origin_user=None, commit=True)
                     notifications.add(notification_manager.edit_result, result)
 
                 # yield progress

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

@@ -28,7 +28,7 @@ class ConfirmResult(View):
             return abort(400, "confirm flag is missing")
 
 
-        result.set_origin('user')
+        result.set_origin('user', origin_user=user)
 
         self.nm.edit_result(result)
         return make_response()

+ 1 - 0
pycs/frontend/endpoints/results/CreateResult.py

@@ -68,6 +68,7 @@ class CreateResult(View):
             # insert into database
             result = file.create_result(
                 origin='user',
+                origin_user=user,
                 result_type=result_type,
                 label=label,
                 data=data)

+ 1 - 1
pycs/frontend/endpoints/results/EditResultData.py

@@ -30,7 +30,7 @@ class EditResultData(View):
             abort(400, "Could not find data argument!")
 
         result.data = data
-        result.set_origin('user', commit=True)
+        result.set_origin('user', origin_user=user, commit=True)
 
         self.nm.edit_result(result)
         return make_response()

+ 1 - 1
pycs/frontend/endpoints/results/EditResultLabel.py

@@ -35,7 +35,7 @@ class EditResultLabel(View):
             abort(400, "Label is required for 'labeled-images' results")
 
         result.label_id = label
-        result.set_origin('user', commit=True)
+        result.set_origin('user', origin_user=user, commit=True)
 
         self.nm.edit_result(result)
         return make_response()

+ 4 - 2
pycs/interfaces/MediaFile.py

@@ -50,7 +50,8 @@ class MediaFile:
         else:
             data = None
 
-        created = self.__file.create_result('pipeline', 'labeled-image', label, data)
+        created = self.__file.create_result(origin='pipeline',
+            result_type='labeled-image', label=label, data=data)
         self.__notifications.add(self.__notifications.notifications.create_result, created)
 
     def add_bounding_box(self, x: float, y: float, w: float, h: float,
@@ -77,7 +78,8 @@ class MediaFile:
         if label is not None and isinstance(label, MediaLabel):
             label = label.identifier
 
-        created = self.__file.create_result('pipeline', 'bounding-box', label, result)
+        created = self.__file.create_result(origin='pipeline',
+            result_type='bounding-box', label=label, data=result)
         self.__notifications.add(self.__notifications.notifications.create_result, created)
 
     def remove_predictions(self):

+ 1 - 1
tests/client/project_tests.py

@@ -426,6 +426,7 @@ class ProjectListTests(_BaseProjectTests):
 
             file.create_result(
                 origin="user",
+                origin_user="dummy_username",
                 result_type="bounding-box",
                 label=None,
                 data=dict(x=0, y=0, w=1, h=1)
@@ -516,4 +517,3 @@ class ProjectEditTests(_BaseProjectTests):
         self.post(url, json=dict(), status_code=400)
 
         self.assertEqual("Project for a test case", self.project.description)
-

+ 1 - 1
tests/client/result_tests.py

@@ -121,7 +121,7 @@ class ResultGettingTests(_BaseResultTests):
 
         for i in range(n):
             box = dict(x=0, y=0, w=0.9, h=1.0)
-            another_file.create_result("user", "bounding-box", data=box)
+            another_file.create_result("user", "bounding-box", origin_user="dummy_username", data=box)
 
         self.assertEqual(10, Result.query.count())
 

+ 6 - 1
webui/src/components/media/cropped-image.vue

@@ -7,7 +7,7 @@
     </div>
 
     <div class="label-container">
-      <h3> {{ label }} </h3>
+      <h3>{{ label }}</h3>
 
       <div  v-if="this.box.origin === 'user'"
             ref="create_predictions"
@@ -22,6 +22,11 @@
     <div v-if="src" class="image-container">
       <img alt="crop" :src="src"/>
     </div>
+
+    <div v-if="this.box.origin === 'user'">
+      Annotated by: {{this.box.origin_user}}
+    </div>
+
     <div v-else>
       click a bounding box
     </div>

+ 0 - 1
webui/src/main.js

@@ -45,7 +45,6 @@ new Vue({
                 headers: function () {
                     const authHeaders = new Headers();
                     authHeaders.set('Authorization', 'Basic ' + window.btoa(this.username + ":" + this.password));
-                    authHeaders.set('Content-Type', 'application/json');
                     return authHeaders;
                 },
                 url: function (name) {