Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
Changes
=======

1.2.0 (unreleased)
------------------
1.2.0b4 (unreleased)
--------------------

- Nothing changed yet.


1.2.0b3 (2025-04-17)
--------------------

- Now includes default values when saving, making the OCF files larger.

- It's now possible to pass in the data to the Captable object when
creating it, instead of adding the data to the attribute lists later.


1.2.0b2 (2025-02-04)
--------------------

- Added the new files to the format.


1.2.0b1 (2025-01-10)
--------------------

- Updated schema to 1.2.0


1.1.0 (2025-01-09)
------------------

Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ ifndef NO_VENV
bin_dir := $(root_dir)/ve/bin/
python_exe := $(bin_dir)python3
endif
git_source := https://github.com/Open-Cap-Table-Coalition/Open-Cap-Format-OCF.git
git_branch := release-v1.1.0
git_source := https://github.com/regebro/Open-Cap-Format-OCF.git
git_branch := lregebro-v1.2.0

all: devenv fetch build

Expand Down
29 changes: 19 additions & 10 deletions docs/source/using.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,12 @@ but you then need to create ``File`` objects for each file, with dummy md5 hashe
... id="d6c49a5a-257d-4b41-9f1d-073a77dfe719",
... name={"legal_name": "Person Y"},
... stakeholder_type="INDIVIDUAL",
... comments=[],
... )
... )

