Browse Source

Merge branch 'work_with_orgs' into 'master'

rework orgs. Add various properties to orgs. Make all org functions return...

See merge request medlabboulder/modpol!3
Nathan Schneider 3 years ago
parent
commit
418926f551
4 changed files with 414 additions and 33 deletions
  1. 50 0
      modpol/DOCS/ledgers
  2. 137 0
      modpol/DOCS/orgs
  3. 224 30
      modpol/orgs/orgs.lua
  4. 3 3
      modpol_minetest/chatcommands/chatcommands.lua

+ 50 - 0
modpol/DOCS/ledgers

@@ -0,0 +1,50 @@
+Ledger
+**************************
+
+a legder is a table of records that holds the history of an org, and of modpol itself.
+
+its structure is a table of legder entries.
+
+Every org has one, starting with the entry recording its creation.
+
+Ledger Entry
+**************************
+a legder entry is a table with the following properties:
+
+timestamp = nil,
+entrytype = nil,
+action_msg = '',
+
+the timestamp will hold the output of os.time at the moment the entry was created.
+
+the entrytype is optional; it can hold an identifier of the kind of entry it is (e.g. 'org_init', 'new_user', etc etc.)
+
+action_msg is the record output itself.
+
+
+
+Entrytypes
+**************************
+
+These are the entrytypes that modpol uses. Each additional module should document whatever entrytypes it adds.
+
+'org_init'
+type used when an org is created
+
+'org_purge'
+type used when all orgs are purged. -- will only be found in the main modpol ledger.
+
+'org_remove'
+type used when removing an individual org
+
+'add_member'
+type used when adding a member to an org
+
+'remove_member'
+type used when removing a member from an org
+
+
+
+Old orgs
+**************************
+when an org is removed, its ledger is stored in modpol.old_legders = {} Note that multiple ledgers may exist here with the same org name.

+ 137 - 0
modpol/DOCS/orgs

@@ -0,0 +1,137 @@
+== Dev note ===
+*note, see doc ledgers for info about org ledgers.
+they are *not* just a message in a table anymore... they have a timestamp and more.
+===============
+
+
+Orgs are stored in modpol.orgs by the key 'org_name'
+
+Old ledgers of deleted orgs are stored in modpol.old_ledgers, 
+if preserve records is enabled
+
+An org is a table with the following properties:
+
+{
+   name = '', -- a unique name... 
+        this is a reference to the key used to store the org table. Changing this does not change the org name. 
+   policies = {}, -- a table to store the org's policies
+   members = {}, -- a table that contains the member names 
+        or the org, in no particular order (TKTK: maybe change it so that members have a name:properties table?)
+   ledger = {}, -- a table of ledger entries. See the ledger docs
+   parent = nil, -- the name of the org that spawned this org.
+        removing a parent org removes its children
+   children = {}, -- a table of strings of org names of child orgs
+   properties = { -- good for modules to store arbitrary info about the org here.
+      
+   },
+
+}
+
+
+API functions for orgs
+======================
+
+a local variable setting 'preserve_records' determines whether 
+to store old ledgers when an org is deleted. It is set to true. 
+It would be possible to extend this to a settings file later.
+
+--==oo888888888888888888888888888888oo==--
+Function: modpol.record(org_name, msg, type)
+-- Params: strings msg, org, type
+-- Outputs:
+-- "msg" specifies an event and/or status message.
+-- "org" specifies an "org" name.
+-- "type" -- optional type of legder entry. Could be e.g. 'election_result', 'add_member', etc
+
+
+-- This  function adds  the message to a  global ledger  and, if "org"
+-- specifies a valid "org", to an "org"-specific ledger. Both the mem-
+-- ory-resident and on-disk copies of the data structures used are up-
+-- dated.
+-- the type input is used to be able to search ledgers for specific events
+
+--==oo888888888888888888888888888888oo==--
+
+Function: modpol.add_org(org_name, members, parent, properties)
+-- Params:   string name, table members, string parent, table properties
+-- Parent must be an existing org, defaults to 'instance', properties 
+-- are arbitrary properties to initialize the org with
+
+-- Output:
+-- This function creates an "org".  It returns a boolean success indicator and
+-- either an error message starting with "Error:" or a success message.
+--
+--
+-- The string parameter specifies the "org" name.
+--
+-- The members table should  be an  integer-indexed array of member
+-- names.
+
+-- parent should be nil or a valid existing org name. If nil, defaults to 
+-- 'instance'
+
+-- properties defaults to an empty table. Use it to initialize the org 
+-- with arbitrary properties
+
+--==oo888888888888888888888888888888oo==--
+
+-- Function: modpol.remove_org(org_name, reason)
+-- Params:   string name, opt string reason
+-- Output:
+--
+-- This function removes an "org".  It returns a boolean
+-- success indicator and either an error message
+-- starting with "Error:" or a success message. It also recursively 
+-- removes all child orgs, with 'remove parent org' as reason. If 
+-- preserve_records is enabled, it logs the removal in the org ledger,
+-- and stores the ledger in modpol.old_legders
+--
+-- The string parameter specifies the "org" name.
+--
+-- The reason is an optional string to additionally include in the 
+-- log as reason for removal
+
+--==oo888888888888888888888888888888oo==--
+
+Function: modpol.list_orgs()
+-- Params:   None
+-- Output:
+-- This function returns a text-format list of "orgs".  The list shows
+-- one "org" per line in the following format:
+-- org_name (member, member, ...)
+-- If there are no "orgs", the output is an empty string.
+
+--==oo888888888888888888888888888888oo==--
+
+
+Function: modpol.reset_orgs()
+-- Params:   None
+-- Removes all orgs and recreates blank org "instance" with all
+-- current users
+-- returns confirmation message
+-- if preserve_records is enabled, stores all org ledgers
+-- in modpol.old_ledgers
+
+--==oo888888888888888888888888888888oo==--
+
+Function: modpol.add_member(org_name, member)
+-- Params: org_name (string), member (string)
+-- Output:
+-- Adds the specified member to the specified org
+-- Returns a boolean success indicator and
+-- either a confirmation or error message
+
+--==oo888888888888888888888888888888oo==--
+
+Function: modpol.is_member
+-- Params: org (string), member (string)
+-- Output: boolean, or nil and error message if not applicable. (org nonexistent)
+
+
+--==oo888888888888888888888888888888oo==--
+
+Function: modpol.remove_member
+-- Params: org (string), member (string)
+-- Output:
+-- Removes the specified member from the specified org
+-- Returns confirmation or error message

