add CTA people mapping from murmurations

This commit is contained in:
Darren Zal 2023-03-06 19:44:39 -08:00
parent df4707ff7b
commit cc1c044dcf
28 changed files with 1819 additions and 16 deletions

View File

@ -1,16 +0,0 @@
from terminusdb_client import WOQLClient
import json
# we keep all the information in dictionaries with Employee id as keys
orgs = {}
client = WOQLClient("https://cloud.terminusdb.com/Myseelia/")
client.connect(db="playground", team="Myseelia", use_token=True)
with open("../src/components/explore/knowledge_graph.json") as json_file:
data = json.load(json_file)
entities = data.get("entities")
for entity in entities:
if entity.get("type") == "organization":
id = entity.get("id")
client.deleteDocument(id);

View File

@ -0,0 +1 @@
{"branch": "main", "ref": null}

Binary file not shown.

View File

@ -0,0 +1,6 @@
{
"database": "murmurations",
"endpoint": "https://cloud.terminusdb.com/Myseelia/",
"team": "Myseelia",
"use JWT token": true
}

View File

@ -0,0 +1,12 @@
from terminusdb_client import WOQLClient
client = WOQLClient("https://cloud.terminusdb.com/Myseelia/")
client.connect(db="murmurations", team="Myseelia", use_token=True)
# Query for all documents with type "person"
query = client.query_document({"@type": "person"})
ids = []
# Delete each document by its ID
for doc in list(query):
ids.append(doc['@id'])
client.delete_document(ids)

View File

@ -0,0 +1,174 @@
import csv
from itertools import islice
from schema import person
from terminusdb_client import WOQLClient
from datetime import datetime
import pytz
import re
import json
import meilisearch
import ast
import hashlib
import requests
import emoji
def get_emoji_regexp():
# Sort emoji by length to make sure multi-character emojis are
# matched first
emojis = sorted(emoji.EMOJI_DATA, key=len, reverse=True)
pattern = u'(' + u'|'.join(re.escape(u) for u in emojis) + u')'
return re.compile(pattern)
def remove_emojis(string):
return get_emoji_regexp().sub(r'', string)
# we keep all the information in dictionaries with Employee id as keys
orgs = {}
orgsjson = []
client = WOQLClient("https://cloud.terminusdb.com/Myseelia/")
client.connect(db="murmurations", team="Myseelia", use_token=True)
client1 = meilisearch.Client(
'https://ms-9ea4a96f02a8-1969.sfo.meilisearch.io', '117c691a34b21a6651798479ebffd181eb276958')
def delete_index(index_name):
try:
index = client1.index(index_name)
response = index.delete()
print(response)
except Exception as e:
print(e)
delete_index('people')
index = client1.index('people')
# Define the endpoint and headers for the API request
endpoint = 'https://test-index.murmurations.network/v2/nodes'
headers = {'Content-Type': 'application/json'}
# # Load the input data from a file
# with open('murmuration_people.json', 'r') as f:
# input_data = json.load(f)
# Load the input data from a URL
url = "https://test-index.murmurations.network/v2/nodes?schema=person_schema-v0.1.0"
input_data = requests.get(url).json()
# Extract the data field from the input data
response_data = input_data['data']
# Create a dictionary to keep track of the profile URLs that have already been processed
profile_urls = {}
# Create a dictionary to keep track of people based on their profile URL
people_dict = {}
# Create a list to store the people as `person` objects
people = []
for profile in response_data:
# Define the data to be sent in the GET request
endpoint = profile['profile_url']
headers = {'accept': 'application/json'}
# Send the GET request to retrieve the profile details
response = requests.get(endpoint, headers=headers)
if response.status_code == 200:
# Parse the JSON data and extract the necessary information to create a `person` object
json_data = response.json()
name = json_data.get('name', None)
description = json_data.get('description', None)
primary_url = json_data.get('primary_url', None)
image = json_data.get('image', None)
locality = json_data.get('locality', None)
profile_url = json_data.get('profile_url', None)
# Check if the person is already in the database
if profile_url in profile_urls:
continue
personname = remove_emojis(str(name))
# Create a `Person` object with the extracted information and the people they know
newperson = person(
name=personname,
description=str(description),
primary_url=str(primary_url),
image=str(image),
locality=str(locality),
vouches_for=set(),
LI=set()
)
# if personname not blank
if personname != 'None':
people_dict[personname] = newperson
people.append(newperson)
profile_urls[primary_url] = endpoint
else:
print(f"Error {response.status_code}: {response.reason}")
# Update the temporary person objects with missing information
for p in people:
if not p.name or not p.description or not p.image or not p.locality or not p.vouches_for or not p.LI:
profileurl = profile_urls[p.primary_url]
response = requests.get(profileurl, headers={'accept': 'application/json'})
if response.status_code == 200:
json_data = response.json()
p.name = json_data.get('name', None)
p.description = json_data.get('description', '')
p.image = json_data.get('image', '')
p.locality = json_data.get('locality', '')
p.vouches_for = set()
p.LI = set()
for person_data in json_data.get('knows', []):
url = person_data.get('url')
incommunity = False
if url not in profile_urls.values():
continue
print(person_data.get('name'))
knowsname = person_data.get('name')
relationship_type = person_data.get('type')
if relationship_type == 'VOUCHES_FOR':
if knowsname in people_dict:
p.vouches_for.add(people_dict[knowsname])
elif relationship_type == 'LI':
if knowsname in people_dict:
p.LI.add(people_dict[knowsname])
else:
print(f"Error {response.status_code}: {response.reason}")
BATCH_SIZE = 100
# Split the people list into batches
batches = [people[i:i+BATCH_SIZE] for i in range(0, len(people), BATCH_SIZE)]
# Insert each batch into TerminusDB
inserted = []
for batch in batches:
print(batch)
batch_inserted = client.insert_document(batch, commit_msg="Adding people")
print("inserted")
inserted.extend(batch_inserted)
document_ids = [doc_id for doc_id in inserted]
print("done inserting")
# Retrieve all documents at once
documents = client.query_document({"@type": "person"})
# Process each document
indexed_documents = []
for document in documents:
real_id = document['@id']
num_id = real_id.split("/")[-1]
document = {k: json.dumps(v) for k, v in document.items() if k != '@id'}
document.update({'id': num_id})
indexed_documents.append(document)
# Add all indexed documents to the index at once
index.add_documents(indexed_documents)

View File

@ -0,0 +1,63 @@
import csv
from itertools import islice
from schema import person
from terminusdb_client import WOQLClient
from datetime import datetime
import pytz
import re
import json
import meilisearch
import ast
import hashlib
import requests
# we keep all the information in dictionaries with Employee id as keys
orgs = {}
orgsjson = []
client = WOQLClient("https://cloud.terminusdb.com/Myseelia/")
client.connect(db="murmurations", team="Myseelia", use_token=True)
client1 = meilisearch.Client(
'https://ms-9ea4a96f02a8-1969.sfo.meilisearch.io', '117c691a34b21a6651798479ebffd181eb276958')
index = client1.index('people')
# Define the endpoint and headers for the API request
endpoint = 'https://test-index.murmurations.network/v2/nodes'
headers = {'Content-Type': 'application/json'}
# Load the input data from a file
with open('murmuration_people.json', 'r') as f:
input_data = json.load(f)
# Extract the data field from the input data
response_data = input_data['data']
# Loop through the response data and retrieve each profile's details with a curl request
people = []
person1 = person(
name="alice",
primary_url="https://test-index.murmurations.network",
)
person2 = person(
name="alice",
primary_url="https://test-index.murmurations.network",
)
person3 = person(
name="bob",
primary_url="https://test-index.murmurations.network3",
LI={person1}
)
person4 = person(
name="steve",
primary_url="https://test-index.murmurations.network4",
LI={person2}
)
people.append(person1)
people.append(person2)
people.append(person3)
people.append(person4)
inserted = client.insert_document(people, commit_msg="Adding people")

View File

@ -0,0 +1,55 @@
{
"data": [
{
"last_updated": 1677532593,
"linked_schemas": ["person_schema-v0.1.0"],
"locality": "Kortenaken",
"primary_url": "noo.network/user/33ed23b1-b6af-4d2b-96bf-ac050645fc97",
"profile_url": "https://noo.network/api/murmur/network/33ed23b1-b6af-4d2b-96bf-ac050645fc97",
"status": "posted"
},
{
"last_updated": 1676554597,
"linked_schemas": ["person_schema-v0.1.0"],
"primary_url": "noo.network/user/5793fa86-79ab-491c-a662-b66fe784c8dc",
"profile_url": "https://noo.network/api/murmur/network/5793fa86-79ab-491c-a662-b66fe784c8dc",
"status": "posted"
},
{
"last_updated": 1677874496,
"linked_schemas": ["person_schema-v0.1.0"],
"locality": "Victoria, BC",
"primary_url": "noo.network/user/5d614114-bcc6-4b56-8f62-2a59685668ef",
"profile_url": "https://noo.network/api/murmur/network/5d614114-bcc6-4b56-8f62-2a59685668ef",
"status": "posted"
},
{
"last_updated": 1676556053,
"linked_schemas": ["person_schema-v0.1.0"],
"locality": "Oakland, CA",
"primary_url": "noo.network/user/646d7156-f985-4d1b-be61-317ad5a843dd",
"profile_url": "https://noo.network/api/murmur/network/646d7156-f985-4d1b-be61-317ad5a843dd",
"status": "posted"
},
{
"last_updated": 1676555982,
"linked_schemas": ["person_schema-v0.1.0"],
"locality": "Sunnyvale, CA",
"primary_url": "noo.network/user/b7b5897d-fca7-4d06-bfba-63994479be0e",
"profile_url": "https://noo.network/api/murmur/network/b7b5897d-fca7-4d06-bfba-63994479be0e",
"status": "posted"
},
{
"last_updated": 1677532664,
"linked_schemas": ["person_schema-v0.1.0"],
"locality": "Zurich, Switzerland",
"primary_url": "noo.network/user/e3ad9e2c-40ec-4a89-afc8-b7fec35af6cf",
"profile_url": "https://noo.network/api/murmur/network/e3ad9e2c-40ec-4a89-afc8-b7fec35af6cf",
"status": "posted"
}
],
"links": {
"self": "http://test-index.murmurations.network/v2/nodes?schema=person_schema-v0.1.0\u0026page=1"
},
"meta": { "number_of_results": 6, "total_pages": 1 }
}

View File

