Bläddra i källkod

wip, symmetry annotation, graphql schema codegen

Alexander Rose 6 år sedan
förälder
incheckning
8802f518d7

+ 18 - 0
data/rcsb-graphql/codegen.js

@@ -0,0 +1,18 @@
+const { generate } = require('graphql-code-generator')
+const path = require('path')
+
+const basePath = path.join(__dirname, '..', '..', 'src', 'servers', 'model', 'properties', 'rcsb', 'graphql')
+
+generate({
+    args: [
+        path.join(basePath, 'symmetry.gql.ts')
+    ],
+    schema: 'http://rest-experimental.rcsb.org/graphql',
+    template: 'typescript',
+    out: path.join(basePath),
+    skipSchema: true,
+    overwrite: true,
+    config: path.join(__dirname, 'codegen.json')
+}, true).then(
+    console.log('done')
+).catch(e => console.error(e))

+ 8 - 0
data/rcsb-graphql/codegen.json

@@ -0,0 +1,8 @@
+{
+    "flattenTypes": false,
+    "generatorConfig": {
+        "printTime": true,
+        "immutableTypes": true,
+        "resolvers": false
+    }
+}

+ 520 - 0
package-lock.json

@@ -30,6 +30,21 @@
       "integrity": "sha512-hY3S3Ow7k9ieBC3uEaiAluDrnPnAYsHZckOHr+nMWTgdFXa4Q6BqH5512y8mARLT8jExHhrpAiJoBdInCGd2PA==",
       "dev": true
     },
+    "@types/babel-types": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.4.tgz",
+      "integrity": "sha512-WiZhq3SVJHFRgRYLXvpf65XnV6ipVHhnNaNvE8yCimejrGglkg38kEj0JcizqwSHxmPSjcTlig/6JouxLGEhGw==",
+      "dev": true
+    },
+    "@types/babylon": {
+      "version": "6.16.3",
+      "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.3.tgz",
+      "integrity": "sha512-lyJ8sW1PbY3uwuvpOBZ9zMYKshMnQpXmeDHh8dj9j2nJm/xrW0FgB5gLSYOArj5X0IfaXnmhFoJnhS4KbqIMug==",
+      "dev": true,
+      "requires": {
+        "@types/babel-types": "*"
+      }
+    },
     "@types/benchmark": {
       "version": "1.0.31",
       "resolved": "https://registry.npmjs.org/@types/benchmark/-/benchmark-1.0.31.tgz",
@@ -92,6 +107,24 @@
         "@types/range-parser": "*"
       }
     },
+    "@types/graphql": {
+      "version": "0.12.6",
+      "resolved": "https://registry.npmjs.org/@types/graphql/-/graphql-0.12.6.tgz",
+      "integrity": "sha512-wXAVyLfkG1UMkKOdMijVWFky39+OD/41KftzqfX1Oejd0Gm6dOIKjCihSVECg6X7PHjftxXmfOKA/d1H79ZfvQ==",
+      "dev": true
+    },
+    "@types/handlebars": {
+      "version": "4.0.39",
+      "resolved": "https://registry.npmjs.org/@types/handlebars/-/handlebars-4.0.39.tgz",
+      "integrity": "sha512-vjaS7Q0dVqFp85QhyPSZqDKnTTCemcSHNHFvDdalO1s0Ifz5KuE64jQD5xoUkfdWwF4WpqdJEl7LsWH8rzhKJA==",
+      "dev": true
+    },
+    "@types/is-glob": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@types/is-glob/-/is-glob-4.0.0.tgz",
+      "integrity": "sha512-zC/2EmD8scdsGIeE+Xg7kP7oi9VP90zgMQtm9Cr25av4V+a+k8slQyiT60qSw8KORYrOKlPXfHwoa1bQbRzskQ==",
+      "dev": true
+    },
     "@types/jest": {
       "version": "23.3.1",
       "resolved": "https://registry.npmjs.org/@types/jest/-/jest-23.3.1.tgz",
@@ -119,6 +152,12 @@
         "@types/node": "*"
       }
     },
+    "@types/prettier": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-1.13.2.tgz",
+      "integrity": "sha512-k6MCN8WuDiCj6O+UJsVMbrreZxkbrhQbO02oDj6yuRu8UAkp0MDdEcDKif8/gBKuJbT84kkO+VHQAqXkumEklg==",
+      "dev": true
+    },
     "@types/range-parser": {
       "version": "1.2.2",
       "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.2.tgz",
@@ -154,6 +193,12 @@
         "@types/mime": "*"
       }
     },
+    "@types/valid-url": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@types/valid-url/-/valid-url-1.0.2.tgz",
+      "integrity": "sha1-YPpDXOJL/VuhB7jSqAeWrq86j0U=",
+      "dev": true
+    },
     "@webassemblyjs/ast": {
       "version": "1.5.13",
       "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.5.13.tgz",
@@ -648,6 +693,26 @@
         "normalize-path": "^2.0.0"
       }
     },
