Commit 3db018f1 authored by ivan minutillo's avatar ivan minutillo

initial commit

parent d5277545
/target
/classes
/checkouts
profiles.clj
pom.xml
pom.xml.asc
*.jar
*.class
/.lein-*
/.nrepl-port
.hgignore
.hg/
# Change Log
All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/).
## [Unreleased]
### Changed
- Add a new arity to `make-widget-async` to provide a different widget shape.
## [0.1.1] - 2019-09-05
### Changed
- Documentation on how to make the widgets.
### Removed
- `make-widget-sync` - we're all async, all the time.
### Fixed
- Fixed widget maker to keep working when daylight savings switches over.
## 0.1.0 - 2019-09-05
### Added
- Files from the new template.
- Widget maker public API - `make-widget-sync`.
[Unreleased]: https://github.com/your-name/vfprocess/compare/0.1.1...HEAD
[0.1.1]: https://github.com/your-name/vfprocess/compare/0.1.0...0.1.1
This diff is collapsed.
# vf-clojure-experiment
# vfprocess
Experimental demo for ValueFlows written in clojure using a relationship db, with graphql api. Initial scope is the observation layer: incoming value flows, resource distrubution.
A Clojure library designed to ... well, that part is up to you.
## Usage
FIXME
## License
Copyright © 2019 FIXME
This program and the accompanying materials are made available under the
terms of the Eclipse Public License 2.0 which is available at
http://www.eclipse.org/legal/epl-2.0.
This Source Code may also be made available under the following Secondary
Licenses when the conditions for such availability set forth in the Eclipse
Public License, v. 2.0 are satisfied: GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or (at your
option) any later version, with the GNU Classpath Exception which is available
at https://www.gnu.org/software/classpath/license.html.
File added
(ns user
(:require
[vfprocess.schema :as s]
[com.walmartlabs.lacinia :as lacinia]
[com.walmartlabs.lacinia.pedestal :as lp]
[io.pedestal.http :as http]
[clojure.java.browse :refer [browse-url]]
[clojure.walk :as walk])
(:import (clojure.lang IPersistentMap)))
(def schema (s/load-schema))
(defn simplify
"Converts all ordered maps nested within the map into standard hash maps, and
sequences into vectors, which makes for easier constants in the tests, and eliminates ordering problems."
[m]
(walk/postwalk
(fn [node]
(cond
(instance? IPersistentMap node)
(into {} node)
(seq? node)
(vec node)
:else
node))
m))
(defn q
[query-string]
(-> (lacinia/execute schema query-string nil nil)
simplify))
(defonce server nil)
(defn start-server
[_]
(let [server (-> schema
(lp/service-map {:graphiql true})
http/create-server
http/start)]
(browse-url "http://localhost:8888/")
server))
(defn stop-server
[server]
(http/stop server)
nil)
(defn start
[]
(alter-var-root #'server start-server)
:started)
(defn stop
[]
(alter-var-root #'server stop-server)
:stopped)
# Introduction to vfprocess
TODO: write [great documentation](http://jacobian.org/writing/what-to-write/)
File added
(defproject vfprocess "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
:url "https://www.eclipse.org/legal/epl-2.0/"}
:dependencies [[org.clojure/clojure "1.10.0"]
[com.walmartlabs/lacinia-pedestal "0.5.0"]
[io.aviso/logging "0.2.0"]
[org.clojure/java.jdbc "0.7.8"]
[com.stuartsierra/component "0.3.2"]
[camel-snake-kebab "0.4.0"]
[seancorfield/next.jdbc "1.0.7"]
[org.xerial/sqlite-jdbc "3.23.1"]
[com.walmartlabs/lacinia "0.34.0"]]
:repl-options {;; If nREPL takes too long to load it may timeout,
;; increase this to wait longer before timing out.
;; Defaults to 30000 (30 seconds)
:timeout 120000})
{:input-objects {:EconomicEventCreateParams
{:fields
{:note {:type String}
:hasPointInTime {:type String}
:provider {:type Int}
:receiver {:type Int}
:action {:type Int}
:inputOf {:type Int}
:outputOf {:type Int}
:resourceQuantityNumericValue {:type Float}
:resourceQuantityUnit {:type Int}
:effortQuantityNumericValue {:type Float}
:effortQuantityUnit {:type Int}
:resourceInventoriedAs {:type Int}
:resourceConformsTo {:type Int}}}}
:objects
{:Process
{:description "An activity that changes inputs into outputs. It could transform or transport economic resource(s)."
:fields
{:id {:type Int}
:name {:description "An informal or formal textual identifier for an item. Does not imply uniqueness."
:type (non-null String)}
:note {:type String
:description "A textual description or comment."}
:before {:type String
:description "The economic event occurred prior to this date/time."}
:finished {:type Boolean
:description "The process is complete or not. This is irrespective of if the original goal has been met, and indicates that no more will be done."}}}
:Action {:description "An informal or formal textual identifier for an object. Does not imply uniqueness."
:fields {:id {:type (non-null Int)}
:name {:type (non-null String)}}}
:Unit {:description "Defines a unit of measurement, along with its display symbol."
:fields {:id {:type (non-null Int)}
:name {:type String}}}
:Agent {:description "A person or a group"
:fields {:id {:type (non-null Int)}
:name {:type String}
:note {:type String}
:type {:type String}}}
:ResourceSpecification {:description "An informal or formal textual identifier for an object. Does not imply uniqueness."
:fields {:id {:type (non-null Int)}
:name {:type String}
:note {:type String}}}
:EconomicResource {:description "References one or more concepts in a common taxonomy or other classification scheme for purposes of categorization or grouping."
:fields {:id {:type (non-null Int)}
:name {:type String}
:valueflows {:type (list IncomingValueflows)}
:accountingQuantityNumericValue {:type Float}
:accountingQuantityUnit {:type Unit}
:onhandQuantityUnit {:type Unit}
:onhandQuantityNumericValue {:type Float}
:unitOfEffort {:type Unit}
:note {:type String}
:conformsTo {:type ResourceSpecification}}}
:EconomicEvent {:description "Relates an economic event to a verb, such as consume, produce, work, improve, etc."
:fields {:id {:type (non-null Int)}
:note {:type String}
:hasPointInTime {:type String}
:provider {:type Agent}
:receiver {:type Agent}
:action {:type Action}
:inputOf {:type Process}
:outputOf {:type Process}
:resourceQuantityNumericValue {:type Float}
:resourcequantityunit {:type Unit}
:effortQuantityNumericValue {:type Float}
:effortQuantityUnit {:type Unit}
:resourceInventoriedAs {:type EconomicResource}
:resourceConformsTo {:type ResourceSpecification}}}
:IncomingValueflows {:description "Relates the whole valueflows starting from a resource"
:fields {:text {:type String}
:type {:type String}}}}
:queries {; Agent module
:agent {:type :Agent
:description "Retrieve the agent by its ID, if it exists"
:args {:id {:type ID}}
:resolve :query/agent}
:allAgents {:type (list :Agent)
:description "Retrieve all agents"
:args {}
:resolve :query/allAgents}
; Observation layer
:economicEvent {:type :EconomicEvent
:description "Retrieve the economic event by its ID, if it exists."
:args {:id {:type ID}}
:resolve :query/economicEvent}
:allEconomicEvents {:type (list :EconomicEvent)
:description "Retrieve all economic events"
:args {}
:resolve :query/allEconomicEvents}
:economicResource {:type :EconomicResource
:description "Retrieve the economic resource by its ID, if it exists."
:args {:id {:type ID}}
:resolve :query/economicResource}
:allEconomicResource {:type (list :EconomicResource)
:description "Retrieve all economic resources"
:args {}
:resolve :query/allEconomicResources}
:process {:type :Process
:description "Retrieve the process by its ID, if it exists."
:args {:id {:type ID}}
:resolve :query/process}
:allProcesses {:type (list :Process)
:description "Retrieve all processes"
:args {}
:resolve :query/allProcesses}
:resourceSpecification {:type :ResourceSpecification
:description "Retrieve the resource specification by its ID, if it exists."
:args {:id {:type ID}}
:resolve :query/resourceSpecification}
:allResourceSpecification {:type (list :ResourceSpecification)
:description "Retrieve all resource specification"
:args {}
:resolve :query/allResourceSpecification}
:action {:type :Action
:description "Retrieve the action by its ID, if it exists."
:args {:id {:type ID}}
:resolve :query/action}
:allActions {:type (list :Action)
:description "Retrieve all actions"
:args {}
:resolve :query/allActions}
:unit {:type :Unit
:description "Retrieve the unit by its ID, if it exists."
:args {:id {:type ID}}
:resolve :query/unit}
:allUnits {:type (list :Unit)
:description "Retrieve all units"
:args {}
:resolve :query/allUnits}}
:mutations
{:createEconomicEvent
{:type :EconomicEvent
:description "Add a new event with the possibility to create or not a new resource"
:args {:event {:type :EconomicEventCreateParams}}
:resolve :mutation/createEconomicEvent}}}
(ns vfprocess.core
(:require [vfprocess.db.traversal :refer [incoming-vf-dfs]]
[vfprocess.db.queries :refer [db
queryEconomicResources
queryProcesses
queryAgents
queryEconomicEvents
queryResourceSpecifications
queryUnits
queryActions]])
(:gen-class))
(defn find-economicEvent-by-id [id]
(let [event (queryEconomicEvents id)
effortQuantityUnit (queryUnits (:unit event))
action (queryActions (:action event))
provider (queryAgents (:provider event))
receiver (queryAgents (:receiver event))
resourceInventoriedAs (queryEconomicResources (:resourceInventoriedAs event))
resourceConformsTo (queryResourceSpecifications (:resourceConformsTo event))
inputOf (queryProcesses (:inputOf event))
outputOf (queryProcesses (:outputOf event))]
(-> event
(merge {:inputOf inputOf})
(merge {:outputOf outputOf})
(merge {:effortQuantityUnit effortQuantityUnit})
(merge {:action action})
(merge {:provider provider})
(merge {:receiver receiver})
(merge {:resourceConformsTo resourceConformsTo})
(merge {:resourceInventoriedAs resourceInventoriedAs}))))
(defn find-economicResource-by-id [id]
(let [vf (incoming-vf-dfs {:type (str "economicResource_" id)})
resource (queryEconomicResources id)]
(merge resource
{:valueflows vf}
)))
(ns vfprocess.db.queries
(:require [next.jdbc :as jdbc]
[next.jdbc.sql :as sql]
[next.jdbc.optional :as opt]
[next.jdbc.specs :as specs]
[camel-snake-kebab.core :as csk]))
(specs/instrument)
(def ds
{:dbtype "sqlite"
:host :none
:dbname "db/database.db"})
(def db (jdbc/get-datasource ds))
; Query EconomicResource by id or returns all the resources
(defn queryEconomicResources
([id]
(jdbc/execute-one!
db
["select * from EconomicResource where id = ?" id]
{:builder-fn opt/as-unqualified-maps})
)
([]
(jdbc/execute! db
["select * from EconomicResource"]
{:builder-fn opt/as-unqualified-maps})))
; Query Process by id or returns all the processes
(defn queryProcesses
([id]
(jdbc/execute-one! db
["select * from Process where id = ?" id]
{:builder-fn opt/as-unqualified-maps}
))
([]
(jdbc/execute! db
["select * from Process"]
{:builder-fn opt/as-unqualified-maps}
)))
; Query Agent by id or returns all the agents
(defn queryAgents
([id]
(jdbc/execute-one! db
["select * from Agent where id = ?" id]
{:builder-fn opt/as-unqualified-maps}
))
([]
(jdbc/execute! db
["select * from Agent"]
{:builder-fn opt/as-unqualified-maps})))
(defn queryEconomicEvents
([id]
(jdbc/execute-one! db
["select * from EconomicEvent where id = ?" id]
{:builder-fn opt/as-unqualified-maps}
))
([]
(jdbc/execute! db
["select * from EconomicEvent"]
{:builder-fn opt/as-unqualified-maps}
)))
(defn queryResourceSpecifications
([id]
(jdbc/execute-one! db
["select * from ResourceSpecification where id = ?" id]
{:builder-fn opt/as-unqualified-maps}
))
([]
(jdbc/execute! db
["select * from ResourceSpecification"]
{:builder-fn opt/as-unqualified-maps}
)))
(defn queryActions
([id]
(jdbc/execute-one! db
["select * from Action where id = ?" id]
{:builder-fn opt/as-unqualified-maps}
))
([]
(jdbc/execute! db
["select * from Action"]
{:builder-fn opt/as-unqualified-maps}
)))
(defn queryUnits
([id]
(jdbc/execute-one! db
["select * from Unit where id = ?" id]
{:builder-fn opt/as-unqualified-maps}
))
([]
(jdbc/execute! db
["select * from Unit"]
{:builder-fn opt/as-unqualified-maps}
)))
(defn createEconomicEvent
[event]
(sql/insert! db
:EconomicEvent
{:action (:action event)
:resourceQuantityNumericValue (:resourceQuantityNumericValue event)
:resourceQuantityUnit (:resourceQuantityUnit event)
:effortQuantityNumericValue (:effortQuantityNumericValue event)
:effortQuantityUnit (:effortQuantityUnit event)
:hasPointInTime (:hasPointInTime event)
:note (:note event)
:provider (:provider event)
:receiver (:receiver event)
:resourceInventoriedAs (:resourceInventoriedAs event)
:resourceConformsTo (:resourceConformsTo event)
:inputOf (:inputOf event)
:outputOf (:outputOf event)
:toResourceInventoriedAs (:toResourceInventoriedAs event)}))
\ No newline at end of file
(ns vfprocess.db.traversal
(:require [next.jdbc :as jdbc]
[camel-snake-kebab.core :as csk]
[next.jdbc.optional :as opt]
[vfprocess.db.queries :refer [db queryEconomicResources queryProcesses]])
)
(defn visited?
"Predicate which returns true if the node v has been visited already, false otherwise."
[v coll]
(some #(= % v) coll))
(defn first-neighbors
[v]
(let [node (queryEconomicResources v)]
{:type (str "outputOf_" (:id node))}))
(defn find-id
[type]
(.substring type (+ (.indexOf type "_") 1)))
(defn find-neighbors
"Returns the sequence of neighbors for the given node"
[v]
(let [id (find-id (:type v))]
(println id)
(cond
(.contains (:type v) "process")
(let [node (jdbc/execute-one! db
["select * from EconomicEvent where inputOf = ? " id]
{:builder-fn opt/as-unqualified-maps})]
(if (= nil node)
nil
{:type (str "inputOf_" (:id node))
:text (:name node)}))
(.contains (:type v) "inputOf")
(let [node (queryEconomicResources id)]
(if (= nil node)
nil
{:type (str "economicResource_" (:id node))
:text (str (:accountingQuantityNumericValue node) " " (:name node))}))
(.contains (:type v) "outputOf")
(let [node (queryProcesses id)]
(if (= nil node)
nil
{:type (str "process_" (:id node))
:text (:name node)}))
(.contains (:type v) "economicResource")
(let [node(jdbc/execute-one! db
["select * from EconomicEvent where resourceInventoriedAs = ?" id]
{:builder-fn opt/as-unqualified-maps})]
(println node)
(if (= nil node)
nil
{:type (str "outputOf_" (:id node))
:text (str (:name (:action node)) " " (:resourceQuantityNumericValue node) " " (:name (:resourceQuantityUnit node)) " of " (:name (:resourceInventoriedAs node)))}))
:else nil)))
(defn incoming-vf-dfs
"Traverses a graph in Depth First Search (DFS)"
[v]
(println v)
(loop [stack (vector v) ;; Use a stack to store nodes we need to explore
visited []] ;; A vector to store the sequence of visited nodes
(if (empty? stack) ;; Base case - return visited nodes if the stack is empty
visited
(let [v (peek stack)
neighbors (find-neighbors v)
new-stack (if (= nil neighbors) [] (vector neighbors))]
(if (= nil neighbors)
(recur new-stack (conj visited neighbors))
(recur new-stack (conj visited neighbors)))))))
\ No newline at end of file
(ns vfprocess.schema
"Contains custom resolver and a function to provide the full schema"
(:require [clojure.java.io :as io]
[com.walmartlabs.lacinia.util :as util]
[next.jdbc.sql :as sql]
[com.walmartlabs.lacinia.schema :as schema]
[com.walmartlabs.lacinia.resolve :refer [resolve-as]]
[com.stuartsierra.component :as component]
[vfprocess.db.traversal :refer [incoming-vf-dfs
first-neighbors]]
[vfprocess.db.queries :refer [db
queryProcesses
queryAgents
queryEconomicResources
queryEconomicEvents
queryResourceSpecifications
queryUnits
queryActions
createEconomicEvent]]
[vfprocess.core :refer [find-economicEvent-by-id]]
[clojure.edn :as edn]))
(defn resolve-process-by-id
[args]
(let [{:keys [id]} args]
(queryProcesses id)))
(defn resolve-processes
[]
(queryProcesses))
(defn resolve-agent-by-id
[args]
(let [{:keys [id]} args]
(queryAgents id)))
(defn resolve-agents
[]
(queryAgents))
(defn resolve-economicEvent-by-id
[args]
(let [{:keys [id]} args]
(find-economicEvent-by-id id)))
(defn resolve-economicEvents
[]
(queryEconomicEvents))
(defn resolve-economicResource-by-id
[args]
(let [{:keys [id]} args]
(queryEconomicResources id)))
(defn resolve-economicResources
[]
(queryEconomicResources))
(defn resolve-resourceSpecification-by-id
[args]
(let [{:keys [id]} args]
(queryResourceSpecifications id)))
(defn resolve-resourceSpecifications
[]
(queryResourceSpecifications))
(defn resolve-action-by-id
[args]
(let [{:keys [id]} args]
(queryActions id)))
(defn resolve-actions
[]
(queryActions))
(defn resolve-unit-by-id
[args]
(let [{:keys [id]} args]
(queryUnits id)))
(defn resolve-units
[]
(queryUnits))
(defn incoming-valueflows
[args]
(let [{:keys [id]} args
node (first-neighbors id)
resource (queryEconomicResources id)
first-node {:type (str "economicResource_" id)
:text (str (:accountingQuantityNumericValue resource) " " (:name resource))}]
(incoming-vf-dfs first-node)))
(defn mutationNewEconomicEvent
[args]
(let [{:keys [event]} args
economicResource (queryEconomicResources (:resourceInventoriedAs event))
action (queryActions (:action event))
]
(createEconomicEvent event)
(println event)
(if (some? (:resourceInventoriedAs event))
(if (= (:resourceEffect action) "+")
(do
(println "ciao")
(sql/update! db :EconomicResource
{:accountingQuantityNumericValue (+
(:accountingQuantityNumericValue economicResource)
(:resourceQuantityNumericValue event))}
{:onhandQuantitynumericValue (+
(:accountingQuantityNumericValue economicResource)
(:resourceQuantityNumericValue event))}))
(:resourceQuantityNumericValue (:resourceInventoriedAs event))))))
(defn resolver-map
[]
{:query/process (fn [context args value] (resolve-process-by-id args))
:query/allProcesses (fn [context args value] (resolve-processes))
:query/agent (fn [context args value] (resolve-agent-by-id args))
:query/allAgents (fn [context args value] (resolve-agents))
:query/economicEvent (fn [context args value] (resolve-economicEvent-by-id args))
:query/allEconomicEvents (fn [context args value] (resolve-economicEvents))
:query/economicResource (fn [context args value] (resolve-economicResource-by-id args))
:query/allEconomicResources (fn [context args value] (resolve-economicResources))
:query/resourceSpecification (fn [context args value] (resolve-resourceSpecification-by-id args))
:query/allResourceSpecification (fn [context args value] (resolve-resourceSpecifications))
:query/action (fn [context args value] (resolve-action-by-id args))
:query/allActions (fn [context args value] (resolve-actions))
:query/unit (fn