@ -0,0 +1,34 @@
import requests
import json
import pandas as pd
# Define the endpoint and headers for the API request
endpoint = 'https://test-index.murmurations.network/v2/nodes'
headers = {'Content-Type': 'application/json'}
# Load the input data from a file
with open('murmuration_people.json', 'r') as f:
input_data = json.load(f)
# Extract the data field from the input data
response_data = input_data['data']
# Loop through the response data and retrieve each profile's details with a curl request
profiles = []
for profile in response_data:
# Define the data to be sent in the GET request
endpoint = profile['profile_url']
headers = {'accept': 'application/json'}
# Send the GET request to retrieve the profile details
response = requests.get(endpoint, headers=headers)
response_data = response.json()
print(response_data)
profiles.append(response)
# Convert the profiles list to a Pandas dataframe
df = pd.DataFrame(profiles)
# Display the resulting dataframe
df.to_csv('output.csv', index=False)

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,39 @@
####
# This is the script for storing the schema of your TerminusDB
# database for your project.
# Use 'terminusdb commit' to commit changes to the database and
# use 'terminusdb sync' to change this file according to
# the exsisting database schema
####
"""
"""
from typing import Optional, Set
from terminusdb_client.woqlschema import (
DocumentTemplate,
ValueHashKey,
)
class person(DocumentTemplate):
"""person_schema-v0.1.0
Attributes
----------
LI : Set['person']
knows
vouches_for : Set['person']
knows
"""
_key = ValueHashKey()
LI: Set["person"]
description: Optional[str]
image: Optional[str]
locality: Optional[str]
name: Optional[str]
primary_url: Optional[str]
vouches_for: Set["person"]
def __str__(self):
return f"person(name={self.name}, description={self.description}, primary_url={self.primary_url}, image={self.image}, locality={self.locality}, vouches_for={self.vouches_for}, LI={self.LI})"

View File

@ -0,0 +1,290 @@
<script lang="ts">
import cytoscape from 'cytoscape'
import { onMount } from 'svelte'
import { bubble } from 'svelte/internal'
import TerminusClient from '@terminusdb/terminusdb-client'
import { MeiliSearch } from 'meilisearch'
import { generateKnowledgeGraph } from './cytoscape.ts'
let cy
interface INodeData {
id: string
}
interface INode {
data: INodeData
}
interface IEdgeData {
id: string
source: string
target: string
label: string
}
interface IEdge {
data: IEdgeData
}
import json_graph from './knowledge_graph.json'
let knowledgeGraphJson: any = json_graph
// knowledgeGraphJson = await response.json()
// } else {
// alert(`HTTP-Error: ${response.status}`)
// }
// }
let nodes: INode[] = []
let edges: IEdge[] = []
onMount(async () => {
nodes = knowledgeGraphJson.entities.map((entity: any) => ({
data: { id: entity.id, label: entity.label }
}))
edges = knowledgeGraphJson.relations.map(
(relation: any, index: string) => ({
data: {
id: index,
source: relation.source,
target: relation.target,
label: relation.type
}
})
)
cy = cytoscape({
container: document.getElementById('cy'),
elements: {
nodes,
edges
},
style: [
{
selector: 'node',
style: {
'text-valign': 'center',
'text-halign': 'center',
'text-wrap': 'wrap',
'text-max-width': function (ele) {
return Math.max(1, Math.ceil(ele.degree() / 2)) * 30
},
'font-size': function (ele) {
return Math.max(1, Math.ceil(ele.degree() / 2)) * 6
},
'background-color': '#75f6df',
'border-color': '#223152',
'border-width': function (ele) {
return Math.max(1, Math.ceil(ele.degree() / 2))
},
label: 'data(label)',
width: function (ele) {
return Math.max(1, Math.ceil(ele.degree() / 2)) * 40
},
height: function (ele) {
return Math.max(1, Math.ceil(ele.degree() / 2)) * 40
}
}
},
{
selector: 'edge',
style: {
'font-size': 20,
width: 5,
'line-color': '#223152',
'target-arrow-color': '#223152',
'target-arrow-shape': 'triangle',
'curve-style': 'bezier',
'text-rotation': 'autorotate',
'text-offset': { x: 20, y: -20 },
'text-background-opacity': 1,
'text-background-color': '#fafafa',
'text-background-shape': 'roundrectangle',
label: 'data(label)'
}
}
],
layout: {
name: 'cose'
// infinite: true,
}
})
cy.nodes().forEach(function (node) {
node.data({
degree: node.connectedEdges().length
})
})
var nodes = cy.nodes()
nodes = nodes.sort(function (a, b) {
return b.data('degree') - a.data('degree')
})
var top100 = nodes.slice(0, 1000)
//console.log(top100)
cy.nodes().forEach(function (node) {
if (!top100.includes(node)) {
node.hide()
}
})
let toggle = true
// cy.off('tap', 'node', event => {
// const node = event.target;
// const nodeId = node.data('id');
// alert('unDisplay info for ' + nodeId);
// });
cy.on('tap', 'node', function (evt) {
var node = evt.target
var connectedEdges = node.connectedEdges()
var connectedNodes = node.neighborhood().nodes()
var allElements = cy.elements()
var allNodes = cy.nodes()
var allEdges = cy.edges()
if (node.style('display') == 'element') {
// hide all nodes and edges except the selected node and its neighbors
allNodes.style('display', 'none')
allEdges.style('display', 'none')
connectedNodes.style('display', 'element')
node.style('display', 'element')
connectedEdges.style('display', 'element')
} else {
// show all nodes and edges
allNodes.style('display', 'element')
allEdges.style('display', 'element')
}
})
// Reset the state when clicking away from the node
cy.on('tap', function (e) {
if (e.target === cy) {
cy.nodes().style('display', 'element')
cy.edges().style('display', 'element')
cy.nodes().data('highlighted', false)
}
})
cy.on('tap', 'edge', event => {
const edge = event.target
const edgeId = edge.data('id')
alert('Display info for ' + edgeId)
})
// cy.on('tap', 'node', function(){
// alert("put code here"));
// });
// cy.layout({
// name: 'cola'
// }).run();
})
var searchTerm = ''
function updateSearchTerm(e) {
searchTerm = e.target.value
// Perform search in real timebased on searchTerm here
}
async function entered(e) {
const searchclient = new MeiliSearch({
host: 'https://ms-9ea4a96f02a8-1969.sfo.meilisearch.io',
apiKey: '117c691a34b21a6651798479ebffd181eb276958'
})
const index = searchclient.index('people')
// this will search both keys and values
// const search = await index.search(e.target.value.toString(), { q: '*' });
// const searchResult = await index.search('orgs', {
// attributesToRetrieve: ['id']
// })
const searchResult = await index.search(e.target.value.toString())
// need to turn the search results into an array of ids which can be used to query the knowledge graph
const resultsgraph = await generateKnowledgeGraph(searchResult.hits).then(
resultsgraph => {
// console.log(resultsgraph)
const allNodes = resultsgraph.entities.map((entity: any) => ({
data: { id: entity.id, label: entity.label }
}))
const allEdges = resultsgraph.relations.map(
(relation: any, index: string) => ({
data: {
id: index,
source: relation.source,
target: relation.target,
label: relation.type
}
})
)
cy.remove(cy.elements())
cy.add(allNodes)
cy.add(allEdges)
cy.layout({
name: 'cose'
// other layout options here
}).run()
}
)
}
</script>
<div class="pt-8 p-6 md:p-8 mx-auto">
<input
id="search"
type="text"
placeholder="Search..."
on:input={updateSearchTerm}
on:keydown={event => {
if (event.keyCode === 13) {
entered(event)
}
}}
/>
</div>
<section class="overflow-hidden text-gray-700">
<div class="cyDiv" />
<div id="cy" />
</section>
<style>
#search {
position: absolute;
top: 10px;
z-index: 100;
background-color: white;
width: 50%;
height: 40px;
border-radius: 20px;
padding: 10px 20px 10px 40px;
}
#search input[type='text'] {
position: absolute;
top: 0px;
left: 50px;
width: 80%;
height: 100%;
border: none;
background-color: transparent;
font-size: 18px;
}
#cy-div {
z-index: 99;
}
#cy {
width: 100%;
height: 95%;
position: absolute;
top: 55px;
left: 0px;
}
</style>

View File

@ -0,0 +1,98 @@
// import { isNullOrUndefined } from 'util'
import TerminusClient from '@terminusdb/terminusdb-client'
import * as fs from 'fs'
const WOQL = TerminusClient.WOQL
export async function generateKnowledgeGraph(ids: object[]): Promise<object> {
console.log(ids)
const entities: { id: string; label: string; type: string }[] = []
const relations: { source: string; target: string; type: string }[] = []
for (const document of ids) {
const personid = 'Person/' + document['id']
let personEntity = entities.find(entity => entity.id === personid)
if (!personEntity) {
entities.push({
id: personid,
label: document['name'],
type: 'person'
})
personEntity = entities[entities.length - 1]
}
const linktypes = ['vouches_for', 'LI']
for (const link of linktypes) {
let linkValues = document[link]
try {
linkValues = JSON.parse(linkValues)
} catch (error) {
console.log(error)
}
if (Array.isArray(linkValues)) {
for (const linkValue of linkValues) {
if (linkValue === undefined) continue
const linkId = linkValue.replace(/^"|"$/g, '')
if (linkId !== undefined && linkId !== '') {
let linkEntity = entities.find(
entity => entity.id === linkId
)
if (!linkEntity) {
entities.push({
id: linkId,
label: linkValue,
type: 'attribute'
})
linkEntity = entities[entities.length - 1]
}
let linkRelation = relations.find(
relation =>
relation.source === personid && relation.target === linkId
)
if (!linkRelation) {
relations.push({
source: personid,
target: linkId,
type: link
})
}
}
}
} else {
let linkId = linkValues
// console.log(linkId)
if (linkId !== undefined && linkId !== '') {
linkId = linkId.replace(/^"|"$/g, '')
let linkEntity = entities.find(
entity => entity.id === linkId
)
if (!linkEntity) {
entities.push({
id: linkId,
label: document[link],
type: 'attribute'
})
linkEntity = entities[entities.length - 1]
}
let linkRelation = relations.find(
relation =>
relation.source === personid && relation.target === linkId
)
if (!linkRelation) {
relations.push({
source: personid,
target: linkId,
type: link
})
}
}
}
}
}
return {
entities: entities,
relations: relations
}
}
export default generateKnowledgeGraph

View File

