From f53a361dde2156be71fa49812fc96b40e3eec052 Mon Sep 17 00:00:00 2001
From: Peter Sefton <peter.sefton@uts.edu.au>
Date: Thu, 31 Jan 2019 09:20:08 +1100
Subject: [PATCH] Nearly feature complete for describing the DataCrate sample
 dataset - need to be able to add a directory as a Dataset

---
 _CONTEXT_entities.json.template |  14 ++---
 routes/index.js                 |  55 ++++++++++++----
 views/index.ejs                 | 107 ++++++++++++++++++++------------
 3 files changed, 118 insertions(+), 58 deletions(-)

diff --git a/_CONTEXT_entities.json.template b/_CONTEXT_entities.json.template
index af0b8c8..cc51529 100644
--- a/_CONTEXT_entities.json.template
+++ b/_CONTEXT_entities.json.template
@@ -1,20 +1,20 @@
 {
-    "1" : {
+    "1" : {"properties" : {
          "type": [{"value": "Dataset"}],
         "name": [{"value": "Dataset"}], 
         "description": [{"value": "---- Your Description Goes here --- (Try to add more info than is in the name"}], 
         "creator": [{"id": "2", "value": ""}],
         "publisher": [{"id": "3", "value": ""}],
         "datePublished": [{"value": "2019"}]
-        },
-    "2" : {
+        }},
+    "2" : {"properties" : {
         "type": [{"value": "Person"}],
         "name": [{"value": "Person 1"}],  
-        "affiliation": [{"id": "2", "value": ""}]
-        },
-    "3" : {
+        "affiliation": [{"id": "3", "value": ""}]
+        }},
+    "3" : {"properties" : {
         "type": [{"value": "Organization"}],
         "name": [{"value": "Org"}], 
         "description": [{"value": ""}]
-        }
+        }}
 }  