>>> cap.transactions.append(
... api.PlanSecurityIssuance(
... object_type="TX_PLAN_SECURITY_ISSUANCE",
... api.EquityCompensationIssuance(
... object_type="TX_EQUITY_COMPENSATION_ISSUANCE",
... stock_plan_id="test",
... id="Success OPTION",
... custom_id="test",
Expand All @@ -65,9 +64,24 @@ but you then need to create ``File`` objects for each file, with dummy md5 hashe
... security_id="",
... date="2022-12-12",
... security_law_exemptions=[],
... expiration_date=None,
... )
... )

It is also possible to create the list of objects first, and pass them in to the
Captable constructor::

>>> sh_list = [
... api.Stakeholder(
... object_type="STAKEHOLDER",
... id="'917efd77a370-d1f9-14b4-d752-a5a94c6d",
... name={"legal_name": "Person X"},
... stakeholder_type="INDIVIDUAL",
... )
... }

>>> cap2 = Captable(stakeholders=sh_list)

And once you have filled in all the lists with all the information, you save
the captable:

Expand Down Expand Up @@ -101,13 +115,8 @@ A captable will then be created and Python objects will be stored in it.
'pyocf example docs'

>>> cap.stakeholders # doctest: +NORMALIZE_WHITESPACE
[Stakeholder(id='d6c49a5a-257d-4b41-9f1d-073a77dfe719', comments=[],
object_type='STAKEHOLDER', name=Name(legal_name='Person Y', first_name=None,
last_name=None), stakeholder_type=<StakeholderType.ENUM_INDIVIDUAL:
'INDIVIDUAL'>, issuer_assigned_id=None, current_relationship=None,
primary_contact=None, addresses=None, tax_ids=None),
Stakeholder(id='d6c49a5a-257d-4b41-9f1d-073a77dfe719', comments=[],
[Stakeholder(id='d6c49a5a-257d-4b41-9f1d-073a77dfe719', comments=None,
object_type='STAKEHOLDER', name=Name(legal_name='Person Y', first_name=None,
last_name=None), stakeholder_type=<StakeholderType.ENUM_INDIVIDUAL:
'INDIVIDUAL'>, issuer_assigned_id=None, current_relationship=None,
primary_contact=None, addresses=None, tax_ids=None)]
primary_contact=None, contact_info=None, addresses=None, tax_ids=None)]
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = pyocf
version = 1.2.0.dev0
version = 1.2.0b4.dev0
description = Open Captable Format objects and parser
long_description = file: README.rst, CONTRIBUTORS.txt, CHANGES.txt
classifiers =
Expand Down
32 changes: 32 additions & 0 deletions src/pyocf/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from pyocf.enums.accrualperiodtype import AccrualPeriodType
from pyocf.enums.addresstype import AddressType
from pyocf.enums.allocationtype import AllocationType
from pyocf.enums.authorizedshares import AuthorizedShares
from pyocf.enums.compensationtype import CompensationType
from pyocf.enums.compoundingtype import CompoundingType
from pyocf.enums.conversionmechanismtype import ConversionMechanismType
Expand All @@ -22,6 +23,7 @@
from pyocf.enums.parentsecuritytype import ParentSecurityType
from pyocf.enums.periodtype import PeriodType
from pyocf.enums.phonetype import PhoneType
from pyocf.enums.quantitysourcetype import QuantitySourceType
from pyocf.enums.roundingtype import RoundingType
from pyocf.enums.stakeholderrelationshiptype import StakeholderRelationshipType
from pyocf.enums.stakeholdertype import StakeholderType
Expand All @@ -31,9 +33,12 @@
StockPlanCancellationBehaviorType,
)
from pyocf.enums.terminationwindowtype import TerminationWindowType
from pyocf.enums.valuationbasedformulatype import ValuationBasedFormulaType
from pyocf.enums.valuationtype import ValuationType
from pyocf.enums.vestingdayofmonth import VestingDayOfMonth
from pyocf.enums.vestingtriggertype import VestingTriggerType
from pyocf.files.documentsfile import DocumentsFile
from pyocf.files.financingsfile import FinancingsFile
from pyocf.files.ocfmanifestfile import OCFManifestFile
from pyocf.files.stakeholdersfile import StakeholdersFile
from pyocf.files.stockclassesfile import StockClassesFile
Expand All @@ -42,6 +47,8 @@
from pyocf.files.transactionsfile import TransactionsFile
from pyocf.files.valuationsfile import ValuationsFile
from pyocf.files.vestingtermsfile import VestingTermsFile
from pyocf.objects.document import Document
from pyocf.objects.financing import Financing
from pyocf.objects.issuer import Issuer
from pyocf.objects.stakeholder import Stakeholder
from pyocf.objects.stockclass import StockClass
Expand All @@ -58,6 +65,9 @@
)
from pyocf.objects.transactions.acceptance.stockacceptance import StockAcceptance
from pyocf.objects.transactions.acceptance.warrantacceptance import WarrantAcceptance
from pyocf.objects.transactions.adjustment.issuerauthorizedsharesadjustment import (
IssuerAuthorizedSharesAdjustment,
)
from pyocf.objects.transactions.adjustment.stockclassauthorizedsharesadjustment import (
StockClassAuthorizedSharesAdjustment,
)
Expand Down Expand Up @@ -142,6 +152,7 @@
from pyocf.primitives.objects.transactions.conversion.conversion import Conversion
from pyocf.primitives.objects.transactions.exercise.exercise import Exercise
from pyocf.primitives.objects.transactions.issuance.issuance import Issuance
from pyocf.primitives.objects.transactions.issuertransaction import IssuerTransaction
from pyocf.primitives.objects.transactions.reissuance.reissuance import Reissuance
from pyocf.primitives.objects.transactions.release.release import Release
from pyocf.primitives.objects.transactions.repurchase.repurchase import Repurchase
Expand Down Expand Up @@ -194,6 +205,12 @@
from pyocf.types.conversion_mechanisms.safeconversionmechanism import (
SAFEConversionMechanism,
)
from pyocf.types.conversion_mechanisms.sharepricebasedconversionmechanism import (
SharePriceBasedConversionMechanism,
)
from pyocf.types.conversion_mechanisms.valuationbasedconversionmechanism import (
ValuationBasedConversionMechanism,
)
from pyocf.types.conversion_rights.convertibleconversionright import (
ConvertibleConversionRight,
)
Expand Down Expand Up @@ -230,6 +247,7 @@
from pyocf.types.monetary import Monetary
from pyocf.types.name import Name
from pyocf.types.numeric import Numeric
from pyocf.types.objectreference import ObjectReference
from pyocf.types.percentage import Percentage
from pyocf.types.phone import Phone
from pyocf.types.ratio import Ratio
Expand All @@ -238,6 +256,7 @@
from pyocf.types.stockparent import StockParent
from pyocf.types.taxid import TaxID
from pyocf.types.terminationwindow import TerminationWindow
from pyocf.types.vesting import Vesting
from pyocf.types.vesting.vestingcondition import VestingCondition
from pyocf.types.vesting.vestingconditionportion import VestingConditionPortion
from pyocf.types.vesting.vestingeventtrigger import VestingEventTrigger
Expand All @@ -257,6 +276,7 @@
Address,
AddressType,
AllocationType,
AuthorizedShares,
AutomaticConversionOnConditionTrigger,
AutomaticConversionOnDateTrigger,
Cancellation,
Expand Down Expand Up @@ -288,6 +308,8 @@
CustomConversionMechanism,
Date,
DayCountType,
Document,
DocumentsFile,
ElectiveConversionAtWillTrigger,
ElectiveConversionInDateRangeTrigger,
ElectiveConversionOnConditionTrigger,
Expand All @@ -304,18 +326,23 @@
File,
FileObject,
FileType,
Financing,
FinancingsFile,
FixedAmountConversionMechanism,
InterestPayoutType,
InterestRate,
Issuance,
Issuer,
IssuerAuthorizedSharesAdjustment,
IssuerTransaction,
Md5,
Monetary,
Name,
NoteConversionMechanism,
Numeric,
OCFManifestFile,
Object,
ObjectReference,
ObjectType,
OptionType,
ParentSecurityType,
Expand All @@ -331,6 +358,7 @@
PlanSecurityRelease,
PlanSecurityRetraction,
PlanSecurityTransfer,
QuantitySourceType,
Ratio,
RatioConversionMechanism,
Reissuance,
Expand All @@ -343,6 +371,7 @@
SecurityExemption,
SecurityTransaction,
ShareNumberRange,
SharePriceBasedConversionMechanism,
Stakeholder,
StakeholderRelationshipType,
StakeholderType,
Expand Down Expand Up @@ -381,8 +410,11 @@
Transfer,
UnspecifiedConversionTrigger,
Valuation,
ValuationBasedConversionMechanism,
ValuationBasedFormulaType,
ValuationType,
ValuationsFile,
Vesting,
VestingAcceleration,
VestingCondition,
VestingConditionPortion,
Expand Down
71 changes: 54 additions & 17 deletions src/pyocf/captable.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import pathlib
import zipfile