@ -0,0 +1,92 @@
{
"entities": [{
"label": "Organization",
"title": "Neuralink"
}, {
"label": "Organization",
"title": "SpaceX"
}, {
"label": "Organization",
"title": "Pretoria"
}, {
"label": "Organization",
"title": "The Boring Company"
}, {
"label": "Organization",
"title": "University of Pretoria"
}, {
"label": "Organization",
"title": "Stanford University"
}, {
"label": "Person",
"title": "Jeff Bezos"
}, {
"label": "Organization",
"title": "University of Pennsylvania"
}, {
"label": "Person",
"title": "Kimbal Musk"
}, {
"label": "Organization",
"title": "Tesla, Inc."
}, {
"label": "Person",
"title": "Elon Musk"
}],
"relations": [{
"source": "Elon Musk",
"target": "Neuralink"
}, {
"source": "Tesla, Inc.",
"target": "Elon Musk",
"type": "owned by"
}, {
"source": "Elon Musk",
"target": "University of Pennsylvania",
"type": "residence"
}, {
"source": "Elon Musk",
"target": "Tesla, Inc.",
"type": "owned by"
}, {
"source": "The Boring Company",
"target": "Tesla, Inc.",
"type": "owned by"
}, {
"source": "Elon Musk",
"target": "Kimbal Musk",
"type": "sibling"
}, {
"source": "University of Pennsylvania",
"target": "Elon Musk",
"type": "residence"
}, {
"source": "The Boring Company",
"target": "Neuralink",
"type": "subsidiary"
}, {
"source": "Elon Musk",
"target": "University of Pretoria",
"type": "work location"
}, {
"source": "The Boring Company",
"target": "Elon Musk",
"type": "owned by"
}, {
"source": "Kimbal Musk",
"target": "Elon Musk",
"type": "sibling"
}, {
"source": "Neuralink",
"target": "Elon Musk",
"type": "owned by"
}, {
"source": "Elon Musk",
"target": "The Boring Company",
"type": "owned by"
}, {
"source": "Elon Musk",
"target": "University of Pennsylvania",
"type": "work location"
}]
}

25
src/components/CTA/df.csv Normal file
View File

