thepeergroup / aspen Goto Github PK
View Code? Open in Web Editor NEWAspen is a markup language for turning text into graph data (via Cypher for Neo4j).
Home Page: https://aspen-lang.org
License: MIT License
Aspen is a markup language for turning text into graph data (via Cypher for Neo4j).
Home Page: https://aspen-lang.org
License: MIT License
In this example _
represents a trailing space`
(Thing) [has relationship to]:_
- Something else
Expected
That this list transpiles into Aspen.
Actual
A lexing error.
It would be nice if the CLI generated a useful project file structure, and could generate one-off things.
$ aspen new project_name -d neo4j://neo4j:pass@localhost:7474
Generated:
----------
project_name -> Root folder
project_name/.aspen -> File indicating Aspen project
project_name/manifest.yml -> Metadata about included files
project_name/.env -> Env vars
project_name/config/db.yml -> Database configuration
project_name/src/ -> Source files
project_name/src/convert -> Converts non-Aspen to Aspen
project_name/src/grammars/ -> Aspen Grammar collection
project_name/src/prepare/ -> Folder for processing data
project_name/src/prepare/steps.yml -> Data processing step metadata
project_name/build/ -> Compilation is output here
project_name/.gitignore -> Ignoring .aspen, .env, build/
$ cd project_name
$ aspen generate org chart, roles
Generated:
----------
project_name/src/org_chart.aspen
project_name/src/roles.aspen
In this section, it's setting up a CSV (because it's better than Aspen for this particular need) and adding a script to convert the CSV to Aspen in the build process. It's also "attaching" a Google Sheet, and downloading it and converting it to Aspen before building the output; that way, data can be updated via Google Drive.
$ aspen generate --sheet people --type Person --columns Name:String:PK Email ReportsTo:Person
Generated:
----------
project_name/src/people.csv
with columns Name (String, Primary Key), Email (Email), ReportsTo (Person)
Registered:
----------
project_name/src/people.csv with project_name/src/convert
$ echo "API CREDENTIALS GO HERE" > config/google.yml
$ aspen attach --sheet projects --type Project --primary-key Title https://docs.google.com/document/d/GOOGLE-DOC-ID-HERE/edit#
Attached Google Sheet:
----------
Projects
https://docs.google.com/document/d/GOOGLE-DOC-ID-HERE
with columns Title (String, Primary Key), Description (Text), ProjectManager (Person), SubProjectOf (Project)
Registered:
----------
Projects (Google Sheet) with project_name/src/convert
In this section, it's setting up a grammar file. All grammar files are combined and added to the build.
$ aspen g grammar donations
Generated:
----------
project_name/src/grammars/donations.aspen
The validate command ensures that every file converts properly and is valid Aspen syntax.
$ aspen validate
Prevalidated:
-------------
✅ project_name/src/people.csv
✅ Projects (Google Sheet GOOGLE-DOC-ID-HERE...)
Converted:
----------
✅ project_name/src/people.aspen (from project_name/src/people.csv)
✅ project_name/src/projects.aspen (from Google Sheet GOOGLE-DOC-ID-HERE...)
Validated:
----------
✅ project_name/src/grammars/donations.aspen
✅ project_name/src/org_chart.aspen
✅ project_name/src/roles.aspen
✅ project_name/src/people.aspen
$ aspen watch
⚠️ WARNING: Aspen Watch is experimental, and saving a file/rebuilding the data
will cause all of the data in the linked database to be deleted and rewritten.
Use Ctrl+C to quit.
Watching all files in ./manifest.yml for changes.
Save a file to rebuild the data and push it to a Neo4j database at localhost:7474.
To rebuild the data after changes have been made to remote documents,
save manifest.yml.
File changed: project_name/manifest.yml
Checking for changes to remote data.
Validating files ✅
Building Aspen ✅
Building Cypher ✅
Pushing to database ✅
Done!
I'd like to add a command to start the sandbox Neo4j database and maybe guide Docker installation if Docker isn't yet installed.
$ aspen startdb
hi , i'm investigating using neo4j and activegraph (neo4jrb) in a rails app. I'd be interested in using aspen to seed my database and for test fixtures. Would it take much to adapt this project to conform to the ActiveGraph data? (id's specifically i think)
Hi
First off, sorry if this is a simple issue I can't solve because I am a Ruby noob! I just picked it up today morning.
I am trying to make Aspen work in Windows 10. I followed all your installation instructions in your README.md. Then I tried to compile a .aspen
and did this:
> cd D:\Misc_Projects\aspen #This is where I have the aspen directory which I got by forking and cloning
> bundle exec ./bin/aspen compile ./Example/example.aspen
in Powershell. This is what I got:
bundler: command not found: ./bin/aspen
Install missing gem executables with `bundle install`
I did do a bundle install
before running this command (as mentioned in the instructions). Anyways I tried it again, but it didn't help. In fact, I checked it with:
> bundle info aspen
and it did correctly show:
* aspen (0.1.0)
Summary: Aspen turns simple text into graph data, compiling it into Cypher for Neo4j graph databases.
Homepage: https://github.com/beechnut/aspen
Path: D:/Misc_Projects/aspen
(I am assuming this means bundle install
worked properly)
Then I read the ./bin/aspen
file and saw the shebang and it looks like it is trying to execute some command with the ruby interpreter. Since Windows doesn't understand the shebang I did this instead of the command in the instructions:
> bundle exec ruby ./bin/aspen compile ./Example/example.aspen
That gave me a "better" error (or is it?):
Traceback (most recent call last):
11: from ./bin/aspen:3:in `<main>'
10: from C:/Ruby26-x64/lib/ruby/gems/2.6.0/gems/dry-cli-0.6.0/lib/dry/cli.rb:67:in `call'
9: from C:/Ruby26-x64/lib/ruby/gems/2.6.0/gems/dry-cli-0.6.0/lib/dry/cli.rb:114:in `perform_registry'
8: from D:/Misc_Projects/aspen/lib/aspen/cli.rb:42:in `call'
7: from D:/Misc_Projects/aspen/lib/aspen.rb:27:in `compile_text'
6: from D:/Misc_Projects/aspen/lib/aspen.rb:27:in `new'
5: from C:/Ruby26-x64/lib/ruby/gems/2.6.0/gems/dry-container-0.7.2/lib/dry/container/mixin.rb:61:in `initialize'
4: from D:/Misc_Projects/aspen/lib/aspen/configuration.rb:14:in `initialize'
3: from D:/Misc_Projects/aspen/lib/aspen/configuration.rb:52:in `tag_lines'
2: from D:/Misc_Projects/aspen/lib/aspen/configuration.rb:52:in `each'
1: from D:/Misc_Projects/aspen/lib/aspen/configuration.rb:52:in `block in tag_lines'
D:/Misc_Projects/aspen/lib/aspen/configuration.rb:85:in `tag_line': There's no configuration option that matches the line: (Aspen::ConfigurationError)
(Matt) [is friends with] (Brianna).
Maybe it's a spelling error?
(The (Matt) [is friends with] (Brianna)
line comes from example.aspen
. See below.) I don't know what to do next.
This is the ruby version I have installed:
> ruby --version
ruby 2.6.6p146 (2020-03-31 revision 67876) [x64-mingw32]
If there is any more Ruby configuration information needed from my end, let me know. (You may have to tell me the commands to get that info too since, again, I am a Ruby noob!)
example.aspen
:I don't think there is anything wrong with the example.aspen
file. It is in my aspen directory in a folder called Example
. example.aspen
looks like this:
default Person, name
default_attribute Employer, company_name
reciprocal knows, is friends with
(Matt) [is friends with] (Brianna).
(Eliza) [knows] (Brianna).
(Matt) [is friends with] (Eliza).
(Matt) [works for] (Employer, UMass Boston).
which is basically the first example code on Aspen website.
I'd like to include the website for aspen-lang.org, which is already hosted on Github Pages, in a folder in this repository.
Part of the rationale is to make sure the website and documentation have parity by having website doctests.
Would it be valuable to have describe
blocks that describe an entity, and assign all relationships and attributes to them? I think it would.
I've also got an idea for slightly more Cypher-like syntax where if we add arrows/lines, we can generate multiple expressions in a single line, obviating the need for some grammars.
describe (Bilbo)
-[lives in]->(Place: Hobbiton)<-[contains]-(Place: Middle Earth)
-[enemies with]-(Gollum)
[cousins with] (Frodo)
[friends with] (Gandalf)
age: 111
If you're not in the root directory, CLI commands run into issues, like not being able to find config files.
/project_folder $ aspen push
-> OK
/project_folder $ cd src
/project_folder/src $ aspen push
-> Error
Instead, the CLI should work when it's anywhere inside an Aspen project.
Background
A person can know
another person, and a person can also know a skill, fact, or topic.
If a person knows someone, they probably know each other, so you could set reciprocal: knows
.
However, if a dataset says that someone knows
a person and also knows
a fact, then the dataset with the reciprocal setting will say that the fact knows the person! That's not right.
We should be able to scope relationships by setting what node types apply to a given rule.
Here are plain English descriptions and its Aspen discourse translation.
knows
relationships between people are reciprocal, but those between a person and anything else (e.g. a fact or skill) is directed.
reciprocal: knows (Person to Person)
knows
relationships are allowed between a Person node and any other node.
allow_only: knows (Person to *)
knows
relationships are allowed between a Person and a Person, or between a Person and a Skill.
allow_only: knows (Person to Person, Person to Skill)
"Not just for Neo4j anymore!"
Cypher was a major impetus for building Aspen...but it's not the only graph visualizer out there.
Getting the Aspen Editor off the ground, however, is taking a while. For the first public demos, I'd rather have everything be client-side rendered and not have to get posted to a server, then a somehow-multitenanted free Neo4j database, then returned, potentially with Cypher errors.
It would be much simpler to start if the Aspen Editor output graph data in the JSON-encoded graph format used by Sigma.js & G6, and/or GEXF. That way, we could just pipe the JSON output to Sigma or G6 instead of having to do all the Neo4j setup. Later, we can add Neo4j support.
Relatedly, #19 is asking for a feature that might be well-served by adding an ActiveGraph adapter (perhaps one that builds the code but lets the Rails app run it).
I'd like to add someplace—a queries.yml
in an appropriate location—that stores plain-language queries and their Cypher equivalents. The CLI could then be used to run them, either by number or by text. There's templating built into it, so you can write a query for a specific person, etc.
$ aspen queries:list
1) Who supervises the most people?
2) Who does { Person } supervise?
3) Order teams by psychological safety metrics.
$ aspen query 1
> MATCH ... # Cypher goes here
Hickory
$ aspen query Who does Holly supervise?
> MATCH ... # Cypher goes here
Magnolia
Cottonwood
Apple
The queries would be defined sort of like this, I imagine:
-
name: Who does { Person } supervise?
intake: Who does { Person a } supervise?
query: |
WITH (Person { name: {{{a}}} }) as supervisor ...
So, an idea I had is that Aspen should also be valid Markdown. That way, it can be rendered as Markdown/HTML, and it can be rendered as graph relationships.
What would it take to get there? The conventions of (nodes)
and [edges]
would have to change, for one, because those are links.
In a custom grammar, I want to write a few lines of Cypher which let me reference a nickname within the scope of this set of statements.
For example, I want this Aspen
default:
attributes:
Event: title
grammar:
-
match:
- (Person a) and (Person b) met through Graphs4Good.
- (Person a) met (Person b) at Graphs4Good.
template: |
{{{a}}}<-[:INCLUDES]-({{{intro:uniq}}}):Introduction)-[:INCLUDES]->{{{b}}}
({{{intro:uniq}}})-[:OCCURRED_AT]->(Event { title: "Graphs4Good Kickoff" })
{{{a}}}-[:KNOWS]-{{{b}}}
----
(Karin) [organized] (Event, Graphs4Good Kickoff)
Matt and Alyssa met through Graphs4Good.
Alyssa met Joel at Graphs4Good.
to include this Cypher:
MERGE (person_matt)<-[:INCLUDES]-(intro_1:Introduction)-[:INCLUDES]->(person_alyssa)
MERGE (intro_1)-[:OCCURRED_AT]->(:Event { title: "Graphs4Good Kickoff" })
MERGE (person_matt)-[:KNOWS]-(person_alyssa)
MERGE (person_alyssa)<-[:INCLUDES]-(intro_2:Introduction)-[:INCLUDES]->(person_joel)
MERGE (intro_2)-[:OCCURRED_AT]->(Event { title: "Graphs4Good Kickoff" })
MERGE (person_alyssa)-[:KNOWS]-(person_joel)
As you can see, the nickname identifier is unique to each evaluation of the template. We can reference the nickname as a string value anywhere in the template, taking care to wrap it in parens when we reference it as a node: ({{{intro:uniq}}})-[:AND_SO_FORTH]->
The convention could be an slug (here, intro
), followed by :uniq
to indicate it should autoincrement the nickname to preserve uniqueness.
Hi, just trying this out, following the quick start. Getting this error on aspen build
using the demo data:
undefined method `close' for nil:NilClass (NoMethodError)
no implicit conversion of Hash into String(TypeError)
/Users/shawngraham/.rbenv/versions/3.0.3/lib/ruby/gems/3.0.0/gems/aspen-cli-0.1.2/lib/aspen/conversion.rb:32:in `ensure in to_aspen': undefined method `close' for nil:NilClass (NoMethodError)
from /Users/shawngraham/.rbenv/versions/3.0.3/lib/ruby/gems/3.0.0/gems/aspen-cli-0.1.2/lib/aspen/conversion.rb:32:in `to_aspen'
from bin/convert:14:in `<main>'
/Users/shawngraham/.rbenv/versions/3.0.3/lib/ruby/3.0.0/csv.rb:1421:in `initialize': no implicit conversion of Hash into String (TypeError)
from /Users/shawngraham/.rbenv/versions/3.0.3/lib/ruby/3.0.0/csv.rb:1421:in `open'
from /Users/shawngraham/.rbenv/versions/3.0.3/lib/ruby/3.0.0/csv.rb:1421:in `open'
from /Users/shawngraham/.rbenv/versions/3.0.3/lib/ruby/gems/3.0.0/gems/aspen-cli-0.1.2/lib/aspen/conversion.rb:28:in `to_aspen'
from bin/convert:14:in `<main>'
/Users/shawngraham/.rbenv/versions/3.0.3/lib/ruby/gems/3.0.0/gems/aspen-cli-0.1.2/lib/aspen/cli/commands/build_steps.rb:105:in `call': `bin/convert` didn't work, stopping build. See above Traceback. (RuntimeError)
from /Users/shawngraham/.rbenv/versions/3.0.3/lib/ruby/gems/3.0.0/gems/aspen-cli-0.1.2/lib/aspen/cli/commands/build.rb:18:in `call'
from /Users/shawngraham/.rbenv/versions/3.0.3/lib/ruby/gems/3.0.0/gems/dry-cli-0.6.0/lib/dry/cli.rb:114:in `perform_registry'
from /Users/shawngraham/.rbenv/versions/3.0.3/lib/ruby/gems/3.0.0/gems/dry-cli-0.6.0/lib/dry/cli.rb:67:in `call'
from /Users/shawngraham/.rbenv/versions/3.0.3/lib/ruby/gems/3.0.0/gems/aspen-cli-0.1.2/bin/aspen:3:in `<top (required)>'
from /Users/shawngraham/.rbenv/versions/3.0.3/bin/aspen:25:in `load'
from /Users/shawngraham/.rbenv/versions/3.0.3/bin/aspen:25:in `<main>'
Thanks!
Working with free text, there are minimal constraints and a single typo could lead to unpleasant issues.
Let's let Aspen users add guardrails so they can only specify certain nodes and edges.
Given this discourse:
allow_only:
nodes: Person, Employer
edges: knows, works at
----
...
referencing any other nodes or edges in Aspen OR in Cypher templates should raise an error and prevent compilation.
Steps to reproduce:
default_attribute Person, name
match
(Person a) donated $(numeric dollar_amount) to (Person b).
to
{{{a}}}-[:GAVE_DONATION]->(:Donation { amount: {{{dollar_amount}}} })<-[:RECEIVED_DONATION]-{{{b}}}
end
----
Matt donated $20 to Hélène.
Sarah donated $30 to Sumbul.
bundle exec bin/aspen compile path/to/file.aspen
Expected
The file compiles.
Actual
I receive an error that complains that a line starting with default
was not set.
Commentary
You'll notice that there are no unlabeled nodes, so default
shouldn't be needed.
We do need that default_attribute
line there, because we need to tell Aspen that the attribute to assign the names to is name
.
Frankly, I'm not sure how to design this gem so its executable gets put in the global $PATH
, or without needing specific versions of Ruby. The goal is to have them download something and then from the command line, be able to, without bundle exec
or bin/aspen
, just write:
$ aspen some-command
What can we do to dramatically simplify installation and usage?
Hello,
Great work with Aspen, thank you very much!. I will start experimenting with it :)
I have a suggestion, which is to borrow the 'predicate lists' feature from the turtle syntax (https://www.w3.org/TR/turtle/#sec-intro)
This allows to not repeat the starting nodes each time, by using the ';' character
So if you have
default Person, name
default_attribute Employer, company_name
reciprocal knows, is friends with
# Write out the narrative data
(Matt) [is friends with] (Brianna).
(Eliza) [knows] (Brianna).
(Matt) [is friends with] (Eliza).
(Matt) [works for] (Employer, UMass Boston).
This can be prettified to:
default Person, name
default_attribute Employer, company_name
reciprocal knows, is friends with
# Write out the narrative data
(Matt) [is friends with] (Brianna) ;
[is friends with] (Eliza) ;
[works for] (Employer, UMass Boston) .
(Eliza) [knows] (Brianna).
Asking someone to compile a file over and over is...too much.
The CLI (Command Line Interface) should support two operations:
These commands might be available as:
$ aspen watch [file or directory] [-o --output file or directory to output to]
$ aspen watch [file or directory] [-d --database URL to Neo4j instance]
For database connections, if users want to iterate on their data, they may want to clear out the previous data so the database doesn't get cluttered with defunct entities. In order to populate the database idempotently without having to drop all the data every time, aspen watch
should add a special attribute to every node and edge that indicates the element was created in an aspen watch
session.
Whether data is linked to aspen watch
as a whole, or a specific aspen watch
session, that's up for discussion.
From Ansible:
Idempotency
An operation is idempotent if the result of performing it once is exactly the same as the result of performing it repeatedly without any intervening actions.
Aspen Watch sounds like a forest safety thing, so here's a pretty image of a fire lookout watchtower from Firewatch:
Original comment from https://community.neo4j.com/t/aspen-a-simple-language-for-generating-graph-data/15447/2
I attempted to use Aspen from the try.aspen-lang.org site and found 1 limitation: the markup language seems to choke on use of "_" or "-" in the node names. While this is not a big deal for "graph doodling", for some serious work (atleast for me) this would be a PITA to workaround each time. Any plans on allowing such characters in the name?
It looks like node content only allows alphanumerics. We should add support for other characters, though I'd be okay leaving out parentheses for now, just to keep the lexer & parser simple.
For a test, let's make sure this Aspen compiles (it currently doesn't):
(User: George_Lucas44) [directed] (Film: THX-1138).
(Ke$ha) [changed name to] (Name: Kesha).
From #12, there are comments in some of the examples, but Aspen doesn't technically support comments yet.
At minimum, add support for full-line comments.
Ideally, add inline comments as well.
This is my first time writing a language, and I write in a heavily object-oriented style, which isn't how I've heard languages are typically written.
There's some necessary complicated stuff in here, and then some less-necessary complicated stuff. A refactor would, just to clarify what's going on, would be smart.
This is a good opportunity for new contributors to read through the codebase. Get in touch if you want an explanation of anything, or if you want to team up on something.
P.S. I would have been intimidated even a few weeks ago to be contributing to a language. If you write Ruby, this should generally be approachable. We parse some text, convert that text into objects, and then do some things with those objects.
I've started building a large aspen file with 1000+ lines which results in a generated .cql with ~2000 lines. I'm using Neo4j Desktop so I paste the query in the .cql file into Neo4j browser to run. The query run fails with the following error:
There is not enough stack size to perform the current task. Increasing the JVM stack size works but then you hit the same error when the aspen size increases further.
Would it be possible to have a commandline switch to break up the generated cql query into chunks which can be batched ? I found some useful info here for batching queries:
https://medium.com/neo4j/5-tips-tricks-for-fast-batched-updates-of-graph-structures-with-neo4j-and-cypher-73c7f693c8cc
From the README:
One known issue is that
default
anddefault_attribute
auto-type the attributes they're given, and try to make them either a string or number. So, if you pass a Massachusetts zip code like02111
into a node in either short form or default attribute form, it will become integer2111
.As a Massachusetts resident, I vowed never to let this happen in my software, so I intend to address this soon.
The default
and default_attribute
lines should support (or perhaps require!) types.
default:
label: PostOffice
attribute: zipcode (string)
attributes:
Shipment: tracking_number (string)
I want to say that I work with Amber, Mica, and Moss—and that we all work with each other.
To type that out would take a lot of sentences, and even a lot of lists. Instead, I want to write something like this.
! all [works with]
- Matt
- Amber
- Mica
- Moss
This would create 4!
(that's 4 factorial
) works with
relationships, resulting in each node connected to the other three.
You can write a full form node like this, using double quotes,
(Person { name: "Matt", age: 31 })
but if you use single-quotes, everything will break.
(Person { name: 'Matt', age: 31 })
It feels wrong to not support single-quotes. Let's support single-quotes.
Task list:
generate narrative
generate grammar
generate sheet
- should also register a script in bin/convert
attach
- attach a Google Sheet tab for downloading and processing - should also register a script in bin/convert
Let's say we're modeling a network of consultants who work in state-based regulatory environments.
To indicate that our consultant Karl knows how to work in his three specialty states, we'd write something like:
default Person, name
default_attribute State, full_name
(Karl) [knows about] (State, Rhode Island).
(Karl) [knows about] (State, Massachusetts).
(Karl) [knows about] (State, Maine).
This is fine. We can do better.
Let's support list form.
This could come in a few different variants, and I'm undecided on which we should support, but I want Aspen to support at least one bullet form. I'm thinking A and C; or A, C, and D and in D we could support different labels. Option B feels clunky to me.
A.
(Karl) [knows about] (States: Rhode Island, Massachusetts, and Maine).
B.
(Karl) [knows about] (States:
- Rhode Island
- Massachusetts
- Maine
)
C.
(Karl) [knows about] (States):
- Rhode Island
- Massachusetts
- Maine
D.
(Karl) [knows about]:
- Rhode Island (State)
- Massachusetts (State)
- Maine (State)
The tutorial in the README was written quickly, by the author of the language. It could use a review
Does it make sense? What questions do you have while you're reading that, and how could they be addressed better in the tutorial?
This is important: If something doesn't make sense, it's not you, it's the tutorial. I want anyone from any background to get what they need to understand exactly what Aspen does and why, and how they could use it.
Also, should we leave usage instructions in the README and move the tutorial to the Wiki or the Github Pages site?
From the README, in the section on custom grammars:
String matchers will match anything in double-quotes. (Please don't use single quotes, as Aspen doesn't support them yet.)
At the moment, if you have a string matcher like
Matt works as a (string job_position) at UMass Boston.
then make sure to write the job_position in quotes, like
Matt works as a "research assistant" at UMass Boston.
If you don't, it won't match.
This will be addressed in a future update, because the quotes read as sarcastic. 😉
Let's get rid of needing those quotes. We could always make them optional, but I don't see a clear need for them at all.
I have been looking for many years for an application that would combine unstructured writing (complete with bi-directional links and transclusion, etc.) with more precise property relationships. While there has been an explosion of note/tools for thought apps that support unstructured text and graph-like relationship between blocks of text, none so far allow concrete definition of entities along with explicit relationships to other entities. As a fairly nontechnical person, I assume this is because it is a very difficult thing to do. However, I was wondering if aspen's approach might be a way of bringing this to tools like obsidian which are markdown based. I actually discovered aspen through obsidian community.
Okay, first things first, the compiler really wants Formatter objects instead of this string handling here.
Second and most importantly, we need to figure out how to support custom statements for various output formats.
Parse the Cypher and do something with it
Parse the Cypher (maybe with libcypher-parser) and render JSON or GEXF with the output. Use FFI to get the output into Ruby. May be difficult to implement, but easier on the authors.
Custom formatter per language
Custom formatter is scoped to a specific output format, so you write a template and then outputs for JSON, GEXF, and/or Cypher, whichever you're using. Maybe this is what we start with for now; I'd like to get Neo4j out of the Aspen Editor stack. Relatively simple to implement but requires more writing on the authors' part, which is antithetical to the goals of Aspen.
Introduce an abstraction
Have an abstracted graph templating language that looks like Cypher but isn't, perhaps a subset. This would require long-term support, and be quite complex, and would introduce quite a few limitations because it can't support full Cypher.
Curious what the status is on the notebook project that is listed on the Aspen site and/or if you have any documentation around the direction you want to take it.
My interest is coming from the perspective of personal knowledge management tools like LogSeq, AthensResearch and Obsidian. Those lack any notion of named relationships in their graph and are overly-cumbersome when it comes to querying the knowledge you've stored.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.