Merge branch 'development_bizmodel' into development_generator_docusaurus

* development_bizmodel: (93 commits)
  s
  Revert "test: add cmdline parser tests"
  test: add cmdline parser tests
  markdown code
  ...
  revert
  ...
  ..deployments
  ...
  bump version to 1.0.21
  ...
  bump version to 1.0.20
  ...
  fix tests and example
  bump version to 1.0.19
  bump version to 1.0.18
  bump version to 1.0.17
  ...
  ...
  bump version to 1.0.16
  ...

# Conflicts:
#	lib/web/docusaurus/config.v
This commit is contained in:
2025-03-08 10:55:58 +01:00
353 changed files with 14753 additions and 5084 deletions

14
.gitignore vendored
View File

@@ -1,4 +1,13 @@
# Additional ignore files and directories
Thumbs.db
# Logs
logs/
*.log
*.out
# Compiled Python files
*.pyc
*.pyo
__pycache__/
*dSYM/
.vmodules/
.vscode
@@ -28,4 +37,5 @@ output/
.stellar
data.ms/
test_basic
cli/hero
cli/hero
.aider*

View File

@@ -51,7 +51,7 @@ fn do() ! {
mut cmd := Command{
name: 'hero'
description: 'Your HERO toolset.'
version: '1.0.13'
version: '1.0.21'
}
// herocmds.cmd_run_add_flags(mut cmd)
@@ -102,6 +102,7 @@ fn do() ! {
// herocmds.cmd_juggler(mut cmd)
herocmds.cmd_generator(mut cmd)
herocmds.cmd_docusaurus(mut cmd)
herocmds.cmd_starlight(mut cmd)
// herocmds.cmd_docsorter(mut cmd)
// cmd.add_command(publishing.cmd_publisher(pre_func))
cmd.setup()

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env -S v -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.biz.investortool
import freeflowuniverse.herolib.core.playbook
import os
mut plbook := playbook.new(
path: '${os.home_dir()}/code/git.ourworld.tf/ourworld_holding/investorstool/output'
)!
mut it := investortool.play(mut plbook)!
it.check()!

View File

@@ -0,0 +1,48 @@
#!/usr/bin/env -S v -cg -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
// #!/usr/bin/env -S v -cg -enable-globals run
import freeflowuniverse.herolib.data.doctree
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.biz.bizmodel
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.core.playcmds
import freeflowuniverse.herolib.web.mdbook
import freeflowuniverse.herolib.biz.spreadsheet
import os
const name = 'tf9_budget'
const wikipath = '${os.home_dir()}/code/git.ourworld.tf/ourworld_holding/info_ourworld/collections/${name}'
const summarypath = '${wikipath}/summary.md'
// mut sh := spreadsheet.sheet_new(name: 'test2') or { panic(err) }
// println(sh)
// sh.row_new(descr: 'this is a description', name: 'something', growth: '0:100aed,55:1000eur')!
// println(sh)
// println(sh.wiki()!)
// exit(0)
// execute the actions so we have the info populated
// mut plb:=playbook.new(path: wikipath)!
// playcmds.run(mut plb,false)!
buildpath := '${os.home_dir()}/hero/var/mdbuild/bizmodel'
// just run the doctree & mdbook and it should
// load the doctree, these are all collections
mut tree := doctree.new(name: name)!
tree.scan(path: wikipath)!
tree.export(dest: buildpath, reset: true)!
// mut bm:=bizmodel.get("test")!
// println(bm)
mut mdbooks := mdbook.get()!
mdbooks.generate(
name: 'bizmodel'
summary_path: summarypath
doctree_path: buildpath
title: 'bizmodel ${name}'
)!
mdbook.book_open('bizmodel')!

View File

@@ -0,0 +1,12 @@
need to find where the manual is
- [manual](bizmodel_example/configuration.md)
- [widgets](bizmodel_example/widgets.md)
- [graph_bar_row](bizmodel_example/graph_bar_row.md)
- [sheet_tables](bizmodel_example/sheet_tables.md)
- [widget_args](bizmodel_example/widget_args.md)
- [params](bizmodel_example/configuration.md)
- [revenue params](bizmodel_example/revenue_params.md)
- [funding params](bizmodel_example/funding_params.md)
- [hr params](bizmodel_example/hr_params.md)
- [costs params](bizmodel_example/costs_params.md)

25
examples/biztools/bizmodel.vsh Executable file
View File

@@ -0,0 +1,25 @@
#!/usr/bin/env -S v -n -w -cg -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
//#!/usr/bin/env -S v -cg -enable-globals run
import freeflowuniverse.herolib.biz.bizmodel
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.core.playcmds
import os
const playbook_path = os.dir(@FILE) + '/playbook'
const build_path = os.join_path(os.dir(@FILE), '/docusaurus')
buildpath := '${os.home_dir()}/hero/var/mdbuild/bizmodel'
mut model := bizmodel.getset("example")!
model.workdir = build_path
model.play(mut playbook.new(path: playbook_path)!)!
println(model.sheet)
println(model.sheet.export()!)
model.sheet.export(path:"~/Downloads/test.csv")!
model.sheet.export(path:"~/code/github/freeflowuniverse/starlight_template/src/content/test.csv")!

View File

@@ -0,0 +1,4 @@
bizmodel
dest
wiki
build

View File

@@ -0,0 +1 @@
ms1bmodel.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

View File

@@ -0,0 +1,13 @@
## Revenue
Overview of achieved revenue.
Unit is in Million USD.
!!bizmodel.sheet_wiki title:'REVENUE' includefilter:rev sheetname:'bizmodel_test'
!!bizmodel.graph_bar_row rowname:revenue_total unit:million sheetname:'bizmodel_test'
!!bizmodel.graph_line_row rowname:revenue_total unit:million sheetname:'bizmodel_test'
!!bizmodel.graph_pie_row rowname:revenue_total unit:million size:'80%' sheetname:'bizmodel_test'

View File

@@ -0,0 +1,13 @@
- [bizmodel](bizmodel_example/bizmodel.md)
- [Revenue](bizmodel_example/revenue.md)
- [Result](bizmodel_example/overview.md)
- [parameters](bizmodel_example/params.md)
- [revenue_params](bizmodel_example/params/revenue_params.md)
- [funding_params](bizmodel_example/params/funding_params.md)
- [hr_params](bizmodel_example/params/hr_params.md)
- [costs_params](bizmodel_example/params/costs_params.md)
- [rows overview](bizmodel_example/rows_overview.md)
- [employees](bizmodel_example/employees.md)
- [debug](bizmodel_example/debug.md)
- [worksheet](bizmodel_example/worksheet.md)

View File

@@ -0,0 +1,4 @@
# Overview of the rows in the biz model sheet
!!bizmodel.sheet_wiki sheetname:'bizmodel_test'

View File

@@ -0,0 +1,37 @@
#!/usr/bin/env -S v -n -w -cg -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals run
//#!/usr/bin/env -S v -cg -enable-globals run
import freeflowuniverse.herolib.biz.bizmodel
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.core.playcmds
import os
//TODO: need to fix wrong location
const playbook_path = os.dir(@FILE) + '/playbook'
const build_path = os.join_path(os.dir(@FILE), '/docusaurus')
buildpath := '${os.home_dir()}/hero/var/mdbuild/bizmodel'
mut model := bizmodel.getset("example")!
model.workdir = build_path
model.play(mut playbook.new(path: playbook_path)!)!
println(model.sheet)
println(model.sheet.export()!)
// model.sheet.export(path:"~/Downloads/test.csv")!
// model.sheet.export(path:"~/code/github/freeflowuniverse/starlight_template/src/content/test.csv")!
report := model.new_report(
name: 'example_report'
title: 'Example Business Model'
)!
report.export(
path: build_path
overwrite: true
format: .docusaurus
)!

View File

@@ -0,0 +1 @@
output dir of example

View File

@@ -0,0 +1,22 @@
#!/bin/bash
set -ex
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "${script_dir}"
echo "Docs directory: $script_dir"
cd "${HOME}/hero/var/docusaurus"
export PATH=/tmp/docusaurus_build/node_modules/.bin:${HOME}/.bun/bin/:$PATH
rm -rf /Users/despiegk/hero/var/docusaurus/build/
. ${HOME}/.zprofile
bun docusaurus build
mkdir -p /Users/despiegk/code/github/freeflowuniverse/herolib/examples/biztools/bizmodel/example/docusaurus
echo SYNC TO /Users/despiegk/code/github/freeflowuniverse/herolib/examples/biztools/bizmodel/example/docusaurus
rsync -rv --delete /Users/despiegk/hero/var/docusaurus/build/ /Users/despiegk/code/github/freeflowuniverse/herolib/examples/biztools/bizmodel/example/docusaurus/

View File

@@ -0,0 +1 @@
{"style":"dark","links":[]}

View File

@@ -0,0 +1 @@
{"name":"","title":"Docusaurus","tagline":"","favicon":"img/favicon.png","url":"http://localhost","url_home":"docs/introduction","baseUrl":"/","image":"img/tf_graph.png","metadata":{"description":"Docusaurus","image":"Docusaurus","title":"Docusaurus"},"buildDest":[],"buildDestDev":[]}

View File

@@ -0,0 +1 @@
{"title":"Business Model","items":[{"href":"https://threefold.info/kristof/","label":"ThreeFold Technology","position":"right"},{"href":"https://threefold.io","label":"Operational Plan","position":"left"}]}

View File

@@ -0,0 +1,16 @@
#!/bin/bash
set -e
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "${script_dir}"
echo "Docs directory: $script_dir"
cd "${HOME}/hero/var/docusaurus"
export PATH=/tmp/docusaurus_build/node_modules/.bin:${HOME}/.bun/bin/:$PATH
. ${HOME}/.zprofile
bun run start -p 3100

View File

@@ -0,0 +1,10 @@
## Loader instructions
this will make sure we load the appropriate biz model
```js
!!bizmodel.load name:'default' url:'https://github.com/freeflowuniverse/herolib/tree/development/bizmodel/example/data'
```

View File

@@ -0,0 +1 @@
name:bizmodel_example

View File

@@ -0,0 +1,10 @@
![](img/ms1bmodel.png)
# bizmodel
OurWorld has developed a tool to generate and keep business models up to date.
Our aim is to make it easy for ourworld to track changes in planning over the multiple projects and even be able to aggregated them. Because the input for such a plan is text (as you can see in this ebook) its easy to see how the modelling and parameters change over time.
This is a very flexible tool which will be extended for budgetting, cashflow management, shareholder tables, ...

View File

@@ -0,0 +1,31 @@
# HR Params
## Engineering
Costs can be grouped in cost centers which can then be used to futher process e.g. transcactions between companies.
```js
!!bizmodel.costcenter_define bizname:'test'
name:'tfdmcc'
descr:'TFDMCC executes on near source agreement for TFTech'
min_month:'10000USD'
max_month:'100000USD'
end_date:'1/1/2026' //when does agreement stop
!!bizmodel.costcenter_define bizname:'test'
name:'cs_tftech'
descr:'Nearsource agreement for TFTech towards Codescalers'
min_month:'10000USD'
max_month:'100000USD'
end_date:'1/1/2026'
!!bizmodel.costcenter_define bizname:'test'
name:'cs_tfcloud'
descr:'Nearsource agreement for TFCloud towards Codescalers'
min_month:'10000USD'
max_month:'100000USD'
end_date:'1/1/2026'
```

View File

@@ -0,0 +1,39 @@
# Generic Overhead Costs
possible parameters
- name
- descr: description of the cost
- cost: is 'month:amount,month:amount, ...', no extrapolation
- cost_growth: is 'month:amount,month:amount, ..., or just a nr', will extrapolate
- type: travel, admin, legal, varia, office
- cost_percent_revenue e.g. 4%, will make sure the cost will be at least 4% of revenue
- indexation, e.g. 2%
Other financial flows can be mentioned here as well.
```js
!!bizmodel.cost_define bizname:'test'
name:'rental'
descr:'Office Rental in BE.'
cost:'5000'
indexation:'2%'
type:'office'
!!bizmodel.cost_define bizname:'test'
name:'oneoff'
descr:'Event in Z.'
cost_one:'3:50000'
type:'event'
!!bizmodel.cost_define bizname:'test'
name:'cloud'
descr:'Datacenter and Cloud Costs'
cost:'2000eur'
cost_percent_revenue:'2%'
type:'cloud'
```

View File

@@ -0,0 +1,4 @@
# Debug
Some tools and info to help debug the bizmodel simulator.

View File

@@ -0,0 +1,20 @@
# Department Params
```js
!!bizmodel.department_define bizname:'test'
name:'ops'
title:'Operations'
order:5
!!bizmodel.department_define bizname:'test'
name:'coordination'
title:'Coordination'
order:1
!!bizmodel.department_define bizname:'test'
name:'engineering'
title:'Engineering'
order:4
```

View File

@@ -0,0 +1,29 @@
# Funding Params
possible parameters
- name, e.g. for a specific person
- descr: description of the funding
- investment is month:amount,month:amount, ...
- type: loan or capital
Other financial flows can be mentioned here as well.
```js
!!bizmodel.funding_define bizname:'test'
name:'our_investor'
descr:'A fantastic super investor.'
investment:'3:1000000EUR'
type:'capital'
!!bizmodel.funding_define bizname:'test'
name:'a_founder'
descr:'Together Are Strong'
investment:'2000000'
type:'loan'
```

View File

@@ -0,0 +1,73 @@
# HR Params
## Engineering
possible parameters
- descr, description of the function (e.g. master architect)
- cost, any currency eg. 1000usd
- in case cost changes over time e.g. 1:10000USD,20:20000USD,60:30000USD
- indexation, e.g. 2%
- department
- name, e.g. for a specific person
- nrpeople: how many people per month, growth over time notation e.g. 1:10,60:20 means 10 in month 1 growing to 20 month 60
- cost_percent_revenue e.g. 4%, will make sure the cost will be at least 4% of revenue
```js
!!bizmodel.employee_define bizname:'test'
sid:2
descr:'Senior Engineer'
cost:'1:12000,12:14000' //cost is always per person
department:'engineering'
nrpeople:'0:5,20:5'
!!bizmodel.employee_define bizname:'test'
name:'despiegk'
title: 'CTO and crazy inventor.'
sid:3
descr:'CTO'
cost:'12000EUR' //the salary is the cost independent of the fulltime status
indexation:'10%'
department:'coordination'
page:'cto.md'
fulltime: "50%" //100% means yes
!!bizmodel.employee_define bizname:'test'
descr:'Senior Architect'
cost:'10000USD' indexation:'5%'
department:'engineering'
nrpeople:'0:5,20:10'
!!bizmodel.employee_define bizname:'test'
descr:'Junior Engineer'
cost:'4000USD' indexation:'5%'
department:'engineering'
nrpeople:'0:5,20:10'
```
## Operations
```js
!!bizmodel.employee_define bizname:'test'
descr:'Ops Manager'
cost:'1:8000,12:14000'
department:'ops'
!!bizmodel.employee_define bizname:'test'
descr:'Support Junior'
cost:'2000EUR' indexation:'5%'
department:'ops'
nrpeople:'7:5,18:10'
cost_percent_revenue:'1%'
!!bizmodel.employee_define bizname:'test'
descr:'Support Senior'
cost:'5000EUR' indexation:'5%'
department:'ops'
nrpeople:'3:5,20:10'
cost_percent_revenue:'1%'
costcenter:'tfdmcc:25,cs_tfcloud:75'
generate_page:'../employees/support_senior.md'
```

View File

@@ -0,0 +1,14 @@
# Bizmodel Params
In this section we can find all the parameters for the bizmodel.
## how to use and read
The params are defined in the different instruction files e.g. revenue_params.md
Often you will see something like `revenue_growth:'10:1000,20:1100'` this can be read as month 10 it 1000, month 20 its 1100.
The software will extrapolate.

View File

@@ -0,0 +1,85 @@
# HR Params
## Revenue Items (non recurring)
This company is a cloud company ...
- name, e.g. for a specific project
- descr, description of the revenue line item
- revenue_items: does one of revenue, is not exterpolated
- revenue_growth: is a revenue stream which is being extrapolated
- revenue_setup, revenue for 1 item '1000usd'
- revenue_setup_delay
- revenue_monthly, revenue per month for 1 item
- revenue_monthly_delay, how many months before monthly revenue starts
- maintenance_month_perc, how much percent of revenue_setup will come back over months
- cogs_setup, cost of good for 1 item at setup
- cogs_setup_delay, how many months before setup cogs starts, after sales
- cogs_setup_perc: what is percentage of the cogs (can change over time) for setup e.g. 0:50%
- cogs_monthly, cost of goods for the monthly per 1 item
- cogs_monthly_delay, how many months before monthly cogs starts, after sales
- cogs_monthly_perc: what is percentage of the cogs (can change over time) for monthly e.g. 0:5%,12:10%
- nr_sold: how many do we sell per month (is in growth format e.g. 10:100,20:200, default is 1)
- nr_months_recurring: how many months is recurring, if 0 then no recurring
```js
!!bizmodel.revenue_define bizname:'test'
descr:'OEM Deals'
revenue_items:'10:1000000EUR,15:3333,20:1200000'
cogs_setup_perc: '1:5%,20:10%'
!!bizmodel.revenue_define bizname:'test'
descr:'License Deals'
revenue_growth:'10:1000,20:1100'
cogs_perc: '10%'
rev_delay_month: 1
!!bizmodel.revenue_define bizname:'test'
descr:'3NODE License Sales 1 Time'
//means revenue is 100 month 1, 200 month 60
revenue_item:'1:100,60:200'
revenue_nr:'10:1000,24:2000,60:40000'
cogs_perc: '10%'
rev_delay_month: 1
```
## Revenue Items Recurring
possible parameters
- name, e.g. for a specific project
- descr, description of the revenue line item
- revenue_setup, revenue for 1 item '1000usd'
- revenue_monthly, revenue per month for 1 item
- revenue_setup_delay, how many months before revenue comes in after sales
- revenue_monthly_delay, how many months before monthly revenue starts
- cogs_setup, cost of good for 1 item at setup
- cogs_setup_perc: what is percentage of the cogs (can change over time) for setup e.g. 0:50%
- cogs_monthly, cost of goods for the monthly per 1 item
- cogs_monthly_perc: what is percentage of the cogs (can change over time) for monthly e.g. 0:5%,12:10%
- nr_sold: how many do we sell per month (is in growth format e.g. 10:100,20:200)
- nr_months: how many months is recurring
if currency not specified then is always in USD
```js
!!bizmodel.revenue_recurring_define bizname:'test'
name: '3node_lic'
descr:'3NODE License Sales Recurring Basic'
revenue_setup:'1:100,60:50'
// revenue_setup:'5'
revenue_monthly_delay:3
revenue_monthly:'1:1,60:1'
// cogs_setup:'1:0'
cogs_setup_perc:'50%'
revenue_setup_delay:1
cogs_monthly_perc:'50%'
nr_sold:'10:1000,24:2000,60:40000'
60 is the default
nr_months:60
```

View File

@@ -1 +0,0 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run

View File

@@ -1 +0,0 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run

View File

@@ -1,15 +0,0 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.sysadmintools.daguserver
import freeflowuniverse.herolib.installers.infra.zinit
// make sure zinit is there and running, will restart it if needed
mut z := zinit.get()!
z.destroy()!
z.start()!
// mut ds := daguserver.get()!
// ds.destroy()!
// ds.start()!
// println(ds)

View File

@@ -0,0 +1,8 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.db.meilisearch_installer
mut meilisearch := meilisearch_installer.get()!
meilisearch.install()!
meilisearch.start()!
meilisearch.destroy()!

View File

@@ -0,0 +1,9 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.db.postgresql as postgresql_installer
mut db := postgresql_installer.get()!
db.install()!
db.start()!
db.destroy()!

View File

@@ -0,0 +1,9 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.db.zerodb as zerodb_installer
mut db := zerodb_installer.get()!
db.install()!
db.start()!
db.destroy()!

View File

@@ -1,16 +0,0 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.infra.gitea as gitea_installer
mut installer := gitea_installer.get(name: 'test')!
// if you want to configure using heroscript
gitea_installer.play(
heroscript: "
!!gitea.configure name:test
passwd:'something'
domain: 'docs.info.com'
"
)!
installer.start()!

View File

@@ -0,0 +1,8 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.infra.gitea as gitea_installer
mut gitea := gitea_installer.get()!
gitea.install()!
gitea.start()!
gitea.destroy()!

View File

@@ -0,0 +1,8 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.infra.livekit as livekit_installer
mut livekit := livekit_installer.get()!
livekit.install()!
livekit.start()!
livekit.destroy()!

View File

@@ -0,0 +1,8 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.infra.screen as screen_installer
mut screen := screen_installer.get()!
screen.install()!
screen.destroy()!

View File

@@ -1,6 +1,8 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.sysadmintools.zinit as zinit_installer
import freeflowuniverse.herolib.installers.infra.zinit_installer
mut installer := zinit_installer.get()!
installer.install()!
installer.start()!
// installer.destroy()!

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.lang.golang
mut golang_installer := golang.get()!
golang_installer.install()!

View File

@@ -0,0 +1,7 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.lang.nodejs
mut nodejs_installer := nodejs.get()!
// nodejs_installer.install()!
nodejs_installer.destroy()!

View File

@@ -0,0 +1,7 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.lang.python as python_module
mut python_installer := python_module.get()!
// python_installer.install()!
python_installer.destroy()!

View File

@@ -0,0 +1,7 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.lang.rust as rust_module
mut rust_installer := rust_module.get()!
// rust_installer.install()!
rust_installer.destroy()!

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.net.mycelium as mycelium_installer
import freeflowuniverse.herolib.installers.net.mycelium_installer
import freeflowuniverse.herolib.clients.mycelium
mut installer := mycelium_installer.get()!
@@ -13,25 +13,27 @@ mut client := mycelium.get()!
// Send a message to a node by public key
// Parameters: public_key, payload, topic, wait_for_reply
msg := client.send_msg('abc123...', // destination public key
'Hello World', // message payload
'greetings', // optional topic
true // wait for reply
)!
msg := client.send_msg(
public_key: 'abc123...' // destination public key
payload: 'Hello World' // message payload
topic: 'greetings' // optional topic
wait: true // wait for reply
)!
println('Sent message ID: ${msg.id}')
// Receive messages
// Parameters: wait_for_message, peek_only, topic_filter
received := client.receive_msg(true, false, 'greetings')!
received := client.receive_msg(wait: true, peek: false, topic: 'greetings')!
println('Received message from: ${received.src_pk}')
println('Message payload: ${received.payload}')
// Reply to a message
client.reply_msg(received.id, // original message ID
received.src_pk, // sender's public key
'Got your message!', // reply payload
'greetings' // topic
)!
client.reply_msg(
id: received.id // original message ID
public_key: received.src_pk // sender's public key
payload: 'Got your message!' // reply payload
topic: 'greetings' // topic
)!
// Check message status
status := client.get_msg_status(msg.id)!

View File

@@ -0,0 +1,7 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.net.wireguard_installer as wireguard
mut wireguard_installer := wireguard.get()!
wireguard_installer.install()!
wireguard_installer.destroy()!

View File

@@ -1,13 +0,0 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import time
import freeflowuniverse.herolib.installers.db.postgresql
mut db := postgresql.get()!
// db.destroy()!
db.start()!
// db.db_create('my_new_db')!
// db.stop()!
// db.start()!

View File

@@ -1,7 +1,8 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.sysadmintools.actrunner
import freeflowuniverse.herolib.installers.virt.herocontainers
// import freeflowuniverse.herolib.installers.virt.herocontainers
actrunner.install()!
mut actrunner_ := actrunner.get()!
actrunner_.install()!
// herocontainers.start()!

View File

@@ -0,0 +1,8 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.sysadmintools.garage_s3 as garage_s3_installer
mut garage_s3 := garage_s3_installer.get()!
garage_s3.install()!
garage_s3.start()!
garage_s3.destroy()!

View File

@@ -0,0 +1,7 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.sysadmintools.rclone as rclone_installer
mut rclone := rclone_installer.get()!
rclone.install()!
rclone.destroy()!

View File

@@ -4,3 +4,4 @@ import freeflowuniverse.herolib.installers.threefold.griddriver
mut griddriver_installer := griddriver.get()!
griddriver_installer.install()!
griddriver_installer.destroy()!

View File

@@ -0,0 +1,9 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.sysadmintools.daguserver
import freeflowuniverse.herolib.installers.infra.zinit_installer
mut ds := daguserver.get()!
ds.install()!
ds.start()!
ds.destroy()!

View File

@@ -0,0 +1,11 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.virt.pacman as pacman_installer
mut pacman := pacman_installer.get()!
// To install
pacman.install()!
// To remove
pacman.destroy()!

View File

@@ -0,0 +1,60 @@
#!/usr/bin/env -S v -n -w -cg -d use_openssl -enable-globals run
import freeflowuniverse.herolib.installers.infra.coredns as coredns_installer
import freeflowuniverse.herolib.osal.coredns
import freeflowuniverse.herolib.core.playbook
// coredns_installer.delete()!
mut installer := coredns_installer.get()!
// coredns_installer.fix()!
installer.start()!
mut script := "
!!dns.a_record
sub_domain: 'host1'
ip: '1.2.3.4'
ttl: 300
!!dns.aaaa_record
sub_domain: 'host1'
ip: '2001:db8::1'
ttl: 300
!!dns.mx_record
sub_domain: '*'
host: 'mail.example.com'
preference: 10
ttl: 300
!!dns.txt_record
sub_domain: '*'
text: 'v=spf1 mx ~all'
ttl: 300
!!dns.srv_record
service: 'ssh'
protocol: 'tcp'
host: 'host1'
target: 'sip.example.com'
port: 5060
priority: 10
weight: 100
ttl: 300
!!dns.ns_record
host: 'ns1.example.com'
ttl: 300
!!dns.soa_record
mbox: 'hostmaster.example.com'
ns: 'ns1.example.com'
refresh: 44
retry: 55
expire: 66
minttl: 100
ttl: 300
"
mut plbook := playbook.new(text: script)!
mut set := coredns.play_dns(mut plbook)!
set.set(key_prefix: 'dns:', domain: 'heroexample.com')!

View File

@@ -1,6 +1,5 @@
#!/usr/bin/env -S v -gc none -no-retry-compilation -d use_openssl -enable-globals -cg run
//#!/usr/bin/env -S v -gc none -no-retry-compilation -cc tcc -d use_openssl -enable-globals -cg run
import freeflowuniverse.herolib.threefold.gridproxy
import freeflowuniverse.herolib.threefold.tfgrid3deployer
import freeflowuniverse.herolib.installers.threefold.griddriver
@@ -20,7 +19,8 @@ deployment.add_machine(
cpu: 1
memory: 2
planetary: false
public_ip4: true
wireguard: true
public_ip4: false
size: 10 // 10 gig
mycelium: tfgrid3deployer.Mycelium{}
)

View File

@@ -3,10 +3,8 @@
import freeflowuniverse.herolib.threefold.gridproxy
import freeflowuniverse.herolib.threefold.tfgrid3deployer
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.installers.threefold.griddriver
fn main() {
griddriver.install()!
v := tfgrid3deployer.get()!
println('cred: ${v}')
@@ -19,19 +17,19 @@ fn main() {
cpu: 1
memory: 2
planetary: false
public_ip4: true
public_ip4: false
mycelium: tfgrid3deployer.Mycelium{}
nodes: [u32(167)]
)
deployment.add_machine(
name: 'my_vm2'
cpu: 1
memory: 2
planetary: false
public_ip4: true
mycelium: tfgrid3deployer.Mycelium{}
// nodes: [u32(164)]
)
// deployment.add_machine(
// name: 'my_vm2'
// cpu: 1
// memory: 2
// planetary: false
// public_ip4: true
// mycelium: tfgrid3deployer.Mycelium{}
// // nodes: [u32(164)]
// )
deployment.add_zdb(name: 'my_zdb', password: 'my_passw&rd', size: 2)
deployment.add_webname(name: 'mywebname2', backend: 'http://37.27.132.47:8000')

View File

@@ -7,7 +7,10 @@ import freeflowuniverse.herolib.installers.threefold.griddriver
import os
import time
griddriver.install()!
res2:=tfgrid3deployer.filter_nodes()!
println(res2)
exit(0)
v := tfgrid3deployer.get()!
println('cred: ${v}')
@@ -18,7 +21,7 @@ deployment.add_machine(
cpu: 1
memory: 2
planetary: false
public_ip4: true
public_ip4: false
size: 10 // 10 gig
mycelium: tfgrid3deployer.Mycelium{}
)

View File

@@ -1,42 +0,0 @@
#!/usr/bin/env -S v -n -w -gc none -cg -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.web.docusaurus
// import freeflowuniverse.herolib.data.doctree
// Create a new docusaurus factory
mut docs := docusaurus.new(
build_path: '/tmp/docusaurus_build'
)!
// Create a new docusaurus site
mut site := docs.dev(
url: 'https://git.ourworld.tf/despiegk/docs_kristof'
)!
// FOR FUTURE TO ADD CONTENT FROM DOCTREE
// Create a doctree for content
// mut tree := doctree.new(name: 'content')!
// // Add some content from a git repository
// tree.scan(
// git_url: 'https://github.com/yourusername/your-docs-repo'
// git_pull: true
// )!
// // Export the content to the docusaurus site
// tree.export(
// destination: '${site.path_build.path}/docs'
// reset: true
// keep_structure: true
// exclude_errors: false
// )!
// Build the docusaurus site
// site.build()!
// Generate the static site
// site.generate()!
// Optionally open the site in a browser
// site.open()!

View File

@@ -0,0 +1,94 @@
#!/usr/bin/env -S v -n -w -gc none run
import freeflowuniverse.herolib.data.markdownparser2
// Sample markdown text
text := '# Heading 1
This is a paragraph with **bold** and *italic* text.
## Heading 2
- List item 1
- List item 2
- Nested item
- List item 3
```v
fn main() {
println("Hello, world!")
}
```
> This is a blockquote
> with multiple lines
| Column 1 | Column 2 | Column 3 |
|----------|:--------:|---------:|
| Left | Center | Right |
| Cell 1 | Cell 2 | Cell 3 |
[Link to V language](https://vlang.io)
![Image](https://vlang.io/img/v-logo.png)
Footnote reference[^1]
[^1]: This is a footnote.
'
// Example 1: Using the plain text renderer
println('=== PLAINTEXT RENDERING ===')
println(markdownparser2.to_plain(text))
println('')
// Example 2: Using the structure renderer to show markdown structure
println('=== STRUCTURE RENDERING ===')
println(markdownparser2.to_structure(text))
// Example 3: Using the navigator to find specific elements
println('\n=== NAVIGATION EXAMPLE ===')
// Parse the markdown text
doc := markdownparser2.parse(text)
// Create a navigator
mut nav := markdownparser2.new_navigator(doc)
// Find all headings
headings := nav.find_all_by_type(.heading)
println('Found ${headings.len} headings:')
for heading in headings {
level := heading.attributes['level']
println(' ${'#'.repeat(level.int())} ${heading.content}')
}
// Find all code blocks
code_blocks := nav.find_all_by_type(.code_block)
println('\nFound ${code_blocks.len} code blocks:')
for block in code_blocks {
language := block.attributes['language']
println(' Language: ${language}')
println(' Content length: ${block.content.len} characters')
}
// Find all list items
list_items := nav.find_all_by_type(.list_item)
println('\nFound ${list_items.len} list items:')
for item in list_items {
println(' - ${item.content}')
}
// Find content containing specific text
if element := nav.find_by_content('blockquote') {
println('\nFound element containing "blockquote":')
println(' Type: ${element.typ}')
println(' Content: ${element.content}')
}
// Find all footnotes
println('\nFootnotes:')
for id, footnote in nav.footnotes() {
println(' [^${id}]: ${footnote.content}')
}

View File

@@ -0,0 +1,27 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
// import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.ui.console
import log
import os
import markdown
import freeflowuniverse.herolib.data.markdownparser2
path2:="${os.home_dir()}/code/github/freeflowuniverse/herolib/examples/webtools/mdbook_markdown/content/links.md"
path1:="${os.home_dir()}/code/github/freeflowuniverse/herolib/examples/webtools/mdbook_markdown/content/test.md"
text := os.read_file(path1)!
// Example 1: Using the built-in plaintext renderer
println('=== PLAINTEXT RENDERING ===')
println(markdown.to_plain(text))
println('')
// Example 2: Using our custom structure renderer to show markdown structure
println('=== STRUCTURE RENDERING ===')
println(markdownparser2.to_structure(text))
// // Example 3: Using a simple markdown example to demonstrate structure
// println('\n=== STRUCTURE OF A SIMPLE MARKDOWN EXAMPLE ===')
// simple_md := '# Heading 1\n\nThis is a paragraph with **bold** and *italic* text.\n\n- List item 1\n- List item 2\n\n```v\nfn main() {\n\tprintln("Hello, world!")\n}\n```\n\n[Link to V language](https://vlang.io)'
// println(markdown.to_structure(simple_md))

View File

@@ -0,0 +1,29 @@
---
sidebar_position: 10
title: 'Dunia CyberCity'
description: 'Co-create the Future'
---
![alt text](img/cybercity2.png)
We are building a 700,000 m2 Regenerative Startup Cyber City
- 100% co-owned
- regenerative
- autonomous zone
a city for startups and its creators
- build a system for augmented collective intelligence
- operate business wise from a digital freezone
- (co)own assets (shares, digital currencies) safely and privately
## More Info
> see [https://friends.threefold.info/cybercity](https://friends.threefold.info/cybercity)
- login:```planet```
- passwd:```first```

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.data.doctree
@@ -11,16 +11,17 @@ mut tree := doctree.new(name: 'test')!
// git_root string
// git_pull bool
// load bool = true // means we scan automatically the added collection
for project in 'projectinca, legal, why, web4,tfgrid3'.split(',').map(it.trim_space()) {
for project in 'projectinca, legal, why'.split(',').map(it.trim_space()) {
tree.scan(
git_url: 'https://git.ourworld.tf/tfgrid/info_tfgrid/src/branch/development/collections/${project}'
git_pull: false
)!
}
tree.export(
destination: '/tmp/test'
destination: '/tmp/mdexport'
reset: true
keep_structure: true
//keep_structure: true
exclude_errors: false
)!

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
// import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.ui.console

View File

@@ -0,0 +1,17 @@
#!/usr/bin/env -S v -n -w -gc none -cg -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.web.starlight
// import freeflowuniverse.herolib.data.doctree
// Create a new starlight factory
mut docs := starlight.new(
build_path: '/tmp/starlight_build'
)!
// Create a new starlight site
mut site := docs.get(
url: 'https://git.ourworld.tf/tfgrid/docs_aibox'
init:true //init means we put config files if not there
)!
site.dev()!

View File

@@ -4,7 +4,7 @@ set -e
os_name="$(uname -s)"
arch_name="$(uname -m)"
version='1.0.13'
version='1.0.21'
# Base URL for GitHub releases

View File

@@ -181,7 +181,7 @@ function os_update {
fi
#apt install apt-transport-https ca-certificates curl software-properties-common -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" --force-yes
package_install "apt-transport-https ca-certificates curl wget software-properties-common tmux"
package_install "rclone rsync mc redis-server screen net-tools git dnsutils htop ca-certificates screen lsb-release binutils pkg-config"
package_install "rclone rsync mc redis-server screen net-tools git dnsutils htop ca-certificates screen lsb-release binutils pkg-config libssl-dev iproute2"
elif [[ "${OSNAME}" == "darwin"* ]]; then
if command -v brew >/dev/null 2>&1; then

1
lib/biz/bizmodel/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
testdata

155
lib/biz/bizmodel/act.v Normal file
View File

@@ -0,0 +1,155 @@
module bizmodel
import os
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.core.playbook { PlayBook, Action }
import freeflowuniverse.herolib.ui.console
// import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.data.paramsparser {Params}
import freeflowuniverse.herolib.biz.spreadsheet {RowGetArgs, UnitType, PeriodType}
pub fn (mut m BizModel) act(action Action) !Action {
return match texttools.snake_case(action.name) {
'funding_define' {
m.funding_define_action(action)!
}
'revenue_define' {
m.revenue_action(action)!
}
'costcenter_define' {
m.costcenter_define_action(action)!
}
'cost_define' {
m.cost_define_action(action)!
}
'department_define' {
m.department_define_action(action)!
}
'employee_define' {
m.employee_define_action(action)!
}
'export_report' {
m.new_report_action(action)!
}
'sheet_wiki' {
m.export_sheet_action(action)!
}
'graph_bar_row' {
m.export_graph_bar_action(action)!
}
'graph_pie_row' {
m.export_graph_pie_action(action)!
}
'graph_line_row' {
m.export_graph_line_action(action)!
}
'row_overview' {
m.export_overview_action(action)!
}
else {
return error('Unknown operation: ${action.name}')
}
}
}
fn (mut m BizModel) export_sheet_action(action Action) !Action {
return m.export_action(m.sheet.wiki(row_args_from_params(action.params)!)!, action)
}
fn (mut m BizModel) export_graph_title_action(action Action) !Action {
return m.export_action(m.sheet.wiki_title_chart(row_args_from_params(action.params)!)!, action)
}
fn (mut m BizModel) export_graph_line_action(action Action) !Action {
return m.export_action(m.sheet.wiki_line_chart(row_args_from_params(action.params)!)!, action)
}
fn (mut m BizModel) export_graph_bar_action(action Action) !Action {
return m.export_action(m.sheet.wiki_bar_chart(row_args_from_params(action.params)!)!, action)
}
pub fn (mut m BizModel) export_graph_pie_action(action Action) !Action {
return m.export_action(m.sheet.wiki_pie_chart(row_args_from_params(action.params)!)!, action)
}
pub fn (mut m BizModel) export_overview_action(action Action) !Action {
return m.export_action(m.sheet.wiki_row_overview(row_args_from_params(action.params)!)!, action)
}
fn (mut m BizModel) new_report_action(action Action) !Action {
m.new_report(action.params.decode[Report]()!)!
return action
}
// fetches args for getting row from params
pub fn row_args_from_params(p Params) !RowGetArgs {
rowname := p.get_default('rowname', '')!
namefilter := p.get_list_default('namefilter', [])!
includefilter := p.get_list_default('includefilter', [])!
excludefilter := p.get_list_default('excludefilter', [])!
size := p.get_default('size', '')!
title_sub := p.get_default('title_sub', '')!
title := p.get_default('title', '')!
unit := p.get_default('unit', 'normal')!
unit_e := match unit {
'thousand' { UnitType.thousand }
'million' { UnitType.million }
'billion' { UnitType.billion }
else { UnitType.normal }
}
period_type := p.get_default('period_type', 'year')!
if period_type !in ['year', 'month', 'quarter'] {
return error('period type needs to be in year,month,quarter')
}
period_type_e := match period_type {
'year' { PeriodType.year }
'month' { PeriodType.month }
'quarter' { PeriodType.quarter }
else { PeriodType.error }
}
if period_type_e == .error {
return error('period type needs to be in year,month,quarter')
}
rowname_show := p.get_default_true('rowname_show')
descr_show := p.get_default_true('descr_show')
return RowGetArgs{
rowname: rowname
namefilter: namefilter
includefilter: includefilter
excludefilter: excludefilter
period_type: period_type_e
unit: unit_e
title_sub: title_sub
title: title
size: size
rowname_show: rowname_show
descr_show: descr_show
}
}
// creates the name for a file being exported given the params of the export action
fn (m BizModel) export_action(content string, action Action) !Action {
// determine name of file being exported
name := if action.params.exists('name') { action.params.get('name')! } else {
if action.params.exists('title') { action.params.get('title')! } else {
// if no name or title, name is ex: revenue_total_graph_bar_row
rowname := action.params.get_default('rowname', '')!
'${rowname}_${action.name}'
}
}
// by default exports to working dir of bizmodel
destination := action.params.get_default('destination', m.workdir)!
mut path := pathlib.get_file(
path: os.join_path(destination, name)
increment: true
empty: action.params.get_default_false('overwrite')
)!
path.write(content)!
return action
}

151
lib/biz/bizmodel/export.v Normal file
View File

@@ -0,0 +1,151 @@
module bizmodel
import os
import freeflowuniverse.herolib.web.docusaurus
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core.pathlib
pub struct Report {
pub:
name string
title string
description string
path string
sections []ReportSection
}
pub enum ReportSection {
revenue_model
cost_structure
human_resources
}
pub fn (b BizModel) new_report(report Report) !Report {
name := if report.name != '' {report.name} else { texttools.snake_case(report.title) }
path := pathlib.get_dir(
path: os.join_path(os.home_dir(), '/hero/var/bizmodel/reports/${name}')
create: true
empty: true
)!
b.write_introduction(path.path)!
b.write_operational_plan(path.path)!
b.write_revenue_model(path.path)!
b.write_cost_structure(path.path)!
return Report {
...report,
name: name
path: path.path
}
// b.export_summary()
// b.export_business_description()
// b.export_market_analysis()
// b.export_business_model()
// b.export_revenue_model(export)!
// b.export_cost_structure(export)
// b.export_operational_plan(export)!
// b.export_fundraising(export)
}
pub struct Export {
pub:
path string
overwrite bool
format ExportFormat
}
pub enum ExportFormat {
docusaurus
mdbook
}
pub fn (r Report) export(export Export) ! {
match export.format {
.docusaurus {
mut dir := pathlib.get_dir(path: r.path)!
dir.copy(dest: '${export.path}/docs', delete: true)!
mut factory := docusaurus.new()!
mut site := factory.get(
name: r.name
path: export.path
publish_path: export.path
init: true
config: docusaurus.Config {
navbar: docusaurus.Navbar {
title: "Business Model",
items: [
docusaurus.NavbarItem{
"href": "https://threefold.info/kristof/",
"label": "ThreeFold Technology",
"position": "right"
},
docusaurus.NavbarItem{
"href": "https://threefold.io",
"label": "Operational Plan",
"position": "left"
}
]
}
main: docusaurus.Main {
url_home: 'docs/introduction'
}
} //TODO: is this needed
)!
site.generate()!
}
.mdbook {panic('MDBook export not fully implemented')}
}
}
pub fn (model BizModel) write_introduction(path string) ! {
mut index_page := pathlib.get_file(path: '${path}/introduction.md')!
// mut tmpl_index := $tmpl('templates/index.md')
index_page.template_write($tmpl('templates/introduction.md'), true)!
}
pub fn (model BizModel) write_operational_plan(path string) ! {
mut dir := pathlib.get_dir(path: '${path}/operational_plan')!
mut ops_page := pathlib.get_file(path: '${dir.path}/operational_plan.md')!
ops_page.write('# Operational Plan')!
mut hr_dir := pathlib.get_dir(path: '${dir.path}/human_resources')!
mut hr_page := pathlib.get_file(path: '${hr_dir.path}/human_resources.md')!
hr_page.template_write($tmpl('./templates/human_resources.md'), true)!
for key, employee in model.employees {
mut employee_page := pathlib.get_file(path: '${hr_dir.path}/${texttools.snake_case(employee.name)}.md')!
employee_cost_chart := model.sheet.line_chart(rowname:'hr_cost_${employee.name}', unit: .million)!.mdx()
employee_page.template_write($tmpl('./templates/employee.md'), true)!
}
mut depts_dir := pathlib.get_dir(path: '${dir.path}/departments')!
for key, department in model.departments {
mut dept_page := pathlib.get_file(path: '${depts_dir.path}/${texttools.snake_case(department.name)}.md')!
// dept_cost_chart := model.sheet.line_chart(rowname:'hr_cost_${employee.name}', unit: .million)!.mdx()
// println(employee_cost_chart)
dept_page.template_write($tmpl('./templates/department.md'), true)!
}
}
pub fn (model BizModel) write_revenue_model(path string) ! {
mut dir := pathlib.get_dir(path: '${path}/revenue_model')!
mut rm_page := pathlib.get_file(path: '${dir.path}/revenue_model.md')!
rm_page.write('# Revenue Model')!
mut products_dir := pathlib.get_dir(path: '${dir.path}/products')!
mut products_page := pathlib.get_file(path: '${products_dir.path}/products.md')!
products_page.template_write('# Products', true)!
name1 := 'example'
for key, product in model.products {
mut product_page := pathlib.get_file(path: '${products_dir.path}/${texttools.snake_case(product.name)}.md')!
product_page.template_write($tmpl('./templates/product.md'), true)!
}
}
pub fn (model BizModel) write_cost_structure(path string) ! {
mut dir := pathlib.get_dir(path: '${path}/cost_structure')!
mut cs_page := pathlib.get_file(path: '${dir.path}/cost_structure.md')!
cs_page.write('# Cost Structure')!
}

View File

@@ -0,0 +1,14 @@
module bizmodel
import os
import freeflowuniverse.herolib.web.docusaurus
const bizmodel_name = 'test'
const export_path = os.join_path(os.dir(@FILE), 'testdata')
pub fn test_export_report() ! {
model := getset(bizmodel_name)!
model.export_report(Report{
title: 'My Business Model'
}, path: export_path)!
}

View File

@@ -11,8 +11,8 @@ pub fn get(name string) !&BizModel {
if name in bizmodels {
return bizmodels[name] or { panic('bug') }
}
return error("cann't find biz model:'${name}' in global bizmodels ${bizmodels.keys()}")
}
return error("cann't find biz model:'${name}' in global bizmodels")
}
// get bizmodel from global

View File

@@ -62,7 +62,7 @@ fn employee_wiki(p paramsparser.Params, sim BizModel) !string {
// theme := 'light'
// theme := 'dark' // Removed unused variable
mut t := $tmpl('./templates/employee.md')
mut t := $tmpl('./templates/employee_old.md')
return t
}

View File

@@ -1,10 +1,13 @@
module bizmodel
import os
import freeflowuniverse.herolib.biz.spreadsheet
pub struct BizModel {
pub mut:
name string
description string
workdir string = '${os.home_dir()}/hero/var/bizmodel'
sheet &spreadsheet.Sheet
employees map[string]&Employee
departments map[string]&Department

View File

@@ -1,89 +1,51 @@
module bizmodel
import freeflowuniverse.herolib.core.playbook { PlayBook }
import arrays
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core.playbook { PlayBook, Action }
import freeflowuniverse.herolib.ui.console
// import freeflowuniverse.herolib.core.texttools
// import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.biz.spreadsheet
pub fn play(mut plbook PlayBook) ! {
// first make sure we find a run action to know the name
mut actions4 := plbook.actions_find(actor: 'bizmodel')!
if actions4.len == 0 {
return
}
knownactions := ['revenue_define', 'employee_define', 'department_define', 'funding_define',
'costcenter_define', 'cost_define']
for action in actions4 {
// biz name needs to be specified in the the bizmodel hero actions
bizname := action.params.get('bizname') or {
return error("Can't find param: 'bizname' for ${action.actor}.${action.name} macro, is a requirement argument.")
}
mut sim := getset(bizname)!
if action.name !in knownactions {
return error("Can't find macro with name: ${action.name} for macro's for bizmodel.")
}
console.print_debug(action.name)
match action.name {
'revenue_define' {
sim.revenue_action(action)!
}
'funding_define' {
sim.funding_define_action(action)!
}
'costcenter_define' {
sim.costcenter_define_action(action)!
}
else {}
}
}
console.print_debug('TOTALS for bizmodel play')
// now we have processed the macro's, we can calculate the totals
rlock bizmodels {
for _, mut sim in bizmodels {
// sim.hr_total()!
sim.cost_total()!
sim.revenue_total()!
sim.funding_total()!
}
}
for action in actions4 {
console.print_debug(action.name)
// biz name needs to be specified in the the bizmodel hero actions
bizname := action.params.get('bizname') or {
return error("Can't find param: 'bizname' for bizmodel macro, is a requirement argument.")
}
mut sim := get(bizname)!
if action.name !in knownactions {
return error("Can't find macro with name: ${action.name} for macro's for bizmodel.")
}
match action.name {
'cost_define' {
sim.cost_define_action(action)!
}
'department_define' {
sim.department_define_action(action)!
}
'employee_define' {
sim.employee_define_action(action)!
}
else {}
}
}
// mut sim:=get("test")!
// //println(sim.sheet.rows.keys())
// //println(spreadsheet.sheets_keys())
// println(spreadsheet.sheet_get('bizmodel_test')!)
// if true{panic("sss")}
const action_priorities = {
0: ['revenue_define', 'costcenter_define', 'funding_define']
1: ['cost_define', 'department_define', 'employee_define']
2: ['sheet_wiki', 'graph_bar_row', 'graph_pie_row', 'graph_line_row', 'row_overview']
}
pub fn play(mut plbook PlayBook) ! {
// group actions by which bizmodel they belong to
actions_by_biz := arrays.group_by[string, &Action](
plbook.actions_find(actor: 'bizmodel')!,
fn (a &Action) string {
return a.params.get('bizname') or {'default'}
}
)
// play actions for each biz in playbook
for biz, actions in actions_by_biz {
mut model := getset(biz)!
model.play(mut plbook)!
}
}
pub fn (mut m BizModel) play(mut plbook PlayBook) ! {
mut actions := plbook.actions_find(actor: 'bizmodel')!
for action in actions.filter(it.name in action_priorities[0]) {
m.act(*action)!
}
m.cost_total()!
m.revenue_total()!
m.funding_total()!
for action in actions.filter(it.name in action_priorities[1]) {
m.act(*action)!
}
for action in actions.filter(it.name in action_priorities[2]) {
m.act(*action)!
}
}

View File

@@ -3,7 +3,7 @@ module bizmodel
import freeflowuniverse.herolib.core.playbook { Action }
import freeflowuniverse.herolib.core.texttools
fn (mut m BizModel) cost_define_action(action Action) ! {
fn (mut m BizModel) cost_define_action(action Action) !Action {
mut name := action.params.get_default('name', '')!
mut descr := action.params.get_default('descr', '')!
if descr.len == 0 {
@@ -73,6 +73,7 @@ fn (mut m BizModel) cost_define_action(action Action) ! {
)!
m.sheet.row_delete('tmp3')
}
return action
}
fn (mut sim BizModel) cost_total() ! {

View File

@@ -3,7 +3,7 @@ module bizmodel
import freeflowuniverse.herolib.core.playbook { Action }
import freeflowuniverse.herolib.core.texttools
fn (mut m BizModel) costcenter_define_action(action Action) ! {
fn (mut m BizModel) costcenter_define_action(action Action) !Action {
mut name := action.params.get_default('name', '')!
mut descr := action.params.get_default('descr', '')!
if descr.len == 0 {
@@ -20,4 +20,5 @@ fn (mut m BizModel) costcenter_define_action(action Action) ! {
department: department
}
m.costcenters[name] = &cc
return action
}

View File

@@ -9,7 +9,7 @@ import freeflowuniverse.herolib.core.texttools
// - descr: description of the funding .
// - investment is month:amount,month:amount, ... .
// - type: loan or capital .
fn (mut m BizModel) funding_define_action(action Action) ! {
fn (mut m BizModel) funding_define_action(action Action) !Action {
mut name := action.params.get_default('name', '')!
mut descr := action.params.get_default('descr', '')!
if descr.len == 0 {
@@ -29,6 +29,7 @@ fn (mut m BizModel) funding_define_action(action Action) ! {
descr: descr
extrapolate: false
)!
return action
}
fn (mut sim BizModel) funding_total() ! {

View File

@@ -15,7 +15,7 @@ import freeflowuniverse.herolib.core.texttools
// department:'engineering'
// cost_percent_revenue e.g. 4%, will make sure the cost will be at least 4% of revenue
fn (mut m BizModel) employee_define_action(action Action) ! {
fn (mut m BizModel) employee_define_action(action Action) !Action {
mut name := action.params.get_default('name', '')!
mut descr := action.params.get_default('descr', '')!
if descr.len == 0 {
@@ -107,10 +107,7 @@ fn (mut m BizModel) employee_define_action(action Action) ! {
fulltime_perc: action.params.get_percentage_default('fulltime', '100%')!
}
// println(employee)
// todo: use existing id gen
if name != '' {
// sid = smartid.sid_new('')!
// // TODO: this isn't necessary if sid_new works correctly
@@ -120,9 +117,10 @@ fn (mut m BizModel) employee_define_action(action Action) ! {
// }
m.employees[name] = &employee
}
return action
}
fn (mut m BizModel) department_define_action(action Action) ! {
fn (mut m BizModel) department_define_action(action Action) !Action {
mut name := action.params.get_default('name', '')!
mut descr := action.params.get_default('descr', '')!
if descr.len == 0 {
@@ -141,6 +139,8 @@ fn (mut m BizModel) department_define_action(action Action) ! {
if name != '' {
m.departments[name] = &department
}
return action
}
// fn (mut sim BizModel) hr_total() ! {

View File

@@ -13,15 +13,13 @@ import freeflowuniverse.herolib.core.texttools
// - cogs_setup, cost of good for 1 item at setup
// - cogs_setup_delay, how many months before setup cogs starts, after sales
// - cogs_setup_perc: what is percentage of the cogs (can change over time) for setup e.g. 0:50%
// - cogs_monthly, cost of goods for the monthly per 1 item
// - cogs_monthly_delay, how many months before monthly cogs starts, after sales
// - cogs_monthly_perc: what is percentage of the cogs (can change over time) for monthly e.g. 0:5%,12:10%
// - nr_sold: how many do we sell per month (is in growth format e.g. 10:100,20:200)
// - nr_sold: how many do we sell per month (is in growth format e.g. 10:100,20:200, default is 1)
// - nr_months_recurring: how many months is recurring, if 0 then no recurring
//
fn (mut m BizModel) revenue_action(action Action) ! {
fn (mut m BizModel) revenue_action(action Action) !Action {
mut name := action.params.get_default('name', '')!
mut descr := action.params.get_default('descr', '')!
if descr.len == 0 {
@@ -62,6 +60,10 @@ fn (mut m BizModel) revenue_action(action Action) ! {
extrapolate: false
)!
println(action)
println(revenue)
exit(0)
mut revenue_setup := m.sheet.row_new(
name: '${name}_revenue_setup'
growth: action.params.get_default('revenue_setup', '0:0')!
@@ -139,11 +141,6 @@ fn (mut m BizModel) revenue_action(action Action) ! {
aggregatetype: .avg
)!
// if true{
// println(cogs_setup_perc)
// println(cogs_monthly_perc)
// panic("sdsd")
// }
mut nr_sold := m.sheet.row_new(
name: '${name}_nr_sold'
@@ -211,10 +208,6 @@ fn (mut m BizModel) revenue_action(action Action) ! {
nrmonths: nr_months_recurring
aggregatetype: .max
)!
// if true{
// println(nr_sold_recurring)
// panic('sd')
// }
}
// cogs as percentage of revenue
@@ -229,16 +222,17 @@ fn (mut m BizModel) revenue_action(action Action) ! {
name: '${name}_cogs_monthly_from_perc'
)!
// if true{
// println(revenue_setup_total)
// println(cogs_setup_perc)
// println(cogs_setup_from_perc)
// println("montlhy")
// println(revenue_monthly_total)
// println(cogs_monthly_perc)
// println(cogs_monthly_from_perc)
// panic("sdsd")
// }
println(action)
println(nr_sold)
println(revenue)
println(revenue_setup_total)
println(revenue_monthly_total)
println(cogs_setup_perc)
println(cogs_setup_from_perc)
println(cogs_monthly_perc)
println(cogs_monthly_from_perc)
exit(0)
// mut cogs_from_perc:=cogs_perc.action(action:.multiply,rows:[revenue],name:"cogs_from_perc")!
@@ -312,6 +306,7 @@ fn (mut m BizModel) revenue_action(action Action) ! {
// panic("sdsd")
// }
return action
}
// revenue_total calculates and aggregates the total revenue and cost of goods sold (COGS) for the business model

View File

@@ -0,0 +1,6 @@
# @{department.name}
`@{department.description}`
**Cost To The Company:**

View File

@@ -9,6 +9,7 @@
`@{employee.cost}`
@{employee_cost_chart}
@if employee.cost_percent_revenue > 0.0

View File

@@ -0,0 +1,27 @@
# @{employee.name}
`@{employee.description}`
> department: `@{employee.department}`
**Cost To The Company:**
`@{employee.cost}`
@if employee.cost_percent_revenue > 0.0
**Cost Percent Revenue:**
`@{employee.cost_percent_revenue}%`
@end
@if employee.nrpeople.len > 1
**Number of People in this group**
`@{employee.nrpeople}`
@end

View File

@@ -0,0 +1,7 @@
# Human Resources
| Name | Title | Nr People |
|------|-------|-------|
@for employee in model.employees.values()
| @{employee.name} | @{employee.title} | @{employee.nrpeople} |
@end

View File

@@ -2,53 +2,48 @@
## FUNDING
!!bizmodel.sheet_wiki includefilter:'funding'
@{bizmodel.sheet.wiki(includefilter:'funding')!}
## REVENUE vs COGS
!!bizmodel.sheet_wiki includefilter:rev
@{bizmodel.sheet.wiki(includefilter:'rev')!}
#### Revenue Lines
!!bizmodel.sheet_wiki title:'Revenue Total' includefilter:'revtotal'
@{bizmodel.sheet.wiki(title:'Revenue Total', includefilter:'revtotal')!}
#### COGS Lines
!!bizmodel.sheet_wiki title:'COGS' includefilter:'cogstotal'
@{bizmodel.sheet.wiki(title:'COGS', includefilter:'cogstotal')!}
## HR
!!bizmodel.sheet_wiki title:'HR Teams' includefilter:'hrnr'
!!bizmodel.sheet_wiki title:'HR Costs' includefilter:'hrcost'
@{bizmodel.sheet.wiki(title:'HR Teams', includefilter:'hrnr')!}
@{bizmodel.sheet.wiki(title:'HR Costs', includefilter:'hrcost')!}
## Operational Costs
!!bizmodel.sheet_wiki title:'COSTS' includefilter:'ocost'
@{bizmodel.sheet.wiki(title:'COSTS', includefilter:'ocost')!}
## P&L Overview
<!-- period is in months, 3 means every quarter -->
!!bizmodel.sheet_wiki title:'P&L Overview' includefilter:'pl'
@{bizmodel.sheet.wiki(title:'P&L Overview', includefilter:'pl')!}
!!bizmodel.graph_bar_row rowname:revenue_total unit:million title:'A Title' title_sub:'Sub'
@{bizmodel.graph_bar_row(rowname:'revenue_total', unit:'million', title:'A Title', title_sub:'Sub')!}
Unit is in Million USD.
!!bizmodel.graph_bar_row rowname:revenue_total unit:million
@{bizmodel.graph_bar_row(rowname:'revenue_total', unit:'million')!}
!!bizmodel.graph_line_row rowname:revenue_total unit:million
!!bizmodel.graph_pie_row rowname:revenue_total unit:million size:'80%'
@{bizmodel.graph_line_row(rowname:'revenue_total', unit:'million')!}
@{bizmodel.graph_pie_row(rowname:'revenue_total', unit:'million', size:'80%')!}
## Some Details
> show how we can do per month
!!bizmodel.sheet_wiki includefilter:'pl' period_months:1
@{bizmodel.sheet_wiki(includefilter:'pl', period_months:1)!}

View File

@@ -0,0 +1,49 @@
# @{model.name}
@{model.description}
## FUNDING
@{model.sheet.wiki(includefilter:['funding']) or {panic(err)}}
## REVENUE vs COGS
@{model.sheet.wiki(includefilter:['rev']) or {panic(err)}}
#### Revenue Lines
@{model.sheet.wiki(title:'Revenue Total', includefilter:['revtotal']) or {panic(err)}}
#### COGS Lines
@{model.sheet.wiki(title:'COGS', includefilter:['cogstotal']) or {panic(err)}}
## HR
@{model.sheet.wiki(title:'HR Teams', includefilter:['hrnr']) or {panic(err)}}
@{model.sheet.wiki(title:'HR Costs', includefilter:['hrcost']) or {panic(err)}}
## Operational Costs
@{model.sheet.wiki(title:'COSTS', includefilter:['ocost']) or {panic(err)}}
## P&L Overview
<!-- period is in months, 3 means every quarter -->
@{model.sheet.wiki(title:'P&L Overview', includefilter:['pl']) or {panic(err)}}
@{model.sheet.bar_chart(rowname:'revenue_total', unit: .million, title:'A Title', title_sub:'Sub') or {panic(err)}.mdx()}
Unit is in Million USD.
@{model.sheet.line_chart(rowname:'revenue_total', unit: .million) or {panic(err)}.mdx()}
@{model.sheet.pie_chart(rowname:'revenue_total', unit: .million, size:'80%') or {panic(err)}.mdx()}
## Some Details
> show how we can do per month
@{model.sheet.wiki(includefilter:['pl'], period_type:.month) or {panic(err)}}

View File

@@ -0,0 +1,62 @@
# @{product.title}
@{product.description}
#### parameters for the product
@if product.has_oneoffs
Product ${name1} has revenue events (one offs)
@{model.sheet.wiki() or {''}}
namefilter:'${name1}_revenue,${name1}_cogs,${name1}_cogs_perc,${name1}_maintenance_month_perc' sheetname:'bizmodel_tf9
- COGS = Cost of Goods Sold (is our cost to deliver the product/service)
- maintenance is fee we charge to the customer per month in relation to the revenue we charged e.g. 1% of a product which was sold for 1m EUR means we charge 1% of 1 m EUR per month.
@end //one offs
@if product.has_items
Product sold and its revenue/cost of goods
@{model.sheet.wiki() or {''}}
namefilter:'${name1}_nr_sold,${name1}_revenue_setup,${name1}_revenue_monthly,${name1}_cogs_setup,${name1}_cogs_setup_perc,${name1}_cogs_monthly,${name1}_cogs_monthly_perc'
sheetname:'bizmodel_tf9
- nr sold, is the nr sold per month of ${name1}
- revenue setup is setup per item for ${name1}, this is the money we receive. Similar there is a revenue monthly.
- cogs = Cost of Goods Sold (is our cost to deliver the product)
- can we as a setup per item, or per month per item
@if product.nr_months_recurring>1
This product ${name1} is recurring, means customer pays per month ongoing, the period customer is paying for in months is: **${product.nr_months_recurring}**
@end //recurring
@end
#### the revenue/cogs calculated
@{model.sheet.wiki() or {''}}
namefilter:'${name1}_nr_sold_recurring'
sheetname:'bizmodel_tf9
This results in following revenues and cogs:
@{model.sheet.wiki() or {''}}
namefilter:'${name1}_revenue_setup_total,${name1}_revenue_monthly_total,${name1}_cogs_setup_total,${name1}_cogs_monthly_total,${name1}_cogs_setup_from_perc,${name1}_cogs_monthly_from_perc,${name1}_maintenance_month,
${name1}_revenue_monthly_recurring,${name1}_cogs_monthly_recurring'
sheetname:'bizmodel_tf9
resulting revenues:
@{model.sheet.wiki() or {''}}
namefilter:'${name1}_revenue_total,${name1}_cogs_total'
sheetname:'bizmodel_tf9
!!!spreadsheet.graph_line_row rowname:'${name1}_cogs_total' unit:million sheetname:'bizmodel_tf9'
!!!spreadsheet.graph_line_row rowname:'${name1}_revenue_total' unit:million sheetname:'bizmodel_tf9'

View File

@@ -0,0 +1,68 @@
# @{product.title}
@{product.description}
#### parameters for the product
@if product.has_oneoffs
Product ${name1} has revenue events (one offs)
!!!spreadsheet.sheet_wiki
namefilter:'${name1}_revenue,${name1}_cogs,${name1}_cogs_perc,${name1}_maintenance_month_perc' sheetname:'bizmodel_tf9
- COGS = Cost of Goods Sold (is our cost to deliver the product/service)
- maintenance is fee we charge to the customer per month in relation to the revenue we charged e.g. 1% of a product which was sold for 1m EUR means we charge 1% of 1 m EUR per month.
@end //one offs
@if product.has_items
Product sold and its revenue/cost of goods
!!!spreadsheet.sheet_wiki
namefilter:'${name1}_nr_sold,${name1}_revenue_setup,${name1}_revenue_monthly,${name1}_cogs_setup,${name1}_cogs_setup_perc,${name1}_cogs_monthly,${name1}_cogs_monthly_perc'
sheetname:'bizmodel_tf9
- nr sold, is the nr sold per month of ${name1}
- revenue setup is setup per item for ${name1}, this is the money we receive. Similar there is a revenue monthly.
- cogs = Cost of Goods Sold (is our cost to deliver the product)
- can we as a setup per item, or per month per item
@if product.nr_months_recurring>1
This product ${name1} is recurring, means customer pays per month ongoing, the period customer is paying for in months is: **${product.nr_months_recurring}**
@end //recurring
@end
#### the revenue/cogs calculated
!!!spreadsheet.sheet_wiki
namefilter:'${name1}_nr_sold_recurring'
sheetname:'bizmodel_tf9
This results in following revenues and cogs:
!!!spreadsheet.sheet_wiki
namefilter:'${name1}_revenue_setup_total,${name1}_revenue_monthly_total,${name1}_cogs_setup_total,${name1}_cogs_monthly_total,${name1}_cogs_setup_from_perc,${name1}_cogs_monthly_from_perc,${name1}_maintenance_month,
${name1}_revenue_monthly_recurring,${name1}_cogs_monthly_recurring'
sheetname:'bizmodel_tf9
resulting revenues:
!!!spreadsheet.sheet_wiki
namefilter:'${name1}_revenue_total,${name1}_cogs_total'
sheetname:'bizmodel_tf9
!!!spreadsheet.graph_line_row rowname:'${name1}_cogs_total' unit:million sheetname:'bizmodel_tf9'
!!!spreadsheet.graph_line_row rowname:'${name1}_revenue_total' unit:million sheetname:'bizmodel_tf9'
@end //product has_revenue
@end //loop

View File

@@ -124,9 +124,4 @@ fn test_curr() {
console.print_debug(sh.rows['something'].cells[0])
assert sh.rows['something']!.cells[0].val == 25.0
assert sh.rows['something']!.cells[60 - 1].val == 900.0
// TODO: we need to create tests for it
console.print_debug(sh)
panic('test1')
}

View File

@@ -0,0 +1,138 @@
module spreadsheet
import freeflowuniverse.herolib.data.markdownparser.elements
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.web.echarts
pub fn (s Sheet) title_chart(args RowGetArgs) echarts.EChartsOption {
return echarts.EChartsOption{
title: echarts.Title{
text: args.title
subtext: args.title_sub
left: 'center'
}
}
}
pub fn (s Sheet) line_chart(args_ RowGetArgs) !echarts.EChartsOption {
mut args := args_
rownames := s.rownames_get(args)!
header := s.header_get_as_string(args.period_type)!
mut series := []echarts.Series{}
for rowname in rownames {
data := s.data_get_as_string(RowGetArgs{
...args
rowname: rowname
})!
series << echarts.Series{
name: rowname
type_: 'line'
stack: 'Total'
data: data.split(',')
}
}
return echarts.EChartsOption{
title: s.title_chart(args).title
tooltip: echarts.Tooltip{
trigger: 'axis'
}
legend: echarts.Legend{
data: rownames
}
grid: echarts.Grid{
left: '3%'
right: '4%'
bottom: '3%'
contain_label: true
}
toolbox: echarts.Toolbox{
feature: echarts.ToolboxFeature{
save_as_image: {}
}
}
x_axis: echarts.XAxis{
type_: 'category'
boundary_gap: false
data: header.split(',')
}
y_axis: echarts.YAxis{
type_: 'value'
}
series: series
}
}
pub fn (s Sheet) bar_chart(args_ RowGetArgs) !echarts.EChartsOption {
mut args := args_
args.rowname = s.rowname_get(args)!
header := s.header_get_as_list(args.period_type)!
data := s.data_get_as_list(args)!
return echarts.EChartsOption{
title: s.title_chart(args).title
x_axis: echarts.XAxis{
type_: 'category'
data: header
}
y_axis: echarts.YAxis{
type_: 'value'
}
series: [
echarts.Series{
name: args.rowname
type_: 'bar'
data: data
stack: ''
},
]
}
}
pub fn (s Sheet) pie_chart(args_ RowGetArgs) !echarts.EChartsOption {
mut args := args_
args.rowname = s.rowname_get(args)!
header := s.header_get_as_list(args.period_type)!
data := s.data_get_as_list(args)!
if header.len != data.len {
return error('Data and header lengths must match.')
}
mut pie_data := []map[string]string{}
for i, _ in data {
pie_data << {
'value': data[i].trim_space().trim("'")
'name': header[i].trim_space().trim("'")
}
}
return echarts.EChartsOption{
title: s.title_chart(args).title
tooltip: echarts.Tooltip{
trigger: 'item'
}
legend: echarts.Legend{
data: header
orient: 'vertical'
left: 'left'
}
series: [
echarts.Series{
name: 'Data'
type_: 'pie'
radius: args.size.int()
data: pie_data.map(it.str())
emphasis: echarts.Emphasis{
item_style: echarts.ItemStyle{
shadow_blur: 10
shadow_offset_x: 0
shadow_color: 'rgba(0, 0, 0, 0.5)'
}
}
},
]
}
}

View File

@@ -0,0 +1,77 @@
module spreadsheet
import freeflowuniverse.herolib.data.markdownparser.elements
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.web.echarts
fn test_title_chart() {
mut s := sheet_new() or { panic(err) }
mut nrnodes := s.row_new(
name: 'nrnodes'
growth: '5:100,55:1000'
tags: 'cat:nodes color:yellow urgent'
)!
args := RowGetArgs{
rowname: 'nrnodes'
title: 'Main Title'
title_sub: 'Subtitle'
}
title := s.title_chart(args).title
assert title.text == 'Main Title'
assert title.subtext == 'Subtitle'
assert title.left == 'center'
}
fn test_line_chart() {
mut s := sheet_new() or { panic(err) }
mut nrnodes := s.row_new(
name: 'nrnodes'
growth: '5:100,55:1000'
tags: 'cat:nodes color:yellow urgent'
)!
args := RowGetArgs{
rowname: 'nrnodes'
title: 'Line Chart'
period_type: .month
}
option := s.line_chart(args) or { panic(err) }
assert option.title.text == 'Line Chart'
assert option.tooltip.trigger == 'axis'
assert option.grid.contain_label == true
}
fn test_bar_chart() {
mut s := sheet_new() or { panic(err) }
mut nrnodes := s.row_new(
name: 'nrnodes'
growth: '5:100,55:1000'
tags: 'cat:nodes color:yellow urgent'
)!
args := RowGetArgs{
rowname: 'nrnodes'
title: 'Bar Chart'
period_type: .year
}
option := s.bar_chart(args) or { panic(err) }
assert option.title.text == 'Bar Chart'
assert option.x_axis.type_ == 'category'
assert option.y_axis.type_ == 'value'
}
fn test_pie_chart() {
mut s := sheet_new() or { panic(err) }
mut nrnodes := s.row_new(
name: 'nrnodes'
growth: '5:100,55:1000'
tags: 'cat:nodes color:yellow urgent'
)!
args := RowGetArgs{
rowname: 'nrnodes'
title: 'Pie Chart'
period_type: .quarter
}
option := s.pie_chart(args) or { panic(err) }
assert option.title.text == 'Pie Chart'
assert option.tooltip.trigger == 'item'
assert option.legend.data.len > 0
}

View File

@@ -92,7 +92,7 @@ pub fn playmacro(action Action) !string {
content = sh.wiki(args) or { panic(err) }
}
'graph_title_row' {
content = sh.wiki_title_chart(args)
content = sh.wiki_title_chart(args)!
}
'graph_line_row' {
content = sh.wiki_line_chart(args)!

View File

@@ -95,7 +95,7 @@ pub fn (mut r Row) cell_get(colnr int) !&Cell {
return &r.cells[colnr]
}
pub fn (mut r Row) values_get() []f64 {
pub fn (r Row) values_get() []f64 {
mut out := []f64{}
for cell in r.cells {
out << cell.val

View File

@@ -202,7 +202,7 @@ pub fn (s Sheet) tosmaller(args_ ToYearQuarterArgs) !&Sheet {
// tagsfilter []string
// tags if set will see that there is at least one corresponding tag per row
// rawsfilter is list of names of rows which will be included
pub fn (mut s Sheet) toyear(args ToYearQuarterArgs) !&Sheet {
pub fn (s Sheet) toyear(args ToYearQuarterArgs) !&Sheet {
mut args2 := args
args2.period_months = 12
return s.tosmaller(args2)
@@ -215,7 +215,7 @@ pub fn (mut s Sheet) toyear(args ToYearQuarterArgs) !&Sheet {
// tagsfilter []string
// tags if set will see that there is at least one corresponding tag per row
// rawsfilter is list of names of rows which will be included
pub fn (mut s Sheet) toquarter(args ToYearQuarterArgs) !&Sheet {
pub fn (s Sheet) toquarter(args ToYearQuarterArgs) !&Sheet {
mut args2 := args
args2.period_months = 3
return s.tosmaller(args2)
@@ -259,13 +259,15 @@ pub fn (mut s Sheet) json() string {
}
// find row, report error if not found
pub fn (mut s Sheet) row_get(name string) !&Row {
mut row := s.rows[name] or { return error('could not find row with name: ${name}') }
pub fn (s Sheet) row_get(name string) !&Row {
row := s.rows[name] or {
return error('could not find row with name: ${name}, available rows: ${s.rows.keys()}')
}
return row
}
pub fn (mut s Sheet) values_get(name string) ![]f64 {
mut r := s.row_get(name)!
pub fn (s Sheet) values_get(name string) ![]f64 {
r := s.row_get(name)!
vs := r.values_get()
return vs
}

View File

@@ -0,0 +1,49 @@
module spreadsheet
import os
import freeflowuniverse.herolib.core.pathlib
@[params]
pub struct ExportArgs{
pub mut:
path string
}
fn format_number(val f64) string {
if val < 0.001 && val > -0.001 {
return '0'
}
if val >= 1000.0 || val <= -1000.0 {
return int(val).str()
}
// Format small numbers with 3 decimal places to handle floating point precision
return '${val:.3f}'
}
pub fn (mut s Sheet) export(args ExportArgs) !string {
mut result := []string{}
// Add headers
mut header_row := ['Name', 'Description', 'AggregateType', 'Tags', 'Subgroup']
header_row << s.header()!
result << header_row.join('|')
// Add rows
for _, row in s.rows {
mut row_data := [row.name, row.description, row.aggregatetype.str(), row.tags, row.subgroup]
for cell in row.cells {
if cell.empty {
row_data << '-'
} else {
row_data << format_number(cell.val)
}
}
result << row_data.join('|')
}
if args.path.len>0{
mut p:=pathlib.get_file(path:args.path.replace("~",os.home_dir()), create:true, delete:true)!
p.write(result.join('\n'))!
}
return result.join('\n')
}

Some files were not shown because too many files have changed in this diff Show More