+ 224 - 30
modpol/orgs/orgs.lua

@@ -1,30 +1,94 @@
+
 -- ===================================================================
 -- /orgs.lua
 -- Org-related functions for Modular Politics
 -- Called by modpol/modpol/api.lua
+-- ===================================================================
+-- preserve records -- if true, will store org ledgers when they are closed, for later reference.
+local preserve_records = true
+modpol.default_org_policies = modpol.default_org_policies or {}
+
+
+-- initiate the table of orgs
+
+modpol.orgs = {} 
+if preserve_records then
+   modpol.old_ledgers = {} -- a backup of all legders of all closed orgs.
+end
+
+
+--define the template of a ledger entry
+modpol.ledger_entry_temp = {
+   timestamp = '',
+   entrytype = nil,
+   action_msg = '',
+   org_name = nil,
+}
+
+
+-- define the basic template of an org.
+
+modpol.org_temp = {
+   name = nil,
+   policies = {},
+   members = {},
+   ledger = {},
+   parent = nil,
+   children = {},
+   properties = {},
+   old_names = {},
+
+}
+
+-- ===================================================================
+-- Function: modpol.create_ledger_entry
+-- Params: strings action_msg, org_name, type
+-- Outputs:
+-- returns a ledger entry table, with a timestamp
+-- This function accepts a message and an optional org_name and type specification
+-- and returns a valid ledger entry table
+
+modpol.create_ledger_entry = function(action_msg, org_name, entry_type)
+   local entry = modpol.ledger_entry_temp
+   if action_msg and type(action_msg) == 'string' then
+      entry.action_msg = action_msg
+   end
+   if org_name and type(org_name) == 'string' and modpol.orgs [org_name] then
+      entry.org_name = org_name
+   end
+   if entry_type and type(entry_type) == 'string' then
+      entry.entrytype = entry_type
+   end
+   entry.timestamp = os.time()
+   return entry
+end
+
 
 -- ===================================================================
 -- Function: modpol.record
--- Params: strings msg, org
+-- Params: strings msg, org, type
 -- Outputs:
 -- "msg" specifies an event and/or status message.
 -- "org" specifies an "org" name.
+-- "type" -- optional type of legder entry. Could be e.g. 'election_result', 'add_member', etc
 -- This  function adds  the message to a  global ledger  and, if "org"
 -- specifies a valid "org", to an "org"-specific ledger. Both the mem-
 -- ory-resident and on-disk copies of the data structures used are up-
 -- dated.
 