from pyocf.files import documentsfile
from pyocf.files import financingsfile
from pyocf.files import ocfmanifestfile
from pyocf.files import stakeholdersfile
from pyocf.files import stockclassesfile
Expand All @@ -16,6 +18,8 @@
from pyocf.files import transactionsfile
from pyocf.files import valuationsfile
from pyocf.files import vestingtermsfile
from pyocf.objects.document import Document
from pyocf.objects.financing import Financing
from pyocf.objects.stakeholder import Stakeholder
from pyocf.objects.stockclass import StockClass
from pyocf.objects.stocklegendtemplate import StockLegendTemplate
Expand All @@ -26,25 +30,53 @@
from pyocf.types import file

FILEMAP = [
("stock_plans", stockplansfile.StockPlansFile),
("stock_legend_templates", stocklegendtemplatesfile.StockLegendTemplatesFile),
("documents", documentsfile.DocumentsFile),
("financings", financingsfile.FinancingsFile),
("stakeholders", stakeholdersfile.StakeholdersFile),
("stock_classes", stockclassesfile.StockClassesFile),
("vesting_terms", vestingtermsfile.VestingTermsFile),
("valuations", valuationsfile.ValuationsFile),
("stock_legend_templates", stocklegendtemplatesfile.StockLegendTemplatesFile),
("stock_plans", stockplansfile.StockPlansFile),
("transactions", transactionsfile.TransactionsFile),
("stakeholders", stakeholdersfile.StakeholdersFile),
("valuations", valuationsfile.ValuationsFile),
("vesting_terms", vestingtermsfile.VestingTermsFile),
]