+    "apollo-link": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.2.tgz",
+      "integrity": "sha512-Uk/BC09dm61DZRDSu52nGq0nFhq7mcBPTjy5EEH1eunJndtCaNXQhQz/BjkI2NdrfGI+B+i5he6YSoRBhYizdw==",
+      "dev": true,
+      "requires": {
+        "@types/graphql": "0.12.6",
+        "apollo-utilities": "^1.0.0",
+        "zen-observable-ts": "^0.8.9"
+      }
+    },
+    "apollo-utilities": {
+      "version": "1.0.17",
+      "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.0.17.tgz",
+      "integrity": "sha512-Mm8pS48QVV/CUbcUFN5TmbkdjIJtYw99qVqbBHnIwL8x0H2RW0NpzASpC+5+qSxD1sKNLUl9pA6F5Et/0bbaVg==",
+      "dev": true,
+      "requires": {
+        "fast-json-stable-stringify": "^2.0.0"
+      }
+    },
     "append-transform": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz",
@@ -1489,6 +1554,16 @@
       "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
       "dev": true
     },
+    "camel-case": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
+      "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
+      "dev": true,
+      "requires": {
+        "no-case": "^2.2.0",
+        "upper-case": "^1.1.1"
+      }
+    },
     "camelcase": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
@@ -1567,6 +1642,32 @@
         }
       }
     },
+    "change-case": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/change-case/-/change-case-3.0.2.tgz",
+      "integrity": "sha512-Mww+SLF6MZ0U6kdg11algyKd5BARbyM4TbFBepwowYSR5ClfQGCGtxNXgykpN0uF/bstWeaGDT4JWaDh8zWAHA==",
+      "dev": true,
+      "requires": {
+        "camel-case": "^3.0.0",
+        "constant-case": "^2.0.0",
+        "dot-case": "^2.1.0",
+        "header-case": "^1.0.0",
+        "is-lower-case": "^1.1.0",
+        "is-upper-case": "^1.1.0",
+        "lower-case": "^1.1.1",
+        "lower-case-first": "^1.0.0",
+        "no-case": "^2.3.2",
+        "param-case": "^2.1.0",
+        "pascal-case": "^2.0.0",
+        "path-case": "^2.1.0",
+        "sentence-case": "^2.1.0",
+        "snake-case": "^2.1.0",
+        "swap-case": "^1.1.0",
+        "title-case": "^2.1.0",
+        "upper-case": "^1.1.1",
+        "upper-case-first": "^1.1.0"
+      }
+    },
     "chardet": {
       "version": "0.5.0",
       "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.5.0.tgz",
@@ -1752,6 +1853,12 @@
       "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
       "dev": true
     },
+    "colors": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
+      "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=",
+      "dev": true
+    },
     "combined-stream": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
@@ -1767,6 +1874,12 @@
       "integrity": "sha512-sVXqklSaotK9at437sFlFpyOcJonxe0yST/AG9DkQKUdIE6IqGIMv4SfAQSKaJbSdVEJYItASCrBiVQHq1HQew==",
       "dev": true
     },
+    "common-tags": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz",
+      "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==",
+      "dev": true
+    },
     "commondir": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
@@ -1854,6 +1967,16 @@
       "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
       "dev": true
     },
+    "constant-case": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-2.0.0.tgz",
+      "integrity": "sha1-QXV2TTidP6nI7NKRhu1gBSQ7akY=",
+      "dev": true,
+      "requires": {
+        "snake-case": "^2.1.0",
+        "upper-case": "^1.1.1"
+      }
+    },
     "constants-browserify": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
@@ -2036,6 +2159,27 @@
         "sha.js": "^2.4.8"
       }
     },
+    "cross-fetch": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-2.0.0.tgz",
+      "integrity": "sha512-gnx0GnDyW73iDq6DpqceL8i4GGn55PPKDzNwZkopJ3mKPcfJ0BUIXBsnYfJBVw+jFDB+hzIp2ELNRdqoxN6M3w==",
+      "requires": {
+        "node-fetch": "2.0.0",
+        "whatwg-fetch": "2.0.3"
+      },
+      "dependencies": {
+        "node-fetch": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.0.0.tgz",
+          "integrity": "sha1-mCu6Q+zU8pIqKcwYamu7C7c/y6Y="
+        },
+        "whatwg-fetch": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz",
+          "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ="
+        }
+      }
+    },
     "cross-spawn": {
       "version": "5.1.0",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
@@ -2156,6 +2300,12 @@
         "array-find-index": "^1.0.1"
       }
     },
+    "cycle": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz",
+      "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=",
+      "dev": true
+    },
     "cyclist": {
       "version": "0.2.2",
       "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
@@ -2328,6 +2478,12 @@
       "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
       "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
     },
+    "deprecated-decorator": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz",
+      "integrity": "sha1-AJZjF7ehL+kvPMgx91g68ym4bDc=",
+      "dev": true
+    },
     "des.js": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
@@ -2390,6 +2546,15 @@
         "webidl-conversions": "^4.0.2"
       }
     },
+    "dot-case": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-2.1.1.tgz",
+      "integrity": "sha1-NNzzf1Co6TwrO8qLt/uRVcfaO+4=",
+      "dev": true,
+      "requires": {
+        "no-case": "^2.2.0"
+      }
+    },
     "duplexer": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
@@ -2875,6 +3040,12 @@
       "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
       "dev": true
     },
+    "eyes": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
+      "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=",
+      "dev": true
+    },
     "falafel": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.1.0.tgz",
@@ -4121,6 +4292,137 @@
       "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
       "dev": true
     },