\ No newline at end of file
diff --git a/routes/index.js b/routes/index.js
index 328e0ae..1684b84 100644
--- a/routes/index.js
+++ b/routes/index.js
@@ -27,10 +27,12 @@ var schema = {
     "instrument" :  {"input": "link", "type": ["IndividualProduct", "SoftwareApplication"]},
     "object" :  {"input": "link", "type": ["File", "Place", "Person", "Organization", "IndividualProduct"]},
     "agent" : {"input": "link", "type": ["Person"]},
+    "publisher" : {"input": "link", "type": ["Person", "Organization"]},
     "result" :  {"input": "link", "type": ["File"]},
     "contactPoint" :  {"input": "link", "type": ["ContactPoint"]},
     "contentLocation" :  {"input": "link", "type": ["Place"]},
     "geo" :  {"input": "link", "type": ["GeoCoordinates"]},
+    "thumbnail" :  {"input": "link", "type": ["File"]},
     "license" :  {"input": "link", "type": ["CreativeWork", "File"]},
     "latitude" : {"input":  "text"},
     "longitude" : {"input":  "text"},
@@ -41,7 +43,8 @@ var schema = {
 
   "types" : {
     "Person" : {
-      "template" : 
+      "create" : true,
+      "template" : {"properties": 
       {
         "type" : [{"value" : "Person"}],
         "name" :  [{"value" : " "}],
@@ -49,35 +52,49 @@ var schema = {
         "email" : [{"value" : " "}],
         "URL" : [{"value" : " "}]
       }
+    }
     },
 
     "Organization" : {
+      "create" : true,
+
       "template" : {
+      "properties": 
+       {
         "type" : [{"value" : "Organization"}],
         "name" :  [{"value" : " "}],
         "description" : [{"value" : " "}],
         "URL" : [{"value" : " "}]
-      }},
+      }}},
 
     "ContactPoint" : {
+      "create" : true,
+
         "template" : {
+          "properties": {
           "type" : [{"value" : "ContactPoint"}],
           "name" :  [{"value" : " "}],
           "email" : [{"value" : " "}],
           "URL" : [{"value" : " "}]
-        }},
+        }}},
 
   
       "GeoCoordinates" : {
+        "create" : true,
+
         "template" : {
+          "properties": {
           "type" : [{"value" : "GeoCoordinates"}],
           "latitude" :  [{"value" : " "}],
           "longitude" : [{"value" : " "}],
           "URL" : [{"value" : " "}],
-        }},
+        }}},
 
     "Dataset" : {
+      "create" : true,
+
         "template" : {
+          "properties": {
           "type" : [{"value" : "Dataset"}],
           "name" :  [{"value" : " "}],
           "description" : [{"value" : " "}],
@@ -85,10 +102,13 @@ var schema = {
           "creator" :  [{"id" : ""}],
           "contributor" :  [{"id" : ""}],
           "URL" : [{"value" : " "}]
-        }},
+        }}},
 
     "File" : {
+      "create" : false,
+
       "template" : {
+        "properties": {
         "type" : [{"value" : "File"}],
         "name" :  [{"value" : " "}],
         "description" : [{"value" : " "}],
@@ -96,38 +116,49 @@ var schema = {
         "contributor" :  [{"id" : ""}],
         "license" : [{"id" : ""}],
         "URL" : [{"value" : " "}]
-      }},
+      }}},
 
     "Place" : {
+      "create" : true,
+
       "template" : {
+        "properties": {
         "type" : [{"value" : "Place"}],
         "name" :  [{"value" : " "}],
         "description" : [{"value" : " "}],
         "URL" : [{"value" : " "}],
         "geo": [{"value" : " "}],
         "Address" : [{"value" : " "}]
-      }
-    },
+      }}},
 
     "IndividualProduct" : {
+      "create" : true,
+
       "template" : {
+        "properties": {
         "type" : [{"value" : "IndividualProduct"}],
         "name" :  [{"value" : " "}],
         "description" : [{"value" : " "}],
         "serialNumber" : [{"value" : ""}]
-      }
+      }}
     },
 
     "SoftwareApplication" : {
+      "create" : true,
+
       "template" : {
+        "properties": {
         "type" : [{"value" : "SoftwareApplication"}],
         "name" :  [{"value" : " "}],
         "description" : [{"value" : " "}],
         "version" : [{"value" : ""}]
-      }
+      }}
     },
     "CreateAction" :   {
+      "create" : true,
+
       "template" : {
+        "properties": {
         "type" : [{"value" : "CreateAction"}],
         "name" :  [{"value" : " "}],
         "agent" :  [{"id" : ""}],
@@ -135,7 +166,7 @@ var schema = {
         "result" :  [{"id" : ""}],
         "object" :  [{"id" : ""}],
 
-      }
+      }}
    }
   }
 }
@@ -172,7 +203,7 @@ router.get('/files', function(req, res, next) {
       files[absolute_path]= ({"filename": f, "name": f, "id": absolute_path})
       // Crude thumbnail-hack for now until we deal with this properly
       if (isImage(absolute_path)) {
-        files[absolute_path]["img"] = `/preview?path=${absolute_path}`;
+        files[absolute_path]["_img"] = `/preview?path=${absolute_path}`;
       }
    }
   }
diff --git a/views/index.ejs b/views/index.ejs
index 30fa368..94ca3a6 100644
--- a/views/index.ejs
+++ b/views/index.ejs
@@ -12,10 +12,11 @@
 
   <!-- Vue app element -->
     <div id="app" >
-        <h1><a v-bind:href="'?path=' + files.up">.. </a></h1>
+        {{ changed }}
+        <h1><a v-bind:href="'?path=' + files.up +  '&dataset_path=' +  dataset_path">.. </a></h1>
 
       <template v-if="context">
-      {{this.changed}}
+
         <template v-for="(item, id) in context">
           <h2 v-bind:id="id">
             <form v-on:submit.prevent="removeItem(id)">
@@ -23,39 +24,42 @@
               <button type="submit">-</button> {{ id }} 
           </form>
         </h2>
-        <img width="60%" v-bind:src='item.img'>
+        <img width="60%" v-bind:src='item._img'>
         <table width="100%" >
           
-          <tr v-for="(values, name) in item">
+          <tr v-for="(values, name) in item.properties">
             <td>
-                <form v-on:submit.prevent="removeProp(item, name)" style='display:inline;'>                     
+                <form v-on:submit.prevent="removeProp(item.properties, name)" style='display:inline;'>                     
                     <button type="submit">-</button>
                 </form>
                 {{ name }}
                 
             </td>
             <td>