class Captable:
manifest: ocfmanifestfile.OCFManifestFile = None
stock_plans: list[StockPlan] = []
stock_legend_templates: list[StockLegendTemplate] = []
stock_classes: list[StockClass] = []
vesting_terms: list[VestingTerms] = []
valuations: list[Valuation] = []
transactions: list[Transaction] = []
stakeholders: list[Stakeholder] = []
manifest: ocfmanifestfile.OCFManifestFile
documents: list[Document]
financings: list[Financing]
stakeholders: list[Stakeholder]
stock_classes: list[StockClass]
stock_legend_templates: list[StockLegendTemplate]
stock_plans: list[StockPlan]
transactions: list[Transaction]
valuations: list[Valuation]
vesting_terms: list[VestingTerms]

def __init__(
self,
manifest: ocfmanifestfile.OCFManifestFile = None,
documents: list[Document] = None,
financings: list[Financing] = None,
stakeholders: list[Stakeholder] = None,
stock_classes: list[StockClass] = None,
stock_legend_templates: list[StockLegendTemplate] = None,
stock_plans: list[StockPlan] = None,
transactions: list[Transaction] = None,
valuations: list[Valuation] = None,
vesting_terms: list[VestingTerms] = None,
):
self.manifest = manifest
self.documents = documents or []
self.financings = financings or []
self.stakeholders = stakeholders or []
self.stock_classes = stock_classes or []
self.stock_legend_templates = stock_legend_templates or []
self.stock_plans = stock_plans or []
self.transactions = transactions or []
self.valuations = valuations or []
self.vesting_terms = vesting_terms or []

@classmethod
def load(cls, location):
Expand Down Expand Up @@ -82,7 +114,10 @@ def file_factory(p):
return open(pathlib.Path(basedir, p))

for filetype, filecls in FILEMAP:
for fileob in getattr(captable.manifest, filetype + "_files"):
fileobjs = getattr(captable.manifest, filetype + "_files")
if fileobjs is None:
continue
for fileob in fileobjs:
infile = file_factory(fileob.filepath)
items = filecls(**json.load(infile)).items
getattr(captable, filetype).extend(items)
Expand All @@ -92,7 +127,7 @@ def file_factory(p):
def _save_ocf_files(self, manifest_path, issuer, file_factory, pretty):
if issuer is None and self.manifest is None:
raise ValueError(
"You must specify an issuer, either by passing the value to the"
"You must specify an issuer, either by passing the value to the "
"save method, or by creating a Manifest."
)

Expand All @@ -104,6 +139,8 @@ def _save_ocf_files(self, manifest_path, issuer, file_factory, pretty):
if self.manifest:
# Check if there is a different filename in the manifest:
ocffilename = getattr(self.manifest, filetype + "_files", [])
if ocffilename is None:
continue
if len(ocffilename) >= 1:
ocffilename = ocffilename[0].filepath

Expand All @@ -112,7 +149,7 @@ def _save_ocf_files(self, manifest_path, issuer, file_factory, pretty):

with file_factory(ocffilename) as ocffile:
itemfile = fileob(items=getattr(self, filetype))
jsonstr = itemfile.json(exclude_unset=True)
jsonstr = itemfile.model_dump_json()
if pretty:
jsonstr = json.dumps(json.loads(jsonstr), indent=4)
jsonstr = jsonstr.encode("UTF-8")
Expand Down Expand Up @@ -143,7 +180,7 @@ def _save_ocf_files(self, manifest_path, issuer, file_factory, pretty):
self.manifest = ocfmanifestfile.OCFManifestFile(**manifest_data)

with file_factory(manifest_path) as ocffile:
jsonstr = self.manifest.json()
jsonstr = self.manifest.model_dump_json()
if pretty:
jsonstr = json.dumps(json.loads(jsonstr), indent=4)
ocffile.write(jsonstr.encode("UTF-8"))
Expand Down
Loading