+    "graphql": {
+      "version": "0.13.2",
+      "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.13.2.tgz",
+      "integrity": "sha512-QZ5BL8ZO/B20VA8APauGBg3GyEgZ19eduvpLWoq5x7gMmWnHoy8rlQWPLmWgFvo1yNgjSEFMesmS4R6pPr7xog==",
+      "requires": {
+        "iterall": "^1.2.1"
+      }
+    },
+    "graphql-code-generator": {
+      "version": "0.10.5",
+      "resolved": "https://registry.npmjs.org/graphql-code-generator/-/graphql-code-generator-0.10.5.tgz",
+      "integrity": "sha1-UoW5/3OTsMldVl6ymSgpFYp/r5g=",
+      "dev": true,
+      "requires": {
+        "@types/babylon": "6.16.3",
+        "@types/is-glob": "4.0.0",
+        "@types/prettier": "1.13.2",
+        "@types/valid-url": "1.0.2",
+        "babel-types": "7.0.0-beta.3",
+        "babylon": "7.0.0-beta.47",
+        "commander": "2.16.0",
+        "glob": "7.1.2",
+        "graphql-codegen-compiler": "0.10.5",
+        "graphql-codegen-core": "0.10.5",
+        "is-glob": "4.0.0",
+        "is-valid-path": "0.1.1",
+        "mkdirp": "0.5.1",
+        "prettier": "1.14.0",
+        "request": "2.87.0",
+        "valid-url": "1.0.9"
+      },
+      "dependencies": {
+        "babel-types": {
+          "version": "7.0.0-beta.3",
+          "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-7.0.0-beta.3.tgz",
+          "integrity": "sha512-36k8J+byAe181OmCMawGhw+DtKO7AwexPVtsPXoMfAkjtZgoCX3bEuHWfdE5sYxRM8dojvtG/+O08M0Z/YDC6w==",
+          "dev": true,
+          "requires": {
+            "esutils": "^2.0.2",
+            "lodash": "^4.2.0",
+            "to-fast-properties": "^2.0.0"
+          }
+        },
+        "babylon": {
+          "version": "7.0.0-beta.47",
+          "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.47.tgz",
+          "integrity": "sha512-+rq2cr4GDhtToEzKFD6KZZMDBXhjFAr9JjPw9pAppZACeEWqNM294j+NdBzkSHYXwzzBmVjZ3nEVJlOhbR2gOQ==",
+          "dev": true
+        },
+        "is-extglob": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+          "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+          "dev": true
+        },
+        "is-glob": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz",
+          "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=",
+          "dev": true,
+          "requires": {
+            "is-extglob": "^2.1.1"
+          }
+        },
+        "to-fast-properties": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+          "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+          "dev": true
+        }
+      }
+    },
+    "graphql-codegen-compiler": {
+      "version": "0.10.5",
+      "resolved": "https://registry.npmjs.org/graphql-codegen-compiler/-/graphql-codegen-compiler-0.10.5.tgz",
+      "integrity": "sha1-oGXFVSNuY9hMXY72HXg3bK1cyjQ=",
+      "dev": true,
+      "requires": {
+        "@types/handlebars": "4.0.39",
+        "change-case": "3.0.2",
+        "common-tags": "1.8.0",
+        "graphql-codegen-core": "0.10.5",
+        "handlebars": "4.0.11",
+        "moment": "2.22.2"
+      }
+    },
+    "graphql-codegen-core": {
+      "version": "0.10.5",
+      "resolved": "https://registry.npmjs.org/graphql-codegen-core/-/graphql-codegen-core-0.10.5.tgz",
+      "integrity": "sha1-YVzAFosOL8nbLxPkJEaZbHLEttU=",
+      "dev": true,
+      "requires": {
+        "graphql-tag": "2.9.2",
+        "graphql-tools": "3.0.5",
+        "winston": "2.4.3"
+      }
+    },
+    "graphql-codegen-typescript-template": {
+      "version": "0.10.5",
+      "resolved": "https://registry.npmjs.org/graphql-codegen-typescript-template/-/graphql-codegen-typescript-template-0.10.5.tgz",
+      "integrity": "sha1-grwJgm4RCVSPDZ/ewPbE9ADK5Qg=",
+      "dev": true
+    },
+    "graphql-request": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-1.8.0.tgz",
+      "integrity": "sha512-kX4u/rPNd8EkYdYXDzAiGyVrLBZmdZREOlQVemcUkXtNVaNTh6eXC0yhmRypIB070/YtRl1w01xR1efOrHO+JA==",
+      "requires": {
+        "cross-fetch": "2.0.0",
+        "graphql": "^0.13.2"
+      }
+    },
+    "graphql-tag": {
+      "version": "2.9.2",
+      "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.9.2.tgz",
+      "integrity": "sha512-qnNmof9pAqj/LUzs3lStP0Gw1qhdVCUS7Ab7+SUB6KD5aX1uqxWQRwMnOGTkhKuLvLNIs1TvNz+iS9kUGl1MhA==",
+      "dev": true
+    },
+    "graphql-tools": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/graphql-tools/-/graphql-tools-3.0.5.tgz",
+      "integrity": "sha512-MawfVPwaqy+L48IiP4QXHpFFOgCH+vWmB9oeU70lckac22nOpDLwbkKtddtdodoTHV54EKbkTpPdW6u6bcjjFA==",
+      "dev": true,
+      "requires": {
+        "apollo-link": "1.2.2",
+        "apollo-utilities": "^1.0.1",
+        "deprecated-decorator": "^0.1.6",
+        "iterall": "^1.1.3",
+        "uuid": "^3.1.0"
+      }
+    },
     "growly": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
@@ -4322,6 +4624,16 @@
         "minimalistic-assert": "^1.0.1"
       }
     },