-                <form v-on:submit.prevent="addProp(item, name)" style='display:inline;'>                     
+                <form v-on:submit.prevent="addProp(item.properties, name)" style='display:inline;'>                     
                     <button type="submit">+</button>
                 </form>
             </td>
             <td>               
              
                   <div v-for="(val, i_index) in values">
-                      
-                      
-                      
+
+                     <!-- TODO - make sure it's deletable -->
+                      <form v-on:submit.prevent="unlinkProp(val)" style='display:inline;'>                     
+                          <button type="submit" title="Delete this property">-</button>
+                      </form>
+                                  
                       <template v-if="schema.properties[name] && schema.properties[name].input === 'text'">
                           <input v-model="val.value" type="text" size="45" v-on:keyup="trigger_change_on_enter"/>
                        </template>
 
                        
-                       <template v-else-if="val && val.id && context[val.id] && context[val.id].name">
+                       <template v-else-if="val && val.id && context[val.id] && context[val.id].properties.name">
+                         
+                          <a v-bind:href="'#' + val.id">{{  context[val.id].properties.name[0].value }}</b></a>
                           <form v-on:submit.prevent="unlinkProp(val)" style='display:inline;'>                     
-                              <button type="submit">-</button>
+                              <button type="submit">unlink</button>
                           </form>
-                          <a v-bind:href="'#' + val.id">{{ context[val.id].name[0].value }}</b></a>
-                            
                         </template>
 
                        <b v-else>
@@ -65,13 +69,14 @@
                        
                        
                         <template v-if="schema.properties[name] && schema.properties[name].input === 'link' " >
-                            <form v-on:submit.prevent="linkProp(val)" style='display:inline;'>      
+                            
+                            <form v-on:submit.prevent="linkProp(val)" style='display:inline;'  v-if="!val.id">      
                                 <button type="submit"> -> </button>               
-                            <select  v-model="link_to_id" >
+                            <select  v-model="val.selection" >
                                 
                                 <template v-for="(link_item, link_id) in context">
-                                  <template v-for="other_name in link_item.name">  
-                                    <option v-if="(!val.id || link_id != val.id) && schema.properties[name] && link_item.type && schema.properties[name].type.includes(link_item.type[0].value)" v-bind:value="link_id">{{ other_name.value  }}</select>
+                                  <template v-for="other_name in link_item.properties.name">  
+                                    <option v-if="(!val.id || link_id != val.id) && schema.properties[name] && link_item.properties.type && schema.properties[name].type.includes(link_item.properties.type[0].value)" v-bind:value="link_id">{{ other_name.value  }}</select>
                                   </template>
                                 </template>
                                
@@ -80,6 +85,22 @@
                               
                             </form>
                             
+                            <form v-on:submit.prevent="linkToNew(val)" style='display:inline;'  v-if="!val.id && schema.properties[name].type.filter(type => {return (schema.types[type] && schema.types[type].create)}).length > 0">      
+                                ... or ...
+                                <button type="submit" text="Link to a new contextual item"> -> New -> </button>               
+                                <select  v-model="val.new_type" >
+                                 <template v-for="type in schema.properties[name].type.filter(type => {return (schema.types[type] && schema.types[type].create)})">  
+
+                                    <option> {{ type  }} </option>
+        
+                                  </template>
+                              
+                               
+                                </select>
+                              </select>
+                              
+                            </form>
+                            
                           </template>
                          
                           
@@ -95,7 +116,7 @@
           <form v-on:submit.prevent="addContextProp(item)">
               <select  v-model="prop_name"  >
                 <template v-for="(prop, prop_name) in schema.properties">
-                  <option v-if="!item[prop_name]">{{prop_name}}</option>
+                  <option v-if="!item.properties[prop_name]">{{prop_name}}</option>
                 </template>
                  
                 </select>
@@ -104,18 +125,18 @@
           <br><br>
         </template>
             
-        
+       
         <form v-on:submit.prevent="addContext(context)">
             <select  v-model="type" >
-               <option v-for="(type, type_name) in schema.types" v-bind:value="type_name"> {{ type_name }} </option>
+               <option v-for="type_name in Object.getOwnPropertyNames(schema.types).filter(type => {return schema.types[type].create})" v-bind:value="type_name"> {{ type_name }} </option>
               </select>
             <button type="submit">Add contextual item</button>
         </form>
