258 lines
8.6 KiB
Python
258 lines
8.6 KiB
Python
from typing import Any, Dict, List, Optional
|
|
|
|
from bs4 import Tag
|
|
from pydantic import BaseModel
|
|
from rich.columns import Columns
|
|
from rich.console import Group
|
|
from rich.panel import Panel
|
|
from rich.table import Table
|
|
from rich.text import Text
|
|
|
|
from edgar.core import IntString
|
|
from edgar.richtools import repr_rich
|
|
from edgar.xmltools import child_text, child_value
|
|
|
|
__all__ = [
|
|
'Address',
|
|
'Issuer',
|
|
'Person',
|
|
'Name',
|
|
'Filer',
|
|
'get_addresses_as_columns'
|
|
]
|
|
|
|
|
|
class Address(BaseModel):
|
|
|
|
street1: Optional[str] = None
|
|
street2: Optional[str] = None
|
|
city: Optional[str] = None
|
|
state_or_country: Optional[str] = None
|
|
state_or_country_description: Optional[str] = None
|
|
zipcode: Optional[str] = None
|
|
|
|
@property
|
|
def empty(self):
|
|
return not self.street1 and not self.street2 and not self.city and not self.state_or_country and not self.zipcode
|
|
|
|
@classmethod
|
|
def from_dict(cls, address_dict: Dict[str, Any]):
|
|
return Address(
|
|
street1=address_dict.get('STREET1'),
|
|
street2=address_dict.get('STREET2'),
|
|
city=address_dict.get('CITY'),
|
|
state_or_country=address_dict.get('STATE'),
|
|
zipcode=address_dict.get('ZIP')
|
|
)
|
|
|
|
def __str__(self):
|
|
if not self.street1:
|
|
return ""
|
|
address_format = "{street1}\n"
|
|
if self.street2:
|
|
address_format += "{street2}\n"
|
|
address_format += "{city}, {state_or_country} {zipcode}"
|
|
|
|
return address_format.format(
|
|
street1=self.street1,
|
|
street2=self.street2,
|
|
city=self.city,
|
|
state_or_country=self.state_or_country_description or self.state_or_country,
|
|
zipcode=self.zipcode or ""
|
|
)
|
|
|
|
def __repr__(self):
|
|
return (f'Address(street1="{self.street1 or ""}", street2="{self.street2 or ""}", city="{self.city or ""}",'
|
|
f'zipcode="{self.zipcode or ""}", state_or_country="{self.state_or_country} or "")'
|
|
)
|
|
|
|
|
|
def get_addresses_as_columns(*,
|
|
mailing_address: Optional[Address],
|
|
business_address: Optional[Address]) -> Columns:
|
|
"""
|
|
Returns a rich Columns object with mailing and business addresses
|
|
"""
|
|
addresses = []
|
|
if mailing_address and not mailing_address.empty:
|
|
addresses.append(Panel(Text(str(mailing_address)), title='\U00002709 Mailing Address', width=40))
|
|
if business_address and not business_address.empty:
|
|
addresses.append(Panel((Text(str(business_address))), title='\U0001F3E2 Business Address', width=40))
|
|
return Columns(addresses, equal=True, expand=True)
|
|
|
|
|
|
class Issuer:
|
|
"""
|
|
<primaryIssuer>
|
|
<cik>0001961089</cik>
|
|
<entityName>1685 38th REIT, L.L.C.</entityName>
|
|
<issuerAddress>
|
|
<street1>2029 CENTURY PARK EAST</street1>
|
|
<street2>SUITE 1370</street2>
|
|
<city>LOS ANGELES</city>
|
|
<stateOrCountry>CA</stateOrCountry>
|
|
<stateOrCountryDescription>CALIFORNIA</stateOrCountryDescription>
|
|
<zipCode>90067</zipCode>
|
|
</issuerAddress>
|
|
<issuerPhoneNumber>424-313-1550</issuerPhoneNumber>
|
|
<jurisdictionOfInc>DELAWARE</jurisdictionOfInc>
|
|
<issuerPreviousNameList>
|
|
<value>None</value>
|
|
</issuerPreviousNameList>
|
|
<edgarPreviousNameList>
|
|
<value>None</value>
|
|
</edgarPreviousNameList>
|
|
<entityType>Limited Liability Company</entityType>
|
|
<yearOfInc>
|
|
<withinFiveYears>true</withinFiveYears>
|
|
<value>2022</value>
|
|
</yearOfInc>
|
|
</primaryIssuer>
|
|
|
|
"""
|
|
|
|
def __init__(self,
|
|
cik: IntString,
|
|
entity_name: str,
|
|
entity_type: str,
|
|
primary_address: Address,
|
|
phone_number: str,
|
|
jurisdiction: str,
|
|
issuer_previous_names: List[str],
|
|
edgar_previous_names: List[str],
|
|
year_of_incorporation: IntString,
|
|
incorporated_within_5_years: bool):
|
|
self.cik = cik
|
|
self.entity_name: str = entity_name
|
|
self.entity_type = entity_type
|
|
self.primary_address: Address = primary_address
|
|
self.phone_number: str = phone_number
|
|
self.issuer_previous_names = issuer_previous_names
|
|
self.edgar_previous_names = edgar_previous_names
|
|
self.jurisdiction: str = jurisdiction
|
|
self.year_of_incorporation = year_of_incorporation
|
|
self.incorporated_within_5_years: bool = incorporated_within_5_years
|
|
|
|
@classmethod
|
|
def from_xml(cls, issuer_el: Tag):
|
|
# edgar previous names
|
|
edgar_previous_names_el = issuer_el.find("edgarPreviousNameList")
|
|
edgar_previous_names = [el.text
|
|
for el in edgar_previous_names_el.find_all("value")
|
|
if el.text != 'None'] if edgar_previous_names_el else []
|
|
|
|
# issuer previous names
|
|
issuer_previous_names_el = issuer_el.find("issuerPreviousNameList")
|
|
issuer_previous_names = [el.text
|
|
for el in issuer_previous_names_el.find_all("value")
|
|
if el.text != 'None'] if issuer_previous_names_el else []
|
|
|
|
year_of_inc_el = issuer_el.find("yearOfInc")
|
|
|
|
# Address
|
|
issuer_address_el = issuer_el.find("issuerAddress")
|
|
address: Address = Address(
|
|
street1=child_text(issuer_address_el, "street1"),
|
|
street2=child_text(issuer_address_el, "street2"),
|
|
city=child_text(issuer_address_el, "city"),
|
|
state_or_country=child_text(issuer_address_el, "stateOrCountry"),
|
|
state_or_country_description=child_text(issuer_address_el, "stateOrCountryDescription"),
|
|
zipcode=child_text(issuer_address_el, "zipCode")
|
|
)
|
|
|
|
return cls(
|
|
cik=child_text(issuer_el, "cik"),
|
|
entity_name=child_text(issuer_el, "entityName"),
|
|
phone_number=child_text(issuer_el, "issuerPhoneNumber"),
|
|
jurisdiction=child_text(issuer_el, "jurisdictionOfInc"),
|
|
entity_type=child_text(issuer_el, "entityType"),
|
|
edgar_previous_names=edgar_previous_names,
|
|
primary_address=address,
|
|
issuer_previous_names=issuer_previous_names,
|
|
year_of_incorporation=child_value(issuer_el, "yearOfInc"),
|
|
incorporated_within_5_years=year_of_inc_el and child_text(year_of_inc_el, "withinFiveYears") == "true"
|
|
)
|
|
|
|
def __rich__(self):
|
|
table = Table("issuer", "entity type", "incorporated")
|
|
table.add_row(self.entity_name, self.entity_type, self.year_of_incorporation)
|
|
return Group(table)
|
|
|
|
def __repr__(self):
|
|
return repr_rich(self.__rich__())
|
|
|
|
|
|
class Person:
|
|
|
|
def __init__(self,
|
|
first_name: str,
|
|
last_name: str,
|
|
address: Optional[Address] = None):
|
|
self.first_name = first_name
|
|
self.last_name = last_name
|
|
self.address: Address = address
|
|
|
|
def __str__(self):
|
|
return f"{self.first_name} {self.first_name}"
|
|
|
|
def __repr__(self):
|
|
return f"{self.first_name} {self.last_name}"
|
|
|
|
|
|
class Name:
|
|
|
|
def __init__(self,
|
|
first_name: str,
|
|
middle_name: str,
|
|
last_name: str,
|
|
suffix:Optional[str]=None):
|
|
self.first_name = first_name
|
|
self.middle_name = middle_name
|
|
self.last_name = last_name
|
|
self.suffix = suffix
|
|
|
|
@property
|
|
def full_name(self):
|
|
return f"{self.first_name}{' ' + self.middle_name or ''} {self.last_name} {self.suffix or ''}".rstrip()
|
|
|
|
def __str__(self):
|
|
return self.full_name
|
|
|
|
def __repr__(self):
|
|
return self.full_name
|
|
|
|
|
|
class Filer:
|
|
|
|
def __init__(self,
|
|
cik: str,
|
|
entity_name: str,
|
|
file_number: str
|
|
):
|
|
self.cik: str = cik
|
|
self.entity_name: str = entity_name
|
|
self.file_number: str = file_number
|
|
|
|
def __str__(self):
|
|
return f"{self.entity_name} ({self.cik})"
|
|
|
|
def __repr__(self):
|
|
return f"{self.entity_name} ({self.cik})"
|
|
|
|
|
|
class Contact:
|
|
|
|
def __init__(self,
|
|
name: str,
|
|
phone_number: str,
|
|
email: str):
|
|
self.name: str = name
|
|
self.phone_number: str = phone_number
|
|
self.email: str = email
|
|
|
|
def __str__(self):
|
|
return f"{self.name} ({self.phone_number}) {self.email}"
|
|
|
|
def __repr__(self):
|
|
return f"{self.name} ({self.phone_number}) {self.email}"
|