+    "header-case": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/header-case/-/header-case-1.0.1.tgz",
+      "integrity": "sha1-lTWXMZfBRLCWE81l0xfvGZY70C0=",
+      "dev": true,
+      "requires": {
+        "no-case": "^2.2.0",
+        "upper-case": "^1.1.3"
+      }
+    },
     "hmac-drbg": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@@ -4667,6 +4979,24 @@
         "is-extglob": "^1.0.0"
       }
     },
+    "is-invalid-path": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/is-invalid-path/-/is-invalid-path-0.1.0.tgz",
+      "integrity": "sha1-MHqFWzzxqTi0TqcNLGEQYFNxTzQ=",
+      "dev": true,
+      "requires": {
+        "is-glob": "^2.0.0"
+      }
+    },
+    "is-lower-case": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz",
+      "integrity": "sha1-fhR75HaNxGbbO/shzGCzHmrWk5M=",
+      "dev": true,
+      "requires": {
+        "lower-case": "^1.1.0"
+      }
+    },
     "is-number": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
@@ -4737,12 +5067,30 @@
       "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
       "dev": true
     },
+    "is-upper-case": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz",
+      "integrity": "sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8=",
+      "dev": true,
+      "requires": {
+        "upper-case": "^1.1.0"
+      }
+    },
     "is-utf8": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
       "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
       "dev": true
     },
+    "is-valid-path": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/is-valid-path/-/is-valid-path-0.1.1.tgz",
+      "integrity": "sha1-EQ+f90w39mPh7HkV60UfLbk6yd8=",
+      "dev": true,
+      "requires": {
+        "is-invalid-path": "^0.1.0"
+      }
+    },
     "is-windows": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
@@ -4908,6 +5256,11 @@
         "handlebars": "^4.0.3"
       }
     },
+    "iterall": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.2.2.tgz",
+      "integrity": "sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA=="
+    },
     "jest": {
       "version": "23.4.2",
       "resolved": "https://registry.npmjs.org/jest/-/jest-23.4.2.tgz",
@@ -5741,6 +6094,21 @@
         "signal-exit": "^3.0.0"
       }
     },
+    "lower-case": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
+      "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=",
+      "dev": true
+    },
+    "lower-case-first": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz",
+      "integrity": "sha1-5dp8JvKacHO+AtUrrJmA5ZIq36E=",
+      "dev": true,
+      "requires": {
+        "lower-case": "^1.1.2"
+      }
+    },
     "lru-cache": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
@@ -6074,6 +6442,12 @@
         "minimist": "0.0.8"
       }
     },
+    "moment": {
+      "version": "2.22.2",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz",
+      "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=",
+      "dev": true
+    },
     "move-concurrently": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
@@ -6173,6 +6547,15 @@
       "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==",
       "dev": true
     },
+    "no-case": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
+      "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==",
+      "dev": true,
+      "requires": {
+        "lower-case": "^1.1.1"
+      }
+    },
     "node-fetch": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.2.0.tgz",
@@ -6876,6 +7259,15 @@
         "readable-stream": "^2.1.5"
       }
     },
+    "param-case": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
+      "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=",
+      "dev": true,
+      "requires": {
+        "no-case": "^2.2.0"
+      }
+    },
     "parse-asn1": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz",
@@ -6921,6 +7313,16 @@
       "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
       "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
     },
+    "pascal-case": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-2.0.1.tgz",
+      "integrity": "sha1-LVeNNFX2YNpl7KGO+VtODekSdh4=",
+      "dev": true,
+      "requires": {
+        "camel-case": "^3.0.0",
+        "upper-case-first": "^1.1.0"
+      }
+    },
     "pascalcase": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
@@ -6933,6 +7335,15 @@
       "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=",
       "dev": true
     },
+    "path-case": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/path-case/-/path-case-2.1.1.tgz",
+      "integrity": "sha1-lLgDfDctP+KQbkZbtF4l0ibo7qU=",
+      "dev": true,
+      "requires": {
+        "no-case": "^2.2.0"
+      }
+    },
     "path-dirname": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
@@ -7142,6 +7553,12 @@
       "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=",
       "dev": true
     },
+    "prettier": {
+      "version": "1.14.0",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.14.0.tgz",
+      "integrity": "sha512-KtQ2EGaUwf2EyDfp1fxyEb0PqGKakVm0WyXwDt6u+cAoxbO2Z2CwKvOe3+b4+F2IlO9lYHi1kqFuRM70ddBnow==",
+      "dev": true
+    },
     "pretty-format": {
       "version": "23.2.0",
       "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-23.2.0.tgz",
@@ -8399,6 +8816,16 @@
         "statuses": "~1.4.0"
       }
     },
+    "sentence-case": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-2.1.1.tgz",
+      "integrity": "sha1-H24t2jnBaL+S0T+G1KkYkz9mftQ=",
+      "dev": true,
+      "requires": {
+        "no-case": "^2.2.0",
+        "upper-case-first": "^1.1.2"
+      }
+    },
     "serialize-javascript": {
       "version": "1.5.0",
       "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz",
@@ -8547,6 +8974,15 @@
       "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
       "dev": true
     },
+    "snake-case": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz",
+      "integrity": "sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8=",
+      "dev": true,
+      "requires": {
+        "no-case": "^2.2.0"
+      }
+    },
     "snapdragon": {
       "version": "0.8.2",
       "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -8766,6 +9202,12 @@
         "safe-buffer": "^5.1.1"
       }
     },