-
+      
       </template>
          
       <h1>
-        <a v-bind:href="'/?path=' + path + '&dataset_path=' +  path">
+        <a v-bind:href="'/?path=' + path + '&dataset_path=' +  dataset_path">
         <template v-if="files.existing_describo">
             Edit existing data package
         </template>
@@ -128,7 +149,7 @@
        <div v-for="(file, path) in files.files">
          <template v-if="!(context[file.id])">
          <h1>{{ file.filename }}</h1>
-         <img width="50%" v-bind:src='file.img'>
+         <img width="50%" v-bind:src='file._img'>
          <form v-if="context" v-on:submit.prevent="addFile(file)">
             <button type="submit">Add file to package</button>
         </form>
@@ -138,7 +159,7 @@
       
       <div id="filelist" border="1">
           <ul v-for="dir in files.dirs">
-            <li><a v-bind:href="'?path=' + dir.absolute">{{ dir.name }}</a></li>
+            <li><a v-bind:href="'?path=' + dir.absolute + '&dataset_path=' +  dataset_path">{{ dir.name }}</a></li>
           </ul>
       </div>
 
@@ -184,7 +205,7 @@
           prop: {"name":"nom", "value": "val"},
           type: "Person",
           prop_name: "",
-          link_to_id: "",
+          link_to_id: {},
           link_id: "",
           changed: 0,
           latest_id: 0,
@@ -205,19 +226,19 @@
             "addContextProp": function addContextProp(item){
               // Warning! Repetition!
               var prop_name = this.prop_name;
-              if (item[prop_name]) {
-                   item[prop_name].push({"value": "", "id": ""});
+              if (item.properties[prop_name]) {
+                   item.properties[prop_name].push({"value": "", "id": ""});
                } else {
-                  item[prop_name] = [{"value": "", "id": ""}];
+                  item.properties[prop_name] = [{"value": "", "id": ""}];
               }
               this.changed += 1;                
               this.save_context();         
             },
             "addProp": function addProp(item, prop_name){
-              if (item[prop_name]) {
-                   item[prop_name].push({"value": "", "id": ""});
+              if (item.properties[prop_name]) {
+                   item.properties[prop_name].push({"value": "", "id": ""});
                } else {
-                  item[prop_name] = [{"value": "", "id": ""}];
+                  item.properties[prop_name] = [{"value": "", "id": ""}];
               }
               this.changed += 1;                
               this.save_context();
@@ -236,23 +257,23 @@
               } 
               
             },
-            "linkProp": function linkProp(val) {   
-               val.value = context[this.link_to_id].name[0].value
-               val.id = this.link_to_id
+            "linkProp": function linkProp(val) { 
+               val.value = context[val.selection].properties.name[0].value;
+               val.id = val.selection;
                this.changed += 1;  
                this.save_context();     
             },
             "unlinkProp": function unlinkProp(val) {   
-               val.value = ""
-               val.id = ""
+               val.value = "";
+               val.id = "";
                this.changed += 1;  
                this.save_context();     
             },
             "addFile": function addFile(file) {
               if (!context[file.id]) {
                 var new_item = JSON.parse(JSON.stringify(schema.types.File.template));
-                new_item.name[0].value = file.id;
-                new_item.img = file.img;
+                new_item.properties.name[0].value = file.id;
+                new_item._img = file._img;
                 context[file.id] = new_item;
 
                 this.changed += 1;  
@@ -271,15 +292,23 @@
               this.save_context();
               
             },
+            "linkToNew" : function linkToNew(val) {
+                alert(val.new_type);
+                this.type = val.new_type;
+                val.id = this.addContext(context);
+                this.save_context();
+
+              },
             "addContext": function addContext(context){
               while (context[this.latest_id.toString()]) {
                 this.latest_id += 1;
               }
               var id = this.latest_id.toString();
               context[id] = JSON.parse(JSON.stringify(schema.types[this.type].template));
-              context[id].name=[{"value": this.id}]
+              context[id].properties.name=[{"value": this.type + "(" + id + ")"}]
               this.changed += 1;
               this.save_context();
+              return(id);
 
             }
           }
-- 
GitLab