@ -0,0 +1,25 @@
Document id,name,assignee,datecreated,description,logo,preJan20thUpvotes,reviewed,submittedbyemail,submittedbyname,submittedbyowner,subscribed,upvotes,impactarea,topic,blockchainecosystem,web3
Organization/1e0832fa5474a176e004f0e4d587ee7801bd7272e9abb7743d8127324dc259aa,test,,,,,,,,,,,,,,,
Organization/94e4bec6c4397de81b525a97b3d16af681727a5c9f06c7f27cc2564835b59143,tester,,0000-12-31T23:59:59.900Z,,,0,,,,,,0,,,,
Organization/9470c06cddf598b90a2e072c71ebe1aff7414f371257809343c92cbb24fdb3ef,darren,,,,,,,,,,,,,,,
Organization/04a5b028a7499aa41d5e0e433b5d6128255611becd569bcbda3115700a7c0744,BVRio Environmental Exchange Platform,https://www.bvrio.org/,2022-05-07T11:03:00Z,BVRio's mission is to promote the use of market mechanisms to facilitate compliance with environmental laws and support the green and low carbon economy.,,0,checked,,,,,,['Investing'],['Consulting'],,
Organization/0008f7122957abba12e9db800fab7975254bd4ad0cd7c90e93a906566174e936,Sustainable Impact Token,https://sustainableimpacttokens.com/,2022-07-08T10:39:00Z,SIT PRESENTS A FIRST-OF-ITS KIND INVESTMENT OPPORTUNITY UNDERPINNED BY ITS THREE CORE DRIVING OBJECTIVES: 1.Food security 2.Renewable energy 3.Carbon reduction,,,checked,,,,,,['FoodAg'],"['Offsetting', 'Water']",['Tezos'],['Token']
Organization/00cc6f5581fa22b43900ac0c0e42fd7ebe3b2e0c9846289fb264b9d3700b9617,Hazel,https://hazelverse.xyz,2022-12-09T11:25:00Z,"Hazel gamifies carbon removal, using NFTs and a casual game mechanic, to bring the public to the carbon-removal market and fight climate change. ",App_Store3x_gu09bjgyh.png (https://v5.airtableusercontent.com/v1/15/15/1675389600000/QApQyC6ogdwCVHtjjqXcOw/dqG9lSZe7D_F-YxLZgamq6hz2DxolRu7dO-AdM19PHR7FD1U6ll7eHzrdOAWsNV8OGPHRYnW2028usrc0c6hGPYYQVL6qjb32_0oae0NLraLYZZAxZ9zPUiqc0QTsXmQ/2bC8jNqUgE7i4PgZxlytnQIsbQ41lagTvu-FnEwy0pI),11,,,,checked,checked,11,['Carbon'],,"['Celo', 'Ethereum', 'Polygon']","['Community', 'Metaverse', 'NFT']"
Organization/01403ea7a34e7b764aea71cde72a086a46026596de4101514a7888616a66715b,Open Forest Protocol,https://www.openforestprotocol.org/product,2022-05-07T11:03:00Z,"Transparent, scalable and open-source forest monitoring and carbon financing",open-forest-logo.jpeg (https://v5.airtableusercontent.com/v1/15/15/1675389600000/REle8XxHaWAMQ97Ytj5I3Q/8CaLlOovk58wMA-pzOczx6P1s6_eVUd4eQ1ZI4nFgVTotdc29QQkIIdYAuKdqedfaYDhMvt_NAqIE7YELFT29TLKBlgd2XLwfBfQSTMpWHI/CIjmH4hsWQyrzrTYhUglOwQXSL1EDdr4s7j0PN5pa1Y),9,checked,,,,,9,['Nature'],"['Biodiversity', 'Community']",['Near'],['Token']
Organization/014f869845e4f3dddf5d26dfae12c9de8c8be2a6f8e4b5463cd02e2685fe343a,Dream Village,https://dreamvillageghana.org/,2022-10-24T06:51:00Z,"Introducing regenerative agriculture, economies and developmental projects in Ghana",FB_IMG_1666014010988.jpg (https://v5.airtableusercontent.com/v1/15/15/1675389600000/X98CY9sPXQ0YdS-4x_PIgw/aehNZ2rZZiY9jnVjEL9yCWMniFHEefn7IcPBhiuA-5Ze54wbSIES-lO59L2uH__fsuIDdjQJyyQ7masFYd_XQc5twYZ6WmU6aCY5KpE03wY/ylF5DBoSJRoUht6ZC7cc33nPa9LyFRYR0jL4YyvJiaI),,,,,checked,checked,,"['Education', 'FoodAg', 'Innovation', 'Nature']","['Agriculture', 'Animals', 'Biodiversity', 'Energy', 'FoodForests', 'Forestry', 'Water']","['Other', 'RegenNetwork']","['DAO', 'dApp']"
Organization/016c0a02a6aa71d885e150f641417b86209a2983d0164d16b7e7446923af87a9,C4EST,cforest.org,2022-10-25T13:26:00Z,"Creating a token-based economy to invest in building plantations near city peripheries most prone to global warmings effects and turning it into a nature-based bio-economy according to local industrial, social and environmental needs of the cities. ",c4est-logo-white.png (https://v5.airtableusercontent.com/v1/15/15/1675389600000/mEjWQLbUQ_lntUnTIgAwLQ/lY4OM_RR6IXwjLCP2QuR27GHJTTUBLZiy3CxxHbieXR4mvwcCnmUxW6SdXvN2DZkKknk0BYXzAWomfQ4xXkgRzHcOS-1SzOmHGXl4AHml4E/1SGyW1Huo5quCKAZyFMewiNGl2fyYWOuRc9FZNuLKWk),,,,,checked,checked,,"['Investing', 'Carbon', 'Energy', 'FoodAg', 'Industry', 'Infrastructure', 'Nature', 'SocialJustice']","['Agriculture', 'Animals', 'Biodiversity', 'Energy', 'FoodForests', 'Forestry', 'Meteorology', 'Water']",['Other'],"['Blockchain', 'DAO', 'NFT', 'Stablecoin', 'Token']"
Organization/019857cb0cfd541aae2988a0f43469d7d854e4821a80de9f2802f95b12d0bd6d,Future Quest,https://future.quest/,2022-10-07T07:27:00Z,"We're building a play-and-earn ecosystem committed to fixing our future. Part game, part launch pad for public good quests for our planet. ",1E755576-AF4D-40C3-ACAF-EB0E7603B88E_eplcl0rwi.jpeg (https://v5.airtableusercontent.com/v1/15/15/1675389600000/8DyTh8VREtSojyYvkgZEfw/sf2GfUZhYh409Pxc-jO9lubI3iBHDyorjvnxSJAE_IL7sOeC3V4JNo74wKB2QwZKAz_50Zwk3M62Ars8QBfEQF8P752SZLTfp20v5QVi2lqAbfepY2lgbqrBUoB9_k7ZborfVygtcu0RwTBbuAGlew/oEaKRPThOtUT5puZQlUqBZ2zg-CHqn31L9wzykPZQps),198,checked,,,,,198,['Investing'],,['Polygon'],['DAO']
Organization/01adb0a6eb8da1c4f4ceb2176ad0f43aff28078a02deb1abc68b1346fceef886,KOKO DAO,https://kokodao.xyz/,2022-10-24T06:51:00Z,Proof of impact based on biodiversity and work with local communities,Koko Dao-02.jpg (https://v5.airtableusercontent.com/v1/15/15/1675389600000/OPi-TZ9RwZQ-BR6Bbd71Iw/M3-EE4ZFpK7IFwwYuv7Mze8sIIFZynaZkoJDjazn_GFaTAmA84b49TIx9468HegZfX16S54FswjFj-6joOLSBxD144CF7f8TLjNCqGj-ugI/Dr17MF1t5ySqm1q-uMy8zoSi4_JCXjwjPi-jxc_VbA8),,,,,checked,checked,,"['FoodAg', 'Nature']","['Agriculture', 'Biodiversity', 'FoodForests', 'Forestry']","['Cosmos', 'Other', 'RegenNetwork']",['DAO']
Organization/02662d81af586be2cb75939a63e6bc7c9cc5e83cfc623cadf98ab3f28e548bd9,[redacted] labs (name incoming),,2022-10-25T10:39:00Z,DeFi-powered impact financing leveraging the graph to create webs of trust,[r].png (https://v5.airtableusercontent.com/v1/15/15/1675389600000/w9wyV7EUxpyv4EiakYXKrg/itqTMuokAETY6w0rNrPx39nYO_fTv3HPWCWjw67hDAHebcp4Hu8Do9bACW3W-_fPba793iDlUS8ybbWeOOyJcg/A-1il_EVTp_xO3urUmL0KM0QxNT6zSJUjYKEMqliZiw),6,,,,checked,checked,6,"['Investing', 'Carbon', 'Education', 'Energy', 'Infrastructure', 'Innovation', 'Nature', 'SocialJustice']","['Agriculture', 'Biodiversity', 'Energy', 'Forestry']","['Celo', 'Ethereum', 'Other', 'Polygon']","['Blockchain', 'DEX', 'NFT', 'Oracle', 'Other', 'Stablecoin', 'Token', 'dApp']"
Organization/02a8610aba2945b5723145dd82e329236c763f703f7e70d0b9725007893d7d35,ORGO,orgo.earth,2023-01-11T13:18:00Z,"ORGO is a Sponsorship Marketplace created to support environmental and social causes, while aiming to close the gap in sustainable finance and rewarding positive behaviors.",ORGO_Logo-04_t44cgzkp6.png (https://v5.airtableusercontent.com/v1/15/15/1675389600000/I1yecpmz-U3zUSYcUDqHbw/dnZTUG352RkLoXhm708w_bS0UzvpA4nYNA8nL3A9Z11hcyKe1RWsEvbFFYWvSxS0AR8RD3-thFSCqTQbxI-HePl1UZ-Qi-Jepn946vhQzxImY4gWRtRJFcELktpiG6bk/VyTIItCojOjg5atTVPjbcE3pM34rHNRpCZr8WTInFbE),,,,,checked,checked,,['Nature'],,"['Polkadot', 'Polygon']","['Community', 'DAO', 'Marketplace', 'NFT', 'Token', 'Wallet', 'dApp']"
Organization/031e45922dbae180e6b18df9679087bf8a5c796b2a446416cf95b5389effe3c6,Seatle NFT Museum,https://www.seattlenftmuseum.com/,2022-05-07T11:03:00Z,"Explore the Future of Art. Welcome to Seattle's first NFT art museum, designed to bring together artists, creators, collectors, and the broader blockchain community. ",,,checked,,,,,,['SocialJustice'],"['Art', 'Community', 'Initiative', 'Local']",,['NFT']
Organization/0353ac2dd478a36e62deb2dba20307892bb8f25cb4d174c03723cfbc611373ef,Circonomy,https://circon.me/,2022-11-27T15:54:00Z,Our mission and vision is to turn capital goods into public goods by putting the circular economy on-chain and we'll accomplish this with a recycle/reuse-to-earn protocol.,Logo_mvm3cargk.png (https://v5.airtableusercontent.com/v1/15/15/1675389600000/Y269VSJYFyDC5rvpbdYFWQ/GKDZqxVfvDsmJk7VbQdxOioAblOA3InWQepzYmuCLwunOYFZZf3AcoIMe4cGfSTHRClb5X0kbi7OtG09iN6ENARDixI1njKwHvI-O2vfAJ8/yPiaOgnhLaXtGPdDfMulfe96VY_oswaf4ZKPnP9yzRI),,,,,checked,checked,,['Industry'],,"['Celo', 'Near', 'Polygon']","['Blockchain', 'Community', 'DAO', 'Marketplace', 'NFT', 'Token', 'Wallet', 'dApp']"
Organization/03a93b37edff42ea223d03209eb8bfb0f3107bfc8e8077fbbaffd452d4ebdc9e,earth.fm,http://earth.fm/,2022-07-08T10:39:00Z,Earth.fm is a non-profit organisation that seeks to protect and regenerate natural ecosystems and reconnect us to the more-than-human world.,,,checked,,,,,,['Nature'],['Biodiversity'],,
Organization/03c3c47e38024c1d5b04dfe90e458dbf645478748df406c0f9f07a338b4411bd,The Sun Exchange,https://thesunexchange.com/,2022-05-07T11:03:00Z,"Enter Sun Exchange, the worlds first peer-to-peer solar leasing platform. Through Sun Exchange, anyone, anywhere in the world, can own solar energy-producing cells and build wealth by leasing those cells to power businesses and organisations in emerging markets, with installations and maintenance taken care of by one of Sun Exchanges carefully selected installation partners. We leverage financial innovation and the power of the crowd to drive sustainable energy development and make the environmental, social and economic benefits of solar accessible and affordable for all.",,,checked,,,,,,['Energy'],"['Fundraising', 'Renewables']",,
Organization/03ea9160ae4936db68df53e3d5ad6bf68b581baaa94ed4a41ddc03c6283c48dd,HARA,https://haratoken.io/,2022-05-07T11:03:00Z,Making the invisible visible through the use of technology to accelerate agricultural development for Indonesian farmers to be more equitable.,hara-token.jpeg (https://v5.airtableusercontent.com/v1/15/15/1675389600000/iOVDj9hkXM9q03Bm6j71Cg/VXwxSyxEKrFvTBFTVEQNlp7O0_uJimFkl5dbmWV4Gah8nia3nZL12DxcQcVu7resnlnO-y79JLjD8O3Tk7TupSa6u2lijrR1NwhZVIPUy7I/3pQR2hA-FQA_h6YisCrGCymzBcShanfdcRep_-LY3QY),4,checked,,,,,4,['FoodAg'],"['Community', 'Land', 'Local']",['Ethereum'],"['Blockchain', 'Oracle']"
Organization/052479193a721ea298ea93ff64522938f64a1ef4590e6a66bfcf12857e452d9e,"UCO Network
",https://www.uco.network/,2022-09-21T15:17:00Z,UCO Network is a public blockchain protocol that is deploying a suite of decentralised technologies that mitigate the risk of fraud and open new and exciting opportunities within the Advanced Bio fuel and Web3. Industries.,,,,,,,,,['Nature'],"['CircularEconomy', 'Traceability', 'waste']",,"['NFT', 'Token']"
Organization/053c0a1ae666ac91dd14ee418b079e5585f0d3d79d6cacbaf0f31aec12aa236f,Rebioca,https://www.rebioca.com/,2022-10-24T06:51:00Z,"Rebioca aims to design the products of the future by recycling organic wastes using less water and energy resulting in low carbon emissions, compared to traditional methods and processes.",rebioca-retina.png (https://v5.airtableusercontent.com/v1/15/15/1675389600000/0BuvqJilW-a2JyvVdvw80Q/bySVWUGoI2IFHjRc8JeqqSE5ROhj99AuT7CLlXzz7A5anfwab4LDw8QYYloYwJmJ5SyCfc3SPAMQIRBLRyi6Pubm66NS81OafcAR8XnXMk8/n4Q0fBDszBqBwS4fZRVhcKAIonPtihdvQyPdu2UUPA0),,,,,checked,checked,,"['Carbon', 'Energy', 'FoodAg', 'Nature']","['Agriculture', 'Animals', 'Water']",['Celo'],"['NFT', 'dApp']"
Organization/0544651b16d40568dedeaa97f4b8bd9c69a5b5988bbd463cfe081219bba5a132,Pozzle Planet,www.pozzleplanet.com,2022-10-24T06:51:00Z,"Pozzle Planet is a social app is a fun and inspiring way to make a positive impact in your everyday life. Earn POZ by joining and sharing planet positive actions with your friends via short-form video. On the outside, a social app where people generate impact by sharing videos and earning rewards. Underneath the hood, an impact-Protocol and ReFi yield generator that passively farms peoples impact into NFTs called Pozzles that contain impact tokens (Impact2) which represent measurable impact units from the real world. The Pozzle Planet mobile app sits at the intersection of 3 main growth trends in social, p2e gaming and DeFi, uniquely positioned as the first impact-2-earn social platform with tailwinds from increased individual and collective aspirations to be part of planet positive solutions. ",PP App Icon.png (https://v5.airtableusercontent.com/v1/15/15/1675389600000/WpQxVA6gWbRZIEpSBWAU3w/5tLAZtjfE3T-GJS1nifQBwzCSY5hbcjU2Bd8NZ8g3kkk2k9mxlhXRT2yDmXeG4csITwplZmpUctEp4-B1hk4p49bRZ2oTsmGl5T57kKNPTE/ZsQM_kMT8hWKj-FTz8egxkc5dJYjbOUqH78Mj0fNbDo),3,,,,checked,checked,3,"['Investing', 'Carbon', 'Education', 'Nature', 'Other', 'SocialJustice']","['Community', 'Local', 'Other']","['Celo', 'Polygon']","['NFT', 'Token', 'dApp']"
Organization/056f40719cfa5cc97d6b969a454032af6e5a92d7f76e8a4fb0b60c41b8fa2814,Global Innovation Lab for Climate Finance (the Lab),http://www.climatefinancelab.org/,2022-05-07T11:03:00Z,"Nations, businesses, and investors are working to move toward a low-carbon, climate resilient economy. Many of the measures underpinning this transition, including energy efficiency, renewable energy, sustainable transport, climate smart agriculture, and curbing deforestation, face specific barriers to attracting investment. By identifying, developing, and supporting transformative sustainable finance ideas, the Lab aims to drive billions of dollars of private investment to the low-carbon economy.",,,checked,,,,,,['Carbon'],"['Fundraising', 'Investing', 'Land', 'Renewables']",,
Organization/05bed03da8c15018650d4d7e13d0cc5a1c6c0b990bed5a92cbad818ff1826603,Carbovalent,https://carbovalent.com/,2022-12-22T13:34:00Z,"Carbovalent is a carbon credit network built on the Solana blockchain that aims to address the challenges of transparency, accessibility, liquidity, and composability in traditional off-chain carbon credit registries. ",Picture1_nxlmhoump.png (https://v5.airtableusercontent.com/v1/15/15/1675389600000/NKH1E0WNPC0G2SMM9mki7Q/6R2vCNu-M-CJG05WhhG25yu490EYRYQuGiUowPZW_o6iHP_EJbMQanZgRZNqojQ91RAzKfj14C3YkFyakQX2YvkOignsoU9nFaKUxtNcJ_Y/I4ItZRupaa8rBkbOBQd_hB9pYVB7WAx_GFCoesZ-7EI),911,,,,checked,,911,['Carbon'],,,"['Blockchain', 'Bridge', 'Marketplace', 'NFT', 'Token', 'dApp']"
1 Document id name assignee datecreated description logo preJan20thUpvotes reviewed submittedbyemail submittedbyname submittedbyowner subscribed upvotes impactarea topic blockchainecosystem web3
2 Organization/1e0832fa5474a176e004f0e4d587ee7801bd7272e9abb7743d8127324dc259aa test
3 Organization/94e4bec6c4397de81b525a97b3d16af681727a5c9f06c7f27cc2564835b59143 tester 0000-12-31T23:59:59.900Z 0 0
4 Organization/9470c06cddf598b90a2e072c71ebe1aff7414f371257809343c92cbb24fdb3ef darren
5 Organization/04a5b028a7499aa41d5e0e433b5d6128255611becd569bcbda3115700a7c0744 BVRio Environmental Exchange Platform https://www.bvrio.org/ 2022-05-07T11:03:00Z BVRio's mission is to promote the use of market mechanisms to facilitate compliance with environmental laws and support the green and low carbon economy. 0 checked ['Investing'] ['Consulting']
6 Organization/0008f7122957abba12e9db800fab7975254bd4ad0cd7c90e93a906566174e936 Sustainable Impact Token https://sustainableimpacttokens.com/ 2022-07-08T10:39:00Z SIT PRESENTS A FIRST-OF-ITS KIND INVESTMENT OPPORTUNITY UNDERPINNED BY ITS THREE CORE DRIVING OBJECTIVES: 1.Food security 2.Renewable energy 3.Carbon reduction checked ['FoodAg'] ['Offsetting', 'Water'] ['Tezos'] ['Token']
7 Organization/00cc6f5581fa22b43900ac0c0e42fd7ebe3b2e0c9846289fb264b9d3700b9617 Hazel https://hazelverse.xyz 2022-12-09T11:25:00Z Hazel gamifies carbon removal, using NFTs and a casual game mechanic, to bring the public to the carbon-removal market and fight climate change. App_Store3x_gu09bjgyh.png (https://v5.airtableusercontent.com/v1/15/15/1675389600000/QApQyC6ogdwCVHtjjqXcOw/dqG9lSZe7D_F-YxLZgamq6hz2DxolRu7dO-AdM19PHR7FD1U6ll7eHzrdOAWsNV8OGPHRYnW2028usrc0c6hGPYYQVL6qjb32_0oae0NLraLYZZAxZ9zPUiqc0QTsXmQ/2bC8jNqUgE7i4PgZxlytnQIsbQ41lagTvu-FnEwy0pI) 11 checked checked 11 ['Carbon'] ['Celo', 'Ethereum', 'Polygon'] ['Community', 'Metaverse', 'NFT']
8 Organization/01403ea7a34e7b764aea71cde72a086a46026596de4101514a7888616a66715b Open Forest Protocol https://www.openforestprotocol.org/product 2022-05-07T11:03:00Z Transparent, scalable and open-source forest monitoring and carbon financing open-forest-logo.jpeg (https://v5.airtableusercontent.com/v1/15/15/1675389600000/REle8XxHaWAMQ97Ytj5I3Q/8CaLlOovk58wMA-pzOczx6P1s6_eVUd4eQ1ZI4nFgVTotdc29QQkIIdYAuKdqedfaYDhMvt_NAqIE7YELFT29TLKBlgd2XLwfBfQSTMpWHI/CIjmH4hsWQyrzrTYhUglOwQXSL1EDdr4s7j0PN5pa1Y) 9 checked 9 ['Nature'] ['Biodiversity', 'Community'] ['Near'] ['Token']
9 Organization/014f869845e4f3dddf5d26dfae12c9de8c8be2a6f8e4b5463cd02e2685fe343a Dream Village https://dreamvillageghana.org/ 2022-10-24T06:51:00Z Introducing regenerative agriculture, economies and developmental projects in Ghana FB_IMG_1666014010988.jpg (https://v5.airtableusercontent.com/v1/15/15/1675389600000/X98CY9sPXQ0YdS-4x_PIgw/aehNZ2rZZiY9jnVjEL9yCWMniFHEefn7IcPBhiuA-5Ze54wbSIES-lO59L2uH__fsuIDdjQJyyQ7masFYd_XQc5twYZ6WmU6aCY5KpE03wY/ylF5DBoSJRoUht6ZC7cc33nPa9LyFRYR0jL4YyvJiaI) checked checked ['Education', 'FoodAg', 'Innovation', 'Nature'] ['Agriculture', 'Animals', 'Biodiversity', 'Energy', 'FoodForests', 'Forestry', 'Water'] ['Other', 'RegenNetwork'] ['DAO', 'dApp']
10 Organization/016c0a02a6aa71d885e150f641417b86209a2983d0164d16b7e7446923af87a9 C4EST cforest.org 2022-10-25T13:26:00Z Creating a token-based economy to invest in building plantations near city peripheries most prone to global warming’s effects and turning it into a nature-based bio-economy according to local industrial, social and environmental needs of the cities. c4est-logo-white.png (https://v5.airtableusercontent.com/v1/15/15/1675389600000/mEjWQLbUQ_lntUnTIgAwLQ/lY4OM_RR6IXwjLCP2QuR27GHJTTUBLZiy3CxxHbieXR4mvwcCnmUxW6SdXvN2DZkKknk0BYXzAWomfQ4xXkgRzHcOS-1SzOmHGXl4AHml4E/1SGyW1Huo5quCKAZyFMewiNGl2fyYWOuRc9FZNuLKWk) checked checked ['Investing', 'Carbon', 'Energy', 'FoodAg', 'Industry', 'Infrastructure', 'Nature', 'SocialJustice'] ['Agriculture', 'Animals', 'Biodiversity', 'Energy', 'FoodForests', 'Forestry', 'Meteorology', 'Water'] ['Other'] ['Blockchain', 'DAO', 'NFT', 'Stablecoin', 'Token']
11 Organization/019857cb0cfd541aae2988a0f43469d7d854e4821a80de9f2802f95b12d0bd6d Future Quest https://future.quest/ 2022-10-07T07:27:00Z We're building a play-and-earn ecosystem committed to fixing our future. Part game, part launch pad for public good quests for our planet. 1E755576-AF4D-40C3-ACAF-EB0E7603B88E_eplcl0rwi.jpeg (https://v5.airtableusercontent.com/v1/15/15/1675389600000/8DyTh8VREtSojyYvkgZEfw/sf2GfUZhYh409Pxc-jO9lubI3iBHDyorjvnxSJAE_IL7sOeC3V4JNo74wKB2QwZKAz_50Zwk3M62Ars8QBfEQF8P752SZLTfp20v5QVi2lqAbfepY2lgbqrBUoB9_k7ZborfVygtcu0RwTBbuAGlew/oEaKRPThOtUT5puZQlUqBZ2zg-CHqn31L9wzykPZQps) 198 checked 198 ['Investing'] ['Polygon'] ['DAO']
12 Organization/01adb0a6eb8da1c4f4ceb2176ad0f43aff28078a02deb1abc68b1346fceef886 KOKO DAO https://kokodao.xyz/ 2022-10-24T06:51:00Z Proof of impact based on biodiversity and work with local communities Koko Dao-02.jpg (https://v5.airtableusercontent.com/v1/15/15/1675389600000/OPi-TZ9RwZQ-BR6Bbd71Iw/M3-EE4ZFpK7IFwwYuv7Mze8sIIFZynaZkoJDjazn_GFaTAmA84b49TIx9468HegZfX16S54FswjFj-6joOLSBxD144CF7f8TLjNCqGj-ugI/Dr17MF1t5ySqm1q-uMy8zoSi4_JCXjwjPi-jxc_VbA8) checked checked ['FoodAg', 'Nature'] ['Agriculture', 'Biodiversity', 'FoodForests', 'Forestry'] ['Cosmos', 'Other', 'RegenNetwork'] ['DAO']
13 Organization/02662d81af586be2cb75939a63e6bc7c9cc5e83cfc623cadf98ab3f28e548bd9 [redacted] labs (name incoming) 2022-10-25T10:39:00Z DeFi-powered impact financing leveraging the graph to create webs of trust [r].png (https://v5.airtableusercontent.com/v1/15/15/1675389600000/w9wyV7EUxpyv4EiakYXKrg/itqTMuokAETY6w0rNrPx39nYO_fTv3HPWCWjw67hDAHebcp4Hu8Do9bACW3W-_fPba793iDlUS8ybbWeOOyJcg/A-1il_EVTp_xO3urUmL0KM0QxNT6zSJUjYKEMqliZiw) 6 checked checked 6 ['Investing', 'Carbon', 'Education', 'Energy', 'Infrastructure', 'Innovation', 'Nature', 'SocialJustice'] ['Agriculture', 'Biodiversity', 'Energy', 'Forestry'] ['Celo', 'Ethereum', 'Other', 'Polygon'] ['Blockchain', 'DEX', 'NFT', 'Oracle', 'Other', 'Stablecoin', 'Token', 'dApp']
14 Organization/02a8610aba2945b5723145dd82e329236c763f703f7e70d0b9725007893d7d35 ORGO orgo.earth 2023-01-11T13:18:00Z ORGO is a Sponsorship Marketplace created to support environmental and social causes, while aiming to close the gap in sustainable finance and rewarding positive behaviors. ORGO_Logo-04_t44cgzkp6.png (https://v5.airtableusercontent.com/v1/15/15/1675389600000/I1yecpmz-U3zUSYcUDqHbw/dnZTUG352RkLoXhm708w_bS0UzvpA4nYNA8nL3A9Z11hcyKe1RWsEvbFFYWvSxS0AR8RD3-thFSCqTQbxI-HePl1UZ-Qi-Jepn946vhQzxImY4gWRtRJFcELktpiG6bk/VyTIItCojOjg5atTVPjbcE3pM34rHNRpCZr8WTInFbE) checked checked ['Nature'] ['Polkadot', 'Polygon'] ['Community', 'DAO', 'Marketplace', 'NFT', 'Token', 'Wallet', 'dApp']
15 Organization/031e45922dbae180e6b18df9679087bf8a5c796b2a446416cf95b5389effe3c6 Seatle NFT Museum https://www.seattlenftmuseum.com/ 2022-05-07T11:03:00Z Explore the Future of Art. Welcome to Seattle's first NFT art museum, designed to bring together artists, creators, collectors, and the broader blockchain community. checked ['SocialJustice'] ['Art', 'Community', 'Initiative', 'Local'] ['NFT']
16 Organization/0353ac2dd478a36e62deb2dba20307892bb8f25cb4d174c03723cfbc611373ef Circonomy https://circon.me/ 2022-11-27T15:54:00Z Our mission and vision is to turn capital goods into public goods by putting the circular economy on-chain and we'll accomplish this with a recycle/reuse-to-earn protocol. Logo_mvm3cargk.png (https://v5.airtableusercontent.com/v1/15/15/1675389600000/Y269VSJYFyDC5rvpbdYFWQ/GKDZqxVfvDsmJk7VbQdxOioAblOA3InWQepzYmuCLwunOYFZZf3AcoIMe4cGfSTHRClb5X0kbi7OtG09iN6ENARDixI1njKwHvI-O2vfAJ8/yPiaOgnhLaXtGPdDfMulfe96VY_oswaf4ZKPnP9yzRI) checked checked ['Industry'] ['Celo', 'Near', 'Polygon'] ['Blockchain', 'Community', 'DAO', 'Marketplace', 'NFT', 'Token', 'Wallet', 'dApp']
17 Organization/03a93b37edff42ea223d03209eb8bfb0f3107bfc8e8077fbbaffd452d4ebdc9e earth.fm http://earth.fm/ 2022-07-08T10:39:00Z Earth.fm is a non-profit organisation that seeks to protect and regenerate natural ecosystems and reconnect us to the more-than-human world. checked ['Nature'] ['Biodiversity']
18 Organization/03c3c47e38024c1d5b04dfe90e458dbf645478748df406c0f9f07a338b4411bd The Sun Exchange https://thesunexchange.com/ 2022-05-07T11:03:00Z Enter Sun Exchange, the world’s first peer-to-peer solar leasing platform. Through Sun Exchange, anyone, anywhere in the world, can own solar energy-producing cells and build wealth by leasing those cells to power businesses and organisations in emerging markets, with installations and maintenance taken care of by one of Sun Exchange’s carefully selected installation partners. We leverage financial innovation and the power of the crowd to drive sustainable energy development and make the environmental, social and economic benefits of solar accessible and affordable for all. checked ['Energy'] ['Fundraising', 'Renewables']
19 Organization/03ea9160ae4936db68df53e3d5ad6bf68b581baaa94ed4a41ddc03c6283c48dd HARA https://haratoken.io/ 2022-05-07T11:03:00Z Making the invisible visible through the use of technology to accelerate agricultural development for Indonesian farmers to be more equitable. hara-token.jpeg (https://v5.airtableusercontent.com/v1/15/15/1675389600000/iOVDj9hkXM9q03Bm6j71Cg/VXwxSyxEKrFvTBFTVEQNlp7O0_uJimFkl5dbmWV4Gah8nia3nZL12DxcQcVu7resnlnO-y79JLjD8O3Tk7TupSa6u2lijrR1NwhZVIPUy7I/3pQR2hA-FQA_h6YisCrGCymzBcShanfdcRep_-LY3QY) 4 checked 4 ['FoodAg'] ['Community', 'Land', 'Local'] ['Ethereum'] ['Blockchain', 'Oracle']
20 Organization/052479193a721ea298ea93ff64522938f64a1ef4590e6a66bfcf12857e452d9e UCO Network https://www.uco.network/ 2022-09-21T15:17:00Z UCO Network is a public blockchain protocol that is deploying a suite of decentralised technologies that mitigate the risk of fraud and open new and exciting opportunities within the Advanced Bio fuel and Web3. Industries. ['Nature'] ['CircularEconomy', 'Traceability', 'waste'] ['NFT', 'Token']
21 Organization/053c0a1ae666ac91dd14ee418b079e5585f0d3d79d6cacbaf0f31aec12aa236f Rebioca https://www.rebioca.com/ 2022-10-24T06:51:00Z Rebioca aims to design the products of the future by recycling organic wastes using less water and energy resulting in low carbon emissions, compared to traditional methods and processes. rebioca-retina.png (https://v5.airtableusercontent.com/v1/15/15/1675389600000/0BuvqJilW-a2JyvVdvw80Q/bySVWUGoI2IFHjRc8JeqqSE5ROhj99AuT7CLlXzz7A5anfwab4LDw8QYYloYwJmJ5SyCfc3SPAMQIRBLRyi6Pubm66NS81OafcAR8XnXMk8/n4Q0fBDszBqBwS4fZRVhcKAIonPtihdvQyPdu2UUPA0) checked checked ['Carbon', 'Energy', 'FoodAg', 'Nature'] ['Agriculture', 'Animals', 'Water'] ['Celo'] ['NFT', 'dApp']
22 Organization/0544651b16d40568dedeaa97f4b8bd9c69a5b5988bbd463cfe081219bba5a132 Pozzle Planet www.pozzleplanet.com 2022-10-24T06:51:00Z Pozzle Planet is a social app is a fun and inspiring way to make a positive impact in your everyday life. Earn POZ by joining and sharing planet positive actions with your friends via short-form video. On the outside, a social app where people generate impact by sharing videos and earning rewards. Underneath the hood, an impact-Protocol and ReFi yield generator that passively farms people’s impact into NFTs called Pozzles that contain impact tokens (Impact2) which represent measurable impact units from the real world. The Pozzle Planet mobile app sits at the intersection of 3 main growth trends in social, p2e gaming and DeFi, uniquely positioned as the first impact-2-earn social platform with tailwinds from increased individual and collective aspirations to be part of planet positive solutions. PP App Icon.png (https://v5.airtableusercontent.com/v1/15/15/1675389600000/WpQxVA6gWbRZIEpSBWAU3w/5tLAZtjfE3T-GJS1nifQBwzCSY5hbcjU2Bd8NZ8g3kkk2k9mxlhXRT2yDmXeG4csITwplZmpUctEp4-B1hk4p49bRZ2oTsmGl5T57kKNPTE/ZsQM_kMT8hWKj-FTz8egxkc5dJYjbOUqH78Mj0fNbDo) 3 checked checked 3 ['Investing', 'Carbon', 'Education', 'Nature', 'Other', 'SocialJustice'] ['Community', 'Local', 'Other'] ['Celo', 'Polygon'] ['NFT', 'Token', 'dApp']
23 Organization/056f40719cfa5cc97d6b969a454032af6e5a92d7f76e8a4fb0b60c41b8fa2814 Global Innovation Lab for Climate Finance (the Lab) http://www.climatefinancelab.org/ 2022-05-07T11:03:00Z Nations, businesses, and investors are working to move toward a low-carbon, climate resilient economy. Many of the measures underpinning this transition, including energy efficiency, renewable energy, sustainable transport, climate smart agriculture, and curbing deforestation, face specific barriers to attracting investment. By identifying, developing, and supporting transformative sustainable finance ideas, the Lab aims to drive billions of dollars of private investment to the low-carbon economy. checked ['Carbon'] ['Fundraising', 'Investing', 'Land', 'Renewables']
24 Organization/05bed03da8c15018650d4d7e13d0cc5a1c6c0b990bed5a92cbad818ff1826603 Carbovalent https://carbovalent.com/ 2022-12-22T13:34:00Z Carbovalent is a carbon credit network built on the Solana blockchain that aims to address the challenges of transparency, accessibility, liquidity, and composability in traditional off-chain carbon credit registries. Picture1_nxlmhoump.png (https://v5.airtableusercontent.com/v1/15/15/1675389600000/NKH1E0WNPC0G2SMM9mki7Q/6R2vCNu-M-CJG05WhhG25yu490EYRYQuGiUowPZW_o6iHP_EJbMQanZgRZNqojQ91RAzKfj14C3YkFyakQX2YvkOignsoU9nFaKUxtNcJ_Y/I4ItZRupaa8rBkbOBQd_hB9pYVB7WAx_GFCoesZ-7EI) 911 checked 911 ['Carbon'] ['Blockchain', 'Bridge', 'Marketplace', 'NFT', 'Token', 'dApp']

View File

@ -0,0 +1,60 @@
from numpy import NaN
from terminusdb_client import WOQLClient
from terminusdb_client.woqlschema import WOQLSchema
from terminusdb_client.woqldataframe import result_to_df
import pandas as pd
import json
# For Terminus X, use the following
# client = WOQLClient("https://cloud.terminusdb.com/<Your Team>/")
# client.connect(db="demo_workshop", team="<Your Team>", use_token=True)
client = WOQLClient("https://cloud.terminusdb.com/Myseelia/")
client.connect(db="murmurations", team="Myseelia", use_token=True)
orgs_raw = client.query_document({"@type": "person"})
# team_marketing_raw = client.query_document({"@type": "Employee", "team": "marketing"})
df = result_to_df(orgs_raw)
# df_selected = df[0:20]
# df = df.head(100)
#df.to_csv('df.csv', index=False)
entities = []
relations = []
for i, row in df.iterrows():
# Create an entity for the person
entity = {'id': row['Document id'], 'label': row['name'], 'type': 'person'}
# Add any additional properties that exist for the person
for prop in ['description', 'image', 'locality', 'primary_url']:
if prop in row and not pd.isna(row[prop]):
entity[prop] = row[prop]
entities.append(entity)
# Create a relation for each knows relationship
if isinstance(row['LI'], list) and len(row['LI']) > 0 and not pd.isna(row['LI'][0]):
for j in row['LI']:
relation = {'source': row['Document id'], 'target': j, 'type': 'LI'}
relations.append(relation)
elif not pd.isna(row['LI']):
relation = {'source': row['Document id'], 'target': row['LI'], 'type': 'LI'}
relations.append(relation)
# Create a relation for each vouches_for relationship
if isinstance(row['vouches_for'], list) and len(row['vouches_for']) > 0 and not pd.isna(row['vouches_for'][0]):
for j in row['vouches_for']:
relation = {'source': row['Document id'], 'target': j, 'type': 'vouches_for'}
relations.append(relation)
elif not pd.isna(row['vouches_for']):
relation = {'source': row['Document id'], 'target': row['vouches_for'], 'type': 'vouches_for'}
relations.append(relation)
knowledgeGraphJson = {
'entities': entities,
'relations': relations
}
with open("knowledge_graph.json", "w") as f:
json.dump(knowledgeGraphJson, f)

File diff suppressed because one or more lines are too long

View File

@ -25,6 +25,11 @@
href: '/explore/',
icon: PhotoGallery
},
{
label: 'CTA',
href: '/cta/',
icon: PhotoGallery
},
{
label: 'About This Template',
href: '/about/',

View File

@ -0,0 +1,7 @@
<script lang="ts">
import Explore from '$components/CTA/CTA.svelte'
</script>
<Explore />

View File

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="60" height="66" fill="none">
<path
stroke="#171717"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M14.167 45.667C7.17 45.667 1.5 39.996 1.5 33c0-6.039 4.226-11.09 9.882-12.36A15.89 15.89 0 0 1 11 17.167c0-8.745 7.089-15.834 15.833-15.834 7.662 0 14.052 5.441 15.518 12.67.105-.002.21-.003.316-.003C51.41 14 58.5 21.089 58.5 29.833c0 7.66-5.44 14.05-12.667 15.517M39.5 36.167l-9.5-9.5m0 0-9.5 9.5m9.5-9.5v38"
/>
</svg>

After

Width:  |  Height:  |  Size: 510 B

View File

@ -0,0 +1,24 @@
<script lang="ts">
import type { Image } from '$routes/gallery/lib/gallery'
export let image: Image
export let openModal: (image: Image) => void
const handleOpenModal = () => openModal(image)
</script>
<div
class="relative group w-full aspect-[22/23] rounded-lg border-2 border-transparent hover:border-base-content box-border overflow-hidden transition-colors ease-in"
on:click={handleOpenModal}
>
<div
class="flex items-center justify-center absolute z-10 top-0 right-0 bottom-0 left-0 bg-[#00000035] opacity-0 group-hover:opacity-100 transition-opacity ease-in"
/>
<div class="relative pb-[105%]">
<img
class="absolute block object-cover object-center w-full h-full"
alt={`Gallery Image: ${image.name}`}
src={image.src}
/>
</div>
</div>

View File

@ -0,0 +1,174 @@
<script lang="ts">
import { onDestroy } from 'svelte'
import { onMount } from 'svelte'
import TerminusClient from "@terminusdb/terminusdb-client"
import { filesystemStore, sessionStore } from '$src/stores'
import { AREAS, galleryStore } from '$routes/gallery/stores'
import { getImagesFromWNFS, type Image } from '$routes/gallery/lib/gallery'
import FileUploadCard from '$routes/gallery/components/upload/FileUploadCard.svelte'
import ImageCard from '$routes/gallery/components/imageGallery/ImageCard.svelte'
import ImageModal from '$routes/gallery/components/imageGallery/ImageModal.svelte'
const client = new TerminusClient.WOQLClient(
"https://cloud.terminusdb.com/Myseelia",{
user:"zaldarren@gmail.com",
organization:"Myseelia",
db: "Myseelia",
token: "dGVybWludXNkYjovLy9kYXRhL2tleXNfYXBpLzg5OTY0ZGI5OWFlYjQ1Zjc5OGM5ZTRiZWI2MzExOGJhZjhiOWRiOWNlOTJiNmU2NGI0NDEzZjIzNDFmOGVkMjc=_869e9bd2465ad84126151962994fcfa22d4b7ec9375edf16b4182e7f36e4b2b820075ba22e78f629e0691eddbeae6998a6504d5ce287aa1df2602cb556b58e1730b0b93feb0e9304"
}
);
let username = $sessionStore.username;
let bioregion = '';
let ecozone = '';
let affiliatedOrganizations = "Organization/8c8368b55dc80f18ba254771701f6d1bc79a3a90f127c28b3145a2c2204e97ce";
let givenName = '';
let hasCredential = {};
onMount(async () => {
await client.connect()
const schema = await client.getSchema("myseelia", "main")
});
/**
* Open the ImageModal and pass it the selected `image` from the gallery
* @param image
*/
let selectedImage: Image
const setSelectedImage: (image: Image) => void = image =>
(selectedImage = image)
const clearSelectedImage = () => (selectedImage = null)
// If galleryStore.selectedArea changes from private to public, re-run getImagesFromWNFS
let selectedArea = null
const unsubscribeGalleryStore = galleryStore.subscribe(async updatedStore => {
// Get initial selectedArea
if (!selectedArea) {
selectedArea = updatedStore.selectedArea
}
if (selectedArea !== updatedStore.selectedArea) {
selectedArea = updatedStore.selectedArea
await getImagesFromWNFS()
}
})
// Once the user has been authed, fetch the images from their file system
let imagesFetched = false
const unsubscribeSessionStore = sessionStore.subscribe((newState) => {
if (newState.authed && $filesystemStore && !imagesFetched) {
imagesFetched = true
// Get images from the user's public WNFS
getImagesFromWNFS()
}
})
onDestroy(() => {
unsubscribeGalleryStore()
unsubscribeSessionStore()
})
function handleSubmit() {
makeConnection();
}
export async function makeConnection(){
try{
const entryObj =
{
"@type" : "Person",
"userName" : username,
"givenName" : givenName,
"bioregion": bioregion,
"ecozone": ecozone,
"hasCredential": hasCredential,
"affiliation": affiliatedOrganizations
};
if (username == entryObj.userName){
await client.updateDocument(entryObj)
} else{
await client.addDocument(entryObj);
}
const entries2 = await client.getDocument({"graph_type":"instance","as_list":true,"type":"Person"})
console.log(entries2);
}catch(err){
console.error(err.message)
}
}
</script>
<style>
form {
display: grid;
grid-template-columns: 1fr 3fr;
}
label {
text-align: left;
}
input{
background-color: rgb(255, 255, 255);
border-radius: 4px;
}
button{
background-color: #4CAF50; /* Green */
padding: 12px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
</style>
<section class="overflow-hidden text-gray-700">
<div class="pt-8 p-6 md:p-8 mx-auto">
<div
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:lg:grid-cols-6 gap-4"
>
{#each $galleryStore.selectedArea === AREAS.PRIVATE ? $galleryStore.privateImages : $galleryStore.publicImages as image}{/each}
</div>
<form on:submit|preventDefault={handleSubmit}>
<label class="label dark:text-white">
Given Name:
<input class="input text-white dark:text-black" type="text" bind:value={givenName} />
</label>
<br />
<label class="label dark:text-white">
Bioregion:
<input class="input text-white dark:text-black" type="text" bind:value={bioregion} />
</label>
<br />
<label class="label dark:text-white">
Ecozone:
<input class="input text-white dark:text-black" type="text" bind:value={ecozone} />
</label >
<br />
<label class="label dark:text-white">
Has Credential:
<input class="input text-white dark:text-black" type="text" bind:value={hasCredential} />
</label >
<br />
<label class="label dark:text-white">
Affiliated organizations:
<input class="input text-white dark:text-black" type="text" bind:value={affiliatedOrganizations}/>
</label>
<br />
<button class="bg-blue-500 text-white dark:text-black" type="submit">Submit</button>
</form>
</div>
{#if selectedImage}
<ImageModal
image={selectedImage}
isModalOpen={!!selectedImage}
on:close={clearSelectedImage}
/>
{/if}
</section>

View File

@ -0,0 +1,162 @@
<script lang="ts">
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
import { ipfsGatewayUrl } from '$lib/app-info';
import { galleryStore } from '$routes/gallery/stores'
import { deleteImageFromWNFS, type Gallery, type Image } from '$routes/gallery/lib/gallery'
export let image: Image
export let isModalOpen: boolean = false
let previousImage: Image | undefined
let nextImage: Image | undefined
let showPreviousArrow: boolean
let showNextArrow: boolean
let gallery: Gallery
const dispatch = createEventDispatcher()
const unsubcribe = galleryStore.subscribe(newState => (gallery = newState))
/**
* Close the modal, clear the image state vars, set `isModalOpen` to false
* and dispatch the close event to clear the image from the parent's state
*/
const handleCloseModal: () => void = () => {
image = null
previousImage = null
nextImage = null
isModalOpen = false
dispatch('close')
}
/**
* Delete an image from the user's WNFS
*/
const handleDeleteImage: () => Promise<void> = async () => {
await deleteImageFromWNFS(image.name)
handleCloseModal()
}
/**
* Set the previous and next images to be toggled to when the arrows are clicked
*/
const setCarouselState = () => {
const imageList = image.private
? gallery.privateImages
: gallery.publicImages
const currentIndex = imageList.findIndex(val => val.cid === image.cid)
previousImage =
imageList[currentIndex - 1] ?? imageList[imageList.length - 1]
nextImage = imageList[currentIndex + 1] ?? imageList[0]
showPreviousArrow = imageList.length > 1 && !!previousImage
showNextArrow = imageList.length > 1 && !!nextImage
}
/**
* Load the correct image when a user clicks the Next or Previous arrows
* @param direction
*/
const handleNextOrPrevImage: (
direction: 'next' | 'prev'
) => void = direction => {
image = direction === 'prev' ? previousImage : nextImage
setCarouselState()
}
/**
* Detect `Escape` key presses to close the modal or `ArrowRight`/`ArrowLeft`
* presses to navigate the carousel
* @param event
*/
const handleKeyDown: (event: KeyboardEvent) => void = event => {
if (event.key === 'Escape') handleCloseModal()
if (showNextArrow && event.key === 'ArrowRight')
handleNextOrPrevImage('next')
if (showPreviousArrow && event.key === 'ArrowLeft')
handleNextOrPrevImage('prev')
}
onMount(() => {
setCarouselState()
})
// Unsubscribe from galleryStore updates
onDestroy(unsubcribe)
</script>
<svelte:window on:keydown={handleKeyDown} />
{#if !!image}
<!-- bind:checked can't be set to !!image, so we need to set it to a boolean(casting image as a boolean throws a svelte error, so we're using isModalOpen) -->
<input
type="checkbox"
id={`image-modal-${image.cid}`}
class="modal-toggle"
bind:checked={isModalOpen}
/>
<label
for={`image-modal-${image.cid}`}
class="modal cursor-pointer z-50"
on:click|self={handleCloseModal}
>
<div class="modal-box relative text-center text-base-content">
<label
for={`image-modal-${image.cid}`}
class="btn btn-xs btn-circle absolute right-2 top-2"
on:click={handleCloseModal}
>
</label>
<div>
<h3 class="mb-7 text-lg break-all">{image.name}</h3>
<div class="relative">
{#if showPreviousArrow}
<button
class="absolute top-1/2 -left-[25px] -translate-y-1/2 inline-block text-center text-[40px]"
on:click={() => handleNextOrPrevImage('prev')}
>
&#8249;
</button>
{/if}
<img
class="block object-cover object-center border-2 border-base-content w-full h-full mb-4 rounded-[1rem]"
alt={`Image: ${image.name}`}
src={image.src}
/>
{#if showNextArrow}
<button
class="absolute top-1/2 -right-[25px] -translate-y-1/2 inline-block text-center text-[40px]"
on:click={() => handleNextOrPrevImage('next')}
>
&#8250;
</button>
{/if}
</div>
<div class="flex flex-col items-center justify-center">
<a
href={`https://ipfs.${ipfsGatewayUrl}/ipfs/${image.cid}/userland`}
target="_blank"
class="underline mb-4 hover:text-slate-500"
>
View on IPFS
</a>
<p class="mb-4">
Created at {new Date(image.ctime).toDateString()}
</p>
<div class="flex items-center justify-between gap-4">
<a href={image.src} download={image.name} class="btn btn-primary">
Download Image
</a>
<button class="btn btn-outline" on:click={handleDeleteImage}>
Delete Image
</button>
</div>
</div>
</div>
</div>
</label>
{/if}

View File

@ -0,0 +1,72 @@
<script lang="ts">
import TerminusClient from "@terminusdb/terminusdb-client";
import { onDestroy } from 'svelte'
import { filesystemStore, sessionStore } from '$src/stores'
import { AREAS, galleryStore } from '$routes/gallery/stores'
import { getImagesFromWNFS, type Image } from '$routes/gallery/lib/gallery'
import FileUploadCard from '$routes/gallery/components/upload/FileUploadCard.svelte'
import ImageCard from '$routes/gallery/components/imageGallery/ImageCard.svelte'
import ImageModal from '$routes/gallery/components/imageGallery/ImageModal.svelte'
/**
* Open the ImageModal and pass it the selected `image` from the gallery
* @param image
*/
let selectedImage: Image
const setSelectedImage: (image: Image) => void = image =>
(selectedImage = image)
// If galleryStore.selectedArea changes from private to public, re-run getImagesFromWNFS
let selectedArea = null
const unsubscribeGalleryStore = galleryStore.subscribe(async updatedStore => {
// Get initial selectedArea
if (!selectedArea) {
selectedArea = updatedStore.selectedArea
}
if (selectedArea !== updatedStore.selectedArea) {
selectedArea = updatedStore.selectedArea
}
})
// Once the user has been authed, fetch the images from their file system
let imagesFetched = false
const unsubscribeSessionStore = sessionStore.subscribe((newState) => {
if (newState.authed && $filesystemStore && !imagesFetched) {
imagesFetched = true
// Get images from the user's public WNFS
getImagesFromWNFS()
}
})
onDestroy(() => {
unsubscribeGalleryStore()
unsubscribeSessionStore()
})
</script>
<section class="overflow-hidden text-gray-700">
<div class="pt-8 p-6 md:p-8 mx-auto">
<div
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:lg:grid-cols-6 gap-4"
>
<FileUploadCard />
{#each $galleryStore.selectedArea === AREAS.PRIVATE ? $galleryStore.privateImages : $galleryStore.publicImages as image}
<ImageCard {image} openModal={setSelectedImage} />
{/each}
</div>
</div>
{#if selectedImage}
<ImageModal
image={selectedImage}
isModalOpen={!!selectedImage}
on:close={clearSelectedImage}
/>
{/if}
</section>

View File

@ -0,0 +1,69 @@
<script lang="ts">
import {
getImagesFromWNFS,
uploadImageToWNFS
} from '$routes/gallery/lib/gallery'
import { addNotification } from '$lib/notifications'
/**
* Detect when a user drags a file in or out of the dropzone to change the styles
*/
let isDragging = false
const handleDragEnter: () => void = () => (isDragging = true)
const handleDragLeave: () => void = () => (isDragging = false)
/**
* Process files being dropped in the drop zone and ensure they are images
* @param event
*/
const handleDrop: (event: DragEvent) => Promise<void> = async event => {
// Prevent default behavior (Prevent file from being opened)
event.preventDefault()
const files = Array.from(event.dataTransfer.items)
// Iterate over the dropped files and upload them to WNFS
await Promise.all(
files.map(async item => {
if (item.kind === 'file') {
const file: File = item.getAsFile()
// If the dropped files aren't images, we don't want them!
if (!file.type.match('image/*')) {
addNotification('Please upload images only', 'error')
console.error('Please upload images only')
} else {
await uploadImageToWNFS(file)
}
}
})
)
// Refetch images and update galleryStore
await getImagesFromWNFS()
// Disable isDragging state
isDragging = false
}
/**
* This is needed to prevent the default behaviour of the file opening in browser
* when it is dropped
* @param event
*/
const handleDragOver: (event: DragEvent) => void = event =>
event.preventDefault()
</script>
<label
on:drop={handleDrop}
on:dragover={handleDragOver}
on:dragenter={handleDragEnter}
on:dragleave={handleDragLeave}
for="dropzone-file"
class="block w-full min-h-[calc(100vh-190px)] rounded-lg border-2 border-solid border-base-content transition ease-in cursor-pointer {isDragging
? 'border-dashed !border-orange-700 bg-orange-50'
: ''}"
>
<slot />
</label>

View File

@ -0,0 +1,40 @@
<script lang="ts">
import { galleryStore } from '$routes/gallery/stores'
import { handleFileInput } from '$routes/gallery/lib/gallery'
import FileUploadIcon from '$routes/gallery/components/icons/FileUploadIcon.svelte'
// Handle files uploaded directly through the file input
let files: FileList
$: if (files) {
handleFileInput(files)
}
</script>
<label
for="upload-file"
class="group btn !p-0 !h-auto flex flex-col justify-center items-center aspect-[22/23] object-cover rounded-lg shadow-orange hover:border-neutral-50 overflow-hidden transition-colors ease-in bg-base-100 border-2 box-content border-neutral cursor-pointer text-neutral bg-gradient-to-r from-orange-600 to-orange-300"
>
{#if $galleryStore.loading}
<div class="flex justify-center items-center p-12">
<div
class="loader ease-linear rounded-full border-4 border-t-4 border-t-orange-300 border-neutral h-16 w-16 animate-spin"
/>
</div>
{:else}
<div class="flex flex-col justify-center items-center pt-5 pb-6">
<FileUploadIcon />
<p class="mt-4 mb-2 text-sm">
<span class="font-bold text-sm">Upload a photo</span>
</p>
<p class="text-xxs">SVG, PNG, JPG or GIF</p>
</div>
<input
bind:files
id="upload-file"
type="file"
multiple
accept="image/*"
class="hidden"
/>
{/if}
</label>

92
src/routes/cta/data.json Normal file
View File

@ -0,0 +1,92 @@
{
"entities": [{
"label": "Organization",
"title": "Neuralink"
}, {
"label": "Organization",
"title": "SpaceX"
}, {
"label": "Organization",
"title": "Pretoria"
}, {
"label": "Organization",
"title": "The Boring Company"
}, {
"label": "Organization",
"title": "University of Pretoria"
}, {
"label": "Organization",
"title": "Stanford University"
}, {
"label": "Person",
"title": "Jeff Bezos"
}, {
"label": "Organization",
"title": "University of Pennsylvania"
}, {
"label": "Person",
"title": "Kimbal Musk"
}, {
"label": "Organization",
"title": "Tesla, Inc."
}, {
"label": "Person",
"title": "Elon Musk"
}],
"relations": [{
"source": "Elon Musk",
"target": "Neuralink"
}, {
"source": "Tesla, Inc.",
"target": "Elon Musk",
"type": "owned by"
}, {
"source": "Elon Musk",
"target": "University of Pennsylvania",
"type": "residence"
}, {
"source": "Elon Musk",
"target": "Tesla, Inc.",
"type": "owned by"
}, {
"source": "The Boring Company",
"target": "Tesla, Inc.",
"type": "owned by"
}, {
"source": "Elon Musk",
"target": "Kimbal Musk",
"type": "sibling"
}, {
"source": "University of Pennsylvania",
"target": "Elon Musk",
"type": "residence"
}, {
"source": "The Boring Company",
"target": "Neuralink",
"type": "subsidiary"
}, {
"source": "Elon Musk",
"target": "University of Pretoria",
"type": "work location"
}, {
"source": "The Boring Company",
"target": "Elon Musk",
"type": "owned by"
}, {
"source": "Kimbal Musk",
"target": "Elon Musk",
"type": "sibling"
}, {
"source": "Neuralink",
"target": "Elon Musk",
"type": "owned by"
}, {
"source": "Elon Musk",
"target": "The Boring Company",
"type": "owned by"
}, {
"source": "Elon Musk",
"target": "University of Pennsylvania",
"type": "work location"
}]
}

View File

@ -0,0 +1,209 @@
import { get as getStore } from 'svelte/store'
import * as wn from 'webnative'
import * as uint8arrays from 'uint8arrays'
import type { CID } from 'multiformats/cid'
import type { PuttableUnixTree, File as WNFile } from 'webnative/fs/types'
import type { Metadata } from 'webnative/fs/metadata'
import { filesystemStore } from '$src/stores'
import { AREAS, galleryStore } from '$routes/gallery/stores'
import { addNotification } from '$lib/notifications'
export type Image = {
cid: string
ctime: number
name: string
private: boolean
size: number
src: string
}
export type Gallery = {
publicImages: Image[] | null
privateImages: Image[] | null
selectedArea: AREAS
loading: boolean
}
interface GalleryFile extends PuttableUnixTree, WNFile {
cid: CID
content: Uint8Array
header: {
content: Uint8Array
metadata: Metadata
}
}
type Link = {
size: number
}
export const GALLERY_DIRS = {
[AREAS.PUBLIC]: ['public', 'gallery'],
[AREAS.PRIVATE]: ['private', 'gallery']
}
const FILE_SIZE_LIMIT = 5
/**
* Get images from the user's WNFS and construct the `src` value for the images
*/
export const getImagesFromWNFS: () => Promise<void> = async () => {
try {
// Set loading: true on the galleryStore
galleryStore.update(store => ({ ...store, loading: true }))
const { selectedArea } = getStore(galleryStore)
const isPrivate = selectedArea === AREAS.PRIVATE
const fs = getStore(filesystemStore)
// Set path to either private or public gallery dir
const path = wn.path.directory(...GALLERY_DIRS[selectedArea])
// Get list of links for files in the gallery dir
const links = await fs.ls(path)
const images = await Promise.all(
Object.entries(links).map(async ([name]) => {
const file = await fs.get(
wn.path.file(...GALLERY_DIRS[selectedArea], `${name}`)
)
// The CID for private files is currently located in `file.header.content`,
// whereas the CID for public files is located in `file.cid`
const cid = isPrivate
? (file as GalleryFile).header.content.toString()
: (file as GalleryFile).cid.toString()
// Create a base64 string to use as the image `src`
const src = `data:image/jpeg;base64, ${uint8arrays.toString(
(file as GalleryFile).content,
'base64'
)}`
return {
cid,
ctime: (file as GalleryFile).header.metadata.unixMeta.ctime,
name,
private: isPrivate,
size: (links[name] as Link).size,
src
}
})
)
// Sort images by ctime(created at date)
// NOTE: this will eventually be controlled via the UI
images.sort((a, b) => b.ctime - a.ctime)
// Push images to the galleryStore
galleryStore.update(store => ({
...store,
...(isPrivate
? {
privateImages: images
}
: {
publicImages: images
}),
loading: false
}))
} catch (error) {
console.error(error)
galleryStore.update(store => ({
...store,
loading: false
}))
}
}
/**
* Upload an image to the user's private or public WNFS
* @param image
*/
export const uploadImageToWNFS: (
image: File
) => Promise<void> = async image => {
try {
const { selectedArea } = getStore(galleryStore)
const fs = getStore(filesystemStore)
// Reject files over 5MB
const imageSizeInMB = image.size / (1024 * 1024)
if (imageSizeInMB > FILE_SIZE_LIMIT) {
throw new Error('Image can be no larger than 5MB')
}
// Reject the upload if the image already exists in the directory
const imageExists = await fs.exists(
wn.path.file(...GALLERY_DIRS[selectedArea], image.name)
)
if (imageExists) {
throw new Error(`${image.name} image already exists`)
}
// Create a sub directory and add some content
await fs.write(
wn.path.file(...GALLERY_DIRS[selectedArea], image.name),
image
)
// Announce the changes to the server
await fs.publish()
addNotification(`${image.name} image has been published`, 'success')
} catch (error) {
addNotification(error.message, 'error')
console.error(error)
}
}
/**
* Delete an image from the user's private or public WNFS
* @param name
*/
export const deleteImageFromWNFS: (
name: string
) => Promise<void> = async name => {
try {
const { selectedArea } = getStore(galleryStore)
const fs = getStore(filesystemStore)
const imageExists = await fs.exists(
wn.path.file(...GALLERY_DIRS[selectedArea], name)
)
if (imageExists) {
// Remove images from server
await fs.rm(wn.path.file(...GALLERY_DIRS[selectedArea], name))
// Announce the changes to the server
await fs.publish()
addNotification(`${name} image has been deleted`, 'success')
// Refetch images and update galleryStore
await getImagesFromWNFS()
} else {
throw new Error(`${name} image has already been deleted`)
}
} catch (error) {
addNotification(error.message, 'error')
console.error(error)
}
}
/**
* Handle uploads made by interacting with the file input directly
*/
export const handleFileInput: (
files: FileList
) => Promise<void> = async files => {
await Promise.all(
Array.from(files).map(async file => {
await uploadImageToWNFS(file)
})
)
// Refetch images and update galleryStore
await getImagesFromWNFS()
}