+    "stack-trace": {
+      "version": "0.0.10",
+      "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+      "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=",
+      "dev": true
+    },
     "stack-utils": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.1.tgz",
@@ -9140,6 +9582,16 @@
         "has-flag": "^3.0.0"
       }
     },
+    "swap-case": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz",
+      "integrity": "sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM=",
+      "dev": true,
+      "requires": {
+        "lower-case": "^1.1.1",
+        "upper-case": "^1.1.1"
+      }
+    },
     "symbol-tree": {
       "version": "3.2.2",
       "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz",
@@ -9489,6 +9941,16 @@
         "setimmediate": "^1.0.4"
       }
     },
+    "title-case": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz",
+      "integrity": "sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o=",
+      "dev": true,
+      "requires": {
+        "no-case": "^2.2.0",
+        "upper-case": "^1.0.3"
+      }
+    },
     "tmp": {
       "version": "0.0.33",
       "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@@ -10028,6 +10490,21 @@
       "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==",
       "dev": true
     },
+    "upper-case": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
+      "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=",
+      "dev": true
+    },
+    "upper-case-first": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz",
+      "integrity": "sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU=",
+      "dev": true,
+      "requires": {
+        "upper-case": "^1.1.1"
+      }
+    },
     "uri-js": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz",
@@ -10125,6 +10602,12 @@
       "integrity": "sha512-qNdTUMaCjPs4eEnM3W9H94R3sU70YCuT+/ST7nUf+id1bVOrdjrpUaeZLqPBPRph3hsgn4a4BvwpxhHZx+oSDg==",
       "dev": true
     },
+    "valid-url": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz",
+      "integrity": "sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA=",
+      "dev": true
+    },
     "validate-npm-package-license": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz",
@@ -11154,6 +11637,28 @@
       "dev": true,
       "optional": true
     },
+    "winston": {
+      "version": "2.4.3",
+      "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.3.tgz",
+      "integrity": "sha512-GYKuysPz2pxYAVJD2NPsDLP5Z79SDEzPm9/j4tCjkF/n89iBNGBMJcR+dMUqxgPNgoSs6fVygPi+Vl2oxIpBuw==",
+      "dev": true,
+      "requires": {
+        "async": "~1.0.0",
+        "colors": "1.0.x",
+        "cycle": "1.0.x",
+        "eyes": "0.1.x",
+        "isstream": "0.1.x",
+        "stack-trace": "0.0.x"
+      },
+      "dependencies": {
+        "async": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz",
+          "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=",
+          "dev": true
+        }
+      }
+    },
     "wordwrap": {
       "version": "0.0.3",
       "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
@@ -11315,6 +11820,21 @@
           "dev": true
         }
       }
+    },
+    "zen-observable": {
+      "version": "0.8.9",
+      "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.9.tgz",
+      "integrity": "sha512-Y9kPzjGvIZ5jchSlqlCpBW3I82zBBL4z+ulXDRVA1NwsKzjt5kwAi+gOYIy0htNkfuehGZZtP5mRXHRV6TjDWw==",
+      "dev": true
+    },
+    "zen-observable-ts": {
+      "version": "0.8.9",
+      "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.9.tgz",
+      "integrity": "sha512-KJz2O8FxbAdAU5CSc8qZ1K2WYEJb1HxS6XDRF+hOJ1rOYcg6eTMmS9xYHCXzqZZzKw6BbXWyF4UpwSsBQnHJeA==",
+      "dev": true,
+      "requires": {
+        "zen-observable": "^0.8.0"
+      }
     }
   }
 }

+ 7 - 2
package.json