-modpol.record = function (org_name, msg)
+modpol.record = function (org_name, msg, type)
+
+   local entry = modpol.create_ledger_entry(msg,org_name,type)
                                 -- Record in global ledger
-   table.insert (modpol.ledger, msg)
+   table.insert (modpol.ledger, entry)
                                 -- Record in "org"-specific ledger
    if modpol.orgs [org_name] ~= nil then
         local org_ledger =  modpol.orgs [org_name]["ledger"]
         if    org_ledger == nil then
-            modpol.orgs [org_name]["ledger"] = { msg }
+            modpol.orgs [org_name]["ledger"] = { entry }
         else
             modpol.orgs [org_name]["ledger"] =
-                table.insert (org_ledger, msg)
+                table.insert (org_ledger, entry)
         end
     end
 
@@ -33,39 +97,144 @@ end
 
 -- ===================================================================
 -- Function: modpol.add_org
--- Params:   string name, table members
+-- Params:   string name, table members, string parent, table properties
+
 -- Output:
 --
--- This function creates an "org".  It returns either an error message
--- starting with "-!-" or a success message.
+-- This function creates an "org".  It returns a boolean success indicator and
+-- either an error message starting with "Error:" or a success message.
 --
 -- The string parameter specifies the "org" name.
 --
--- The  specified table should  be an  integer-indexed array of member
+-- The members table should  be an  integer-indexed array of member
 -- names.
 
-modpol.add_org = function (org_name, members)
+-- parent should be nil or a valid existing org name. If nil, defaults to 
+-- 'instance'
+
+-- properties defaults to an empty table. Use it to initialize the org 
+-- with arbitrary properties
+
+modpol.add_org = function(org_name, members, parent, properties)
     local str
 
     if modpol.ocutil.str_empty (org_name) then
-        return "Error: Org needs a name"
+        return false,"Error: Org needs a name"
+    end
+
+    if parent and not modpol.orgs[parent] then 
+      return false,"Error: Parent must be a valid org"
+    end
+
+    if properties and not type(properties) == 'table' then
+      return false,"Error: Properties must be a table"
     end
 
     modpol.ocutil.log ("Adding org " .. org_name)
     if modpol.orgs [org_name] ~= nil then
         str = "Error: Org " .. org_name .. " already exists"
         modpol.ocutil.log (str)
-        return str
+        return false,str
+    end
+
+    local parent_org = parent or 'instance'
+    local props = properties or {}
+
+   
+    -- actually add the org
+    local org = modpol.org_temp
+    local default_policies = modpol.default_org_policies or {}
+    org.name = org_name -- do not change this without the proper api function. This is only a reference, not the only place this is recorded.
+    org.members = members
+    org.parent = parent_org
+    org.properties = props
+    org.policies = default_policies
+
+    modpol.orgs [org_name] = org
+
+    --record the child org in the parent's children table
+    if modpol.orgs[parent_org].children then
+      table.insert(modpol.orgs[parent_org].children, org_name)
+    else
+      modpol.orgs[parent].children = {org_name}
     end
 
-    modpol.orgs [org_name] = { members=members }
     local msg = "New org: " .. org_name ..
         " (" .. table.concat (members, ", ") .. ")"
 
-   modpol.record (org_name, msg)
-   return msg
+   modpol.record (org_name, msg, 'org_init')
+   return true, msg
+end
+
+
+
+
+-- ===================================================================
+-- Function: modpol.remove_org
+-- Params:   string name, opt string reason
+-- Output:
+--
+-- This function removes an "org".  It returns a boolean
+-- success indicator and either an error message
+-- starting with "Error:" or a success message. It also recursively 
+-- removes all child orgs, with 'remove parent org' as reason. If 
+-- preserve_records is enabled, it logs the removal in the org ledger,
+-- and stores the ledger in modpol.old_legders
+
+
+-- The string parameter specifies the "org" name.
+--
+-- The reason is an optional string to additionally include in the 
+-- log as reason for removal
+
+modpol.remove_org = function (org_name, reason)
+   local str
+   if not reason then reason = '' end
+   if modpol.ocutil.str_empty (org_name) then
+       return false,"Error: Specify an org name to remove"
+   end
+
+   if org_name == 'instance' then
+      return false,"Error: You cannot remove instance"
+   end
+
+   modpol.ocutil.log ("Removing org " .. org_name)
+   if modpol.orgs [org_name] == nil then
+       str = "Error: Cannot remove non-existant org " .. org_name
+       modpol.ocutil.log (str)
+       return false,str
+   end
+
+   --log msg
+   local msg = 'Removing org '..org_name ..', reason: '.. reason
+
+   -- actually remove the org
+   modpol.record (org_name, msg, 'org_remove')
+
+   -- TODO: if callbacks are implemented, do so here --
+   if preserve_records then
+      modpol.old_ledgers = modpol.orgs[org_name].ledger
+   end
+
+   -- also remove all children
+   if modpol.orgs[org_name].children and #modpol.orgs[org_name].children > 0 then
+      for _,child_name in pairs(modpol.orgs[org_name].children) do
+         modpol.remove_org(child_name, 'remove parent org') --this is recursion. If it gets to be a problem, we will need a separate function
+      end
+   end
+
+   modpol.orgs[org_name] = nil
+
+  
+  return true,msg
+
 end
 