@@ -12,9 +12,9 @@
   },
   "scripts": {
     "lint": "tslint src/**/*.ts",
-    "build": "cpx \"src/**/*.{vert,frag,glsl,scss,woff,woff2,ttf,otf,eot,svg,html}\" build/node_modules/ && tsc",
+    "build": "cpx \"src/**/*.{vert,frag,glsl,scss,woff,woff2,ttf,otf,eot,svg,html,gql}\" build/node_modules/ && tsc",
     "watch": "tsc -watch",
-    "watch-extra": "cpx \"src/**/*.{vert,frag,glsl,scss,woff,woff2,ttf,otf,eot,svg,html}\" build/node_modules/ --watch",
+    "watch-extra": "cpx \"src/**/*.{vert,frag,glsl,scss,woff,woff2,ttf,otf,eot,svg,html,gql}\" build/node_modules/ --watch",
     "watch-all-win": "start cmd /K npm run watch & start cmd /K npm run watch-extra & start cmd /K npm run watch-viewer & start http-server -p 1338",
     "test": "jest",
     "build-viewer": "webpack build/node_modules/apps/viewer/index.js --mode development -o build/viewer/index.js",
@@ -84,6 +84,9 @@
     "file-loader": "^1.1.11",
     "glslify-import": "^3.1.0",
     "glslify-loader": "^1.0.2",
+    "graphql-code-generator": "^0.10.5",
+    "graphql-codegen-typescript-template": "^0.10.5",
+    "graphql-tag": "^2.9.2",
     "jest": "^23.4.2",
     "jest-raw-loader": "^1.0.1",
     "mini-css-extract-plugin": "^0.4.1",
@@ -104,6 +107,8 @@
     "argparse": "^1.0.10",
     "compression": "^1.7.3",
     "express": "^4.16.3",
+    "graphql": "^0.13.2",
+    "graphql-request": "^1.8.0",
     "immutable": "^4.0.0-rc.9",
     "node-fetch": "^2.2.0",
     "react": "^16.4.2",

+ 4 - 1
src/servers/model/properties.ts

@@ -2,15 +2,18 @@
  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
 import { Model } from 'mol-model/structure';
 import { StructureQualityReport } from './properties/structure-quality-report';
+import { SymmetryAnnotation } from './properties/rcsb/symmetry';
 
 export function attachModelProperties(model: Model): Promise<any>[] {
     // return a list of promises that start attaching the props in parallel
     // (if there are downloads etc.)
     return [
-        StructureQualityReport.attachFromPDBeApi(model)
+        StructureQualityReport.attachFromPDBeApi(model),
+        SymmetryAnnotation.attachFromRCSB(model)
     ];
 }

+ 28 - 0
src/servers/model/properties/rcsb/graphql/symmetry.gql.ts

@@ -0,0 +1,28 @@
+ // workaround so the query gets found by the codegen
+function gql (strs: TemplateStringsArray) { return strs.raw.join('') }
+
+export default
+gql`query RcsbSymmetry($pdbId: String) {
+    assemblies(pdbId: $pdbId) {
+        assembly_id
+        rcsb_annotation_symmetry {
+            source
+            symmetry_features {
+                type
+                clusters {
+                    avg_rmsd
+                    members
+                }
+                stoichiometry {
+                    description
+                    value
+                }
+                symmetry_axes {
+                    order
+                    start
+                    end
+                }
+            }
+        }
+    }
+}`

+ 197 - 0
src/servers/model/properties/rcsb/graphql/types.ts

@@ -0,0 +1,197 @@
+/* tslint:disable */
+/** Generated in 2018-08-03T15:19:31-07:00 */
+
+export enum PdbxLeavingAtomFlag {
+  N = "N",
+  Y = "Y"
+}
+
+export enum PdbxStereoConfig {
+  N = "N",
+  R = "R",
+  S = "S"
+}
+
+export enum ExperimentalSupport {
+  ASSAY_FOR_OLIGOMERIZATION = "ASSAY_FOR_OLIGOMERIZATION",
+  CROSS_LINKING = "CROSS_LINKING",
+  EQUILIBRIUM_CENTRIFUGATION = "EQUILIBRIUM_CENTRIFUGATION",
+  FLUORESCENCE_RESONANCE_ENERGY_TRANSFER = "FLUORESCENCE_RESONANCE_ENERGY_TRANSFER",
+  GEL_FILTRATION = "GEL_FILTRATION",
+  HOMOLOGY = "HOMOLOGY",
+  IMMUNOPRECIPITATION = "IMMUNOPRECIPITATION",
+  ISOTHERMAL_TITRATION_CALORIMETRY = "ISOTHERMAL_TITRATION_CALORIMETRY",
+  LIGHT_SCATTERING = "LIGHT_SCATTERING",
+  MASS_SPECTROMETRY = "MASS_SPECTROMETRY",
+  MICROSCOPY = "MICROSCOPY",
+  NATIVE_GEL_ELECTROPHORESIS = "NATIVE_GEL_ELECTROPHORESIS",
+  NONE = "NONE",
+  SAXS = "SAXS",
+  SCANNING_TRANSMISSION_ELECTRON_MICROSCOPY = "SCANNING_TRANSMISSION_ELECTRON_MICROSCOPY",
+  SURFACE_PLASMON_RESONANCE = "SURFACE_PLASMON_RESONANCE"
+}
+
+export enum Type {
+  GLOBAL = "GLOBAL",
+  LOCAL = "LOCAL",
+  PSEUDO = "PSEUDO"
+}
+
+export enum UnpublishedFlag {
+  N = "N",
+  Y = "Y"
+}
+
+export enum PdbxDiffrnProtocol {
+  LAUE = "LAUE",
+  MAD = "MAD",
+  SINGLE_WAVELENGTH = "SINGLE_WAVELENGTH"
+}
+
+export enum PdbxMonochromaticOrLaueML {
+  L = "L",
+  M = "M"
+}
+
+export enum PdbxScatteringType {
+  ELECTRON = "ELECTRON",
+  NEUTRON = "NEUTRON",
+  X_RAY = "X_RAY"
+}
+
+export enum Source {
+  ELECTRON_MICROSCOPE = "ELECTRON_MICROSCOPE",
+  FREE_ELECTRON_LASER = "FREE_ELECTRON_LASER",
+  LIQUID_ANODE = "LIQUID_ANODE",
+  NUCLEAR_REACTOR = "NUCLEAR_REACTOR",
+  ROTATING_ANODE = "ROTATING_ANODE",
+  SEALED_TUBE = "SEALED_TUBE",
+  SPALLATION_SOURCE = "SPALLATION_SOURCE",
+  SYNCHROTRON = "SYNCHROTRON"
+}
+
+export enum RefSpace {
+  REAL = "REAL",
+  RECIPROCAL = "RECIPROCAL"
+}
+
+export enum SymmetryType {
+  HELICAL = "HELICAL",
+  POINT = "POINT",
+  _2_D_CRYSTAL = "_2_D_CRYSTAL",
+  _3_D_CRYSTAL = "_3_D_CRYSTAL"
+}
+
+export enum AggregationState {
+  CELL = "CELL",
+  FILAMENT = "FILAMENT",
+  HELICAL_ARRAY = "HELICAL_ARRAY",
+  PARTICLE = "PARTICLE",
+  TISSUE = "TISSUE",
+  _2_D_ARRAY = "_2_D_ARRAY",
+  _3_D_ARRAY = "_3_D_ARRAY"
+}
+
+export enum ReconstructionMethod {
+  CRYSTALLOGRAPHY = "CRYSTALLOGRAPHY",
+  HELICAL = "HELICAL",
+  SINGLE_PARTICLE = "SINGLE_PARTICLE",
+  SUBTOMOGRAM_AVERAGING = "SUBTOMOGRAM_AVERAGING",
+  TOMOGRAPHY = "TOMOGRAPHY"
+}
+
+export enum EmbeddingApplied {
+  NO = "NO",
+  YES = "YES"
+}
+
+export enum StainingApplied {
+  NO = "NO",
+  YES = "YES"
+}
+
+export enum VitrificationApplied {
+  NO = "NO",
+  YES = "YES"
+}
+
+export enum SrcMethod {
+  MAN = "MAN",
+  NAT = "NAT",
+  SYN = "SYN"
+}
+
+export enum RcsbType {
+  DNA = "DNA",
+  HYBRID = "HYBRID",
+  OTHER = "OTHER",
+  POLYPEPTIDE = "POLYPEPTIDE",
+  RNA = "RNA"
+}
+
+export enum Level {
+  _100 = "_100",
+  _30 = "_30",
+  _40 = "_40",
+  _50 = "_50",
+  _60 = "_60",
+  _70 = "_70",
+  _80 = "_80",
+  _90 = "_90",
+  _95 = "_95"
+}
+
+export enum PdbFormatCompatible {
+  N = "N",
+  Y = "Y"
+}
+
+export namespace RcsbSymmetry {
+  export type Variables = {
+    readonly pdbId?: string | null;
+  };
+
+  export type Query = {
+    readonly __typename?: "Query";
+    readonly assemblies?: ReadonlyArray<Assemblies | null> | null;
+  };
+
+  export type Assemblies = {
+    readonly __typename?: "CoreAssembly";
+    readonly assembly_id?: number | null;
+    readonly rcsb_annotation_symmetry?: RcsbAnnotationSymmetry | null;
+  };
+
+  export type RcsbAnnotationSymmetry = {
+    readonly __typename?: "RcsbAnnotationSymmetry";
+    readonly source?: string | null;
+    readonly symmetry_features?: ReadonlyArray<SymmetryFeatures | null> | null;
+  };
+
+  export type SymmetryFeatures = {
+    readonly __typename?: "SymmetryFeature";
+    readonly type?: Type | null;
+    readonly clusters?: ReadonlyArray<Clusters | null> | null;
+    readonly stoichiometry?: Stoichiometry | null;
+    readonly symmetry_axes?: ReadonlyArray<SymmetryAxes | null> | null;
+  };
+
+  export type Clusters = {
+    readonly __typename?: "Cluster";
+    readonly avg_rmsd?: number | null;
+    readonly members?: ReadonlyArray<string | null> | null;
+  };
+
+  export type Stoichiometry = {
+    readonly __typename?: "Stoichiometry";
+    readonly description?: string | null;
+    readonly value?: ReadonlyArray<string | null> | null;
+  };
+
+  export type SymmetryAxes = {
+    readonly __typename?: "SymmetryAxis";
+    readonly order?: number | null;
+    readonly start?: ReadonlyArray<number | null> | null;
+    readonly end?: ReadonlyArray<number | null> | null;
+  };
+}

+ 150 - 0
src/servers/model/properties/rcsb/symmetry.ts

@@ -0,0 +1,150 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { GraphQLClient } from 'graphql-request'
+
+import { RcsbSymmetry } from './graphql/types';
+import query from './graphql/symmetry.gql';
+
+import { Model, ModelPropertyDescriptor } from 'mol-model/structure';
+import { CifWriter } from 'mol-io/writer/cif';
+import { Database as _Database, Column, Table } from 'mol-data/db'
+import { Category } from 'mol-io/writer/cif/encoder';
+import { Tensor } from 'mol-math/linear-algebra';
+import { CifExportContext } from 'mol-model/structure/export/mmcif';
+
+const { str, int, float, Aliased, Vector, List } = Column.Schema;
+
+function getInstance(name: keyof SymmetryAnnotation.Schema): (ctx: CifExportContext) => CifWriter.Category.Instance<any, any> {
+    return function(ctx: CifExportContext) {
+        const db = SymmetryAnnotation.get(ctx.model);
+        return db ? Category.ofTable(db[name]) : CifWriter.Category.Empty;
+    }
+}
+
+function getCategory(name: keyof SymmetryAnnotation.Schema) {
+    return { name, instance: getInstance(name) }
+}
+
+function createDatabase(assemblies: ReadonlyArray<RcsbSymmetry.Assemblies>): SymmetryAnnotation.Database {
+    const Schema = SymmetryAnnotation.Schema
+
+    const featureRows: Table.Row<typeof Schema.symmetry_annotation_feature>[] = []
+    const clusterRows: Table.Row<typeof Schema.symmetry_annotation_cluster>[] = []
+    const axisRows: Table.Row<typeof Schema.symmetry_annotation_axis>[] = []
+
+    let id = 0
+    for (let i = 0, il = assemblies.length; i < il; ++i) {
+        const a = assemblies[i]
+        const assembly_id = (a.assembly_id!).toString()
+        const source = a.rcsb_annotation_symmetry!.source!
+        const symmetry_features = a.rcsb_annotation_symmetry!.symmetry_features!
+        for (let j = 0, jl = symmetry_features.length; j < jl; ++j) {
+            const f = symmetry_features[j]!
+            featureRows.push({
+                id,
+                assembly_id,
+                source,
+                type: f.type!,
+                stoichiometry_value: (f.stoichiometry!.value!) as string[],
+                stoichiometry_description: f.stoichiometry!.description!
+            })
+
+            const clusters = f.clusters
+            if (clusters) {
+                for (let k = 0, kl = clusters.length; k < kl; ++k) {
+                    const c = clusters[k]!
+                    clusterRows.push({
+                        feature_id: id,
+                        avg_rmsd: c.avg_rmsd!,
+                        members: c.members as string[]
+                    })
+                }
+            }
+
+            const axes = f.symmetry_axes
+            if (axes) {
+                for (let k = 0, kl = axes.length; k < kl; ++k) {
+                    const a = axes[k]!
+                    axisRows.push({
+                        feature_id: id,
+                        order: a.order!,
+                        start: a.start as Tensor.Data,
+                        end: a.end as Tensor.Data
+                    })
+                }
+            }
+
+            id += 1
+        }
+    }
+
+    return _Database.ofTables('symmetry_annotation', Schema, {
+        symmetry_annotation_feature: Table.ofRows(Schema.symmetry_annotation_feature, featureRows),
+        symmetry_annotation_cluster: Table.ofRows(Schema.symmetry_annotation_cluster, clusterRows),
+        symmetry_annotation_axis: Table.ofRows(Schema.symmetry_annotation_axis, axisRows)
+    })
+}
+
+const _Descriptor: ModelPropertyDescriptor = {
+    isStatic: true,
+    name: 'symmetry_annotation',
+    cifExport: {
+        categories: [
+            getCategory('symmetry_annotation_feature'),
+            getCategory('symmetry_annotation_cluster'),
+            getCategory('symmetry_annotation_axis')
+        ]
+    }
+}
+
+const client = new GraphQLClient('http://rest-experimental.rcsb.org/graphql')
+
+export namespace SymmetryAnnotation {
+    export const Schema = {
+        symmetry_annotation_feature: {
+            id: int,
+            assembly_id: str,
+            source: str,
+            type: Aliased<'GLOBAL' | 'LOCAL' | 'PSEUDO'>(str),
+            stoichiometry_value: List(',', x => x),
+            stoichiometry_description: str
+        },
+        symmetry_annotation_cluster: {
+            feature_id: int,
+            avg_rmsd: float,
+            members: List(',', x => x)
+        },
+        symmetry_annotation_axis: {
+            feature_id: int,
+            order: int,
+            start: Vector(3),
+            end: Vector(3)
+        }
+    }
+    export type Schema = typeof Schema
+    export interface Database extends _Database<Schema> {}
+
+    export const Descriptor = _Descriptor;
+
+    export async function attachFromRCSB(model: Model) {
+        if (model.customProperties.has(Descriptor)) return true;
+
+        const variables: RcsbSymmetry.Variables = { pdbId: model.label.toLowerCase() };
+        const result = await client.request<RcsbSymmetry.Query>(query, variables);
+        if (!result || !result.assemblies) return false;
+
+        const db: Database = createDatabase(result.assemblies as ReadonlyArray<RcsbSymmetry.Assemblies>)
+        model.customProperties.add(Descriptor);
+        model._staticPropertyData.__SymmetryAnnotation__ = db;
+
+        return true;
+    }
+
+    export function get(model: Model): Database | undefined {
+        return model._staticPropertyData.__SymmetryAnnotation__;
+    }
+}

+ 15 - 2
src/servers/model/test.ts

@@ -1,5 +1,6 @@
 import { resolveJob } from './server/query';
 import * as fs from 'fs'
+import * as path from 'path'
 import { StructureCache } from './server/structure-wrapper';
 import { createJob } from './server/jobs';
 
@@ -33,13 +34,25 @@ function wrapFile(fn: string) {
     return w;
 }
 
+const basePath = path.join(__dirname, '..', '..', '..', '..')
+const examplesPath = path.join(basePath, 'examples')
+const outPath = path.join(basePath, 'build', 'test')
+if (!fs.existsSync(outPath)) fs.mkdirSync(outPath);
+
 async function run() {
     try {
-        const request = createJob('_local_', 'e:/test/quick/1cbs_updated.cif', 'residueInteraction', { label_comp_id: 'REA' });
+        // const request = createJob('_local_', 'e:/test/quick/1cbs_updated.cif', 'residueInteraction', { label_comp_id: 'REA' });
+        // const encoder = await resolveJob(request);
+        // const writer = wrapFile('e:/test/mol-star/1cbs_full.cif');
+        // const testFile = '1crn.cif'
+        const testFile = '1grm_updated.cif'
+        const request = createJob('_local_', path.join(examplesPath, testFile), 'full', {});
         const encoder = await resolveJob(request);
-        const writer = wrapFile('e:/test/mol-star/1cbs_full.cif');
+        const writer = wrapFile(path.join(outPath, testFile));
         encoder.writeTo(writer);
         writer.end();
+    } catch (e) {
+        console.error(e)
     } finally {
         StructureCache.expireAll();
     }