+
+
+
+
+
 -- ===================================================================
 -- Function: modpol.list_orgs
 -- Params:   None
@@ -102,9 +271,17 @@ end
 
 modpol.reset_orgs = function()
    local users = modpol.list_users()
+   --store the ledgers in modpol.old_ledgers, and note that each org was closed in each record.
+   if preserve_records then
+      for name, org in pairs(modpol.orgs) do
+         local old_ledger = org.ledger
+         table.insert(old_ledger,modpol.create_ledger_entry('Removing org '.. name, name, 'org_purge'))
+         table.insert(modpol.old_ledgers, org.ledger)
+      end
+   end
    modpol.orgs = {}
    local message = "Orgs purged"
-   modpol.record(nil, message)
+   modpol.record(nil, message, 'org_purge')
    modpol.add_org("instance", users)
    return message
 end
@@ -114,19 +291,25 @@ end
 -- Params: org (string), member (string)
 -- Output:
 -- Adds the specified member to the specified org
--- Returns confirmation or error message
+-- Returns a boolean success indicator and
+-- either a confirmation or error message
 
-modpol.add_member = function(org, member)
-   if (modpol.orgs[org] == nil) then
-      return "Error: No such org"
+
+-- TODO, check that member is a user, if the org_name is not instance
+
+modpol.add_member = function(org_name, member)
+   if (modpol.orgs[org_name] == nil) then
+      return false,"Error: No such org"
+   elseif type(member) ~= 'string' then
+      return false,"Error: member is wrong type"
    else
-      if modpol.is_member(org,member) then
-         return "Error: " .. member .. " is already a member of " .. org
+      if modpol.is_member(org_name,member) then
+         return false,"Error: " .. member .. " is already a member of " .. org_name
       else
-         table.insert(modpol.orgs[org]["members"], member)
-         local message = member .. " added to org " .. org
-         modpol.record(message, org)
-         return message
+         table.insert(modpol.orgs[org_name]["members"], member)
+         local message = member .. " added to org " .. org_name
+         modpol.record(org_name, message, 'add_member')
+         return true,message
       end
    end
 end
@@ -165,22 +348,26 @@ end
 function modpol.remove_member(org, member)
    local message = "Error: No such org"
    if (modpol.orgs[org] == nil) then
-      return nil, message
+      return false, message
    else
       local member_table = modpol.orgs[org]["members"]
       if member_table then
          for index, value in pairs(member_table) do
             if value == member then
                -- match found, set to nil
-               value = nil               
+               
+               modpol.orgs[org]["members"][value] = nil  
+               
+               -- TODO: add callbacks here --
                message = member .. " removed from org " .. org
-               return message
+               modpol.record(org_name, message, 'remove_member')
+               return true, message
             end
          end
       end
       -- no match found (or org has no members)
       message = member .. " not found in org " .. org
-      return nil, message
+      return false, message
    end
 end
 
@@ -189,6 +376,13 @@ end
 -- rename_org(old_name, new_name)
 -- Renames an org 
 
+
+   
+
+
+
+
+
 -- ===================================================================
 -- POLICIES
 -- Each policy is a table with a string for a key. Contents:

+ 3 - 3
modpol_minetest/chatcommands/chatcommands.lua

@@ -16,8 +16,8 @@ regchat = minetest.register_chatcommand
 chat_table = {
    privs        = {} ,
    func         = function (user, param)
-      local result = modpol.add_org (param, { user })
-      return true, result
+      local success, message = modpol.add_org (param, { user })
+      return true, message
    end        
 }
 regchat ("addorg"  , chat_table)
@@ -59,7 +59,7 @@ minetest.register_chatcommand(
    "joinorg", {
       privs = {},
       func = function(user, param)
-         local result = modpol.add_member(param, user)
+         local success, result = modpol.add_member(param, user)
          return true, result
       end,
 })