This commit is contained in:
James Maslek
2023-08-28 10:50:30 -04:00
35 changed files with 2209 additions and 83 deletions

View File

@@ -15,7 +15,17 @@ echo " |___/ "
BOOTUP_MESSAGE="
The boot up process might take a while. Please be patient...
Have you checked out Community Routines yet? You can access them through the OpenBB Hub!
Get started in less than 3 minutes: https://www.youtube.com/watch?v=UFh_Ku0fdtY
"
echo "$BOOTUP_MESSAGE"
# Add some messages we can print out
messages=("" "" "" "" "Did you know that you can search for stocks from a given country by running stocks/search --exchangecountry COUNTRY")
# Pick a random one
index=$((RANDOM % ${#messages[@]}))
echo ${messages[$index]}
"$SCRIPTDIR"/.OpenBB/OpenBBTerminal

View File

@@ -22,7 +22,7 @@ class AlternativeDataController(BaseController):
"""Alternative Controller class"""
CHOICES_COMMANDS: List[str] = ["hn"]
CHOICES_MENUS = ["covid", "oss", "realestate"]
CHOICES_MENUS = ["covid", "oss", "realestate", "companieshouse"]
PATH = "/alternative/"
CHOICES_GENERATION = True
@@ -41,6 +41,7 @@ class AlternativeDataController(BaseController):
mt.add_menu("covid")
mt.add_menu("oss")
mt.add_menu("realestate")
mt.add_menu("companieshouse")
mt.add_raw("\n")
mt.add_cmd("hn")
console.print(text=mt.menu_text, menu="Alternative")
@@ -94,3 +95,12 @@ class AlternativeDataController(BaseController):
)
self.queue = self.load_class(RealEstateController, self.queue)
@log_start_end(log=logger)
def call_companieshouse(self, _):
"""Process companieshouse command."""
from openbb_terminal.alternative.companieshouse.companieshouse_controller import (
CompaniesHouseController,
)
self.queue = self.load_class(CompaniesHouseController, self.queue)

View File

@@ -0,0 +1,337 @@
"""Companies House Controller."""
__docformat__ = "numpy"
import argparse
import logging
from typing import List, Optional
from openbb_terminal.alternative.companieshouse import companieshouse_view
from openbb_terminal.core.session.current_user import get_current_user
from openbb_terminal.custom_prompt_toolkit import NestedCompleter
from openbb_terminal.decorators import check_api_key, log_start_end
from openbb_terminal.helper_funcs import (
EXPORT_ONLY_RAW_DATA_ALLOWED,
check_positive,
)
from openbb_terminal.menu import session
from openbb_terminal.parent_classes import BaseController
from openbb_terminal.rich_config import MenuText, console
logger = logging.getLogger(__name__)
class CompaniesHouseController(BaseController):
"""Companies House Controller class."""
CHOICES_COMMANDS = [
"search",
"load",
"officers",
"signifcontrol",
"filings",
"filingdocument",
"charges",
]
PATH = "/alternative/companieshouse/"
CHOICES_GENERATION = True
def __init__(self, queue: Optional[List[str]] = None):
"""Construct Data."""
super().__init__(queue)
self.companyNo = ""
self.companyName = ""
self.filingCategory = ""
self.filing_total_count = 0
self.filing_end_index = 0
self.filing_start_index = 0
if session and get_current_user().preferences.USE_PROMPT_TOOLKIT:
choices: dict = self.choices_default
self.completer = NestedCompleter.from_nested_dict(choices)
def print_help(self):
"""Print help"""
company_string = (
f"{self.companyNo} ({self.companyName})" if self.companyNo else ""
)
mt = MenuText("alternative/companieshouse/")
mt.add_param("_company", company_string)
mt.add_raw("\n")
mt.add_cmd("search")
mt.add_cmd("load")
mt.add_cmd("officers")
mt.add_cmd("signifcontrol")
mt.add_cmd("filings")
mt.add_cmd("filingdocument")
mt.add_cmd("charges")
console.print(text=mt.menu_text, menu="UK Companies House Data")
@log_start_end(log=logger)
@check_api_key(["API_COMPANIESHOUSE_KEY"])
def call_search(self, other_args: List[str]):
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="search",
description="Select the company name to search for. [Source: UK Companies House]",
)
parser.add_argument(
"-n",
"--name",
help="name",
type=str.upper,
required="-h" not in other_args,
dest="name",
metavar="name",
nargs="+",
)
parser.add_argument(
"-l",
"--limit",
help="Number of entries to return",
type=check_positive,
required=False,
dest="limit",
metavar="limit",
default=20,
)
if (
other_args
and "-n" not in other_args[0]
and "--name" not in other_args[0]
and "-h" not in other_args
):
other_args.insert(0, "-n")
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if ns_parser:
if ns_parser.name:
query = " ".join(ns_parser.name)
companieshouse_view.display_search(
query, ns_parser.limit, export=ns_parser.export
)
else:
console.print("[red]No entries found for search string[/red]\n")
@log_start_end(log=logger)
@check_api_key(["API_COMPANIESHOUSE_KEY"])
def call_load(self, other_args: List[str]):
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="load",
description="Select the company number to get detailed info on. [Source: UK Companies House]",
)
parser.add_argument(
"-c",
"--companyNo",
help="companyNo",
type=str.upper,
required="-h" not in other_args,
dest="companyNo",
metavar="companyNo",
)
if (
other_args
and "-c" not in other_args[0]
and "--companyNo" not in other_args[0]
and "-h" not in other_args
):
other_args.insert(0, "-c")
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if ns_parser and ns_parser.companyNo:
self.companyNo = ns_parser.companyNo
company = companieshouse_view.display_company_info(
ns_parser.companyNo, export=ns_parser.export
)
if company.dataAvailable():
self.companyName = company.name
self.filing_total_count = 0
self.filing_end_index = 0
console.print(company.name)
console.print(company.address)
console.print(company.lastAccounts)
else:
console.print(
f"[red]No data found for company number {ns_parser.companyNo}[/red]\n"
)
@log_start_end(log=logger)
@check_api_key(["API_COMPANIESHOUSE_KEY"])
def call_officers(self, other_args: List[str]):
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="officers",
description="Select the company number to retrieve officers for. [Source: UK Companies House]",
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if self.companyNo:
if ns_parser:
companieshouse_view.display_officers(
self.companyNo, export=ns_parser.export
)
else:
console.print("Must load a company prior to using this command")
@log_start_end(log=logger)
@check_api_key(["API_COMPANIESHOUSE_KEY"])
def call_signifcontrol(self, other_args: List[str]):
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="signifcontrol",
description="Select the company number to retrieve persons with significant control of company. \
[Source: UK Companies House]",
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if self.companyNo:
if ns_parser:
companieshouse_view.display_persons_with_significant_control(
self.companyNo, export=ns_parser.export
)
else:
console.print("Must load a company prior to using this command")
@log_start_end(log=logger)
@check_api_key(["API_COMPANIESHOUSE_KEY"])
def call_filings(self, other_args: List[str]):
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="filings",
description="Select the company number to retrieve filling history for. [Source: UK Companies House]",
)
parser.add_argument(
"-k",
"--category",
help="category",
type=str.lower,
required=False,
dest="category",
metavar="category",
choices=[
"accounts",
"address",
"capital",
"incorporation",
"officers",
"resolution",
],
)
parser.add_argument(
"-l",
"--limit",
help="Number of entries to return",
type=check_positive,
required=False,
dest="limit",
metavar="limit",
default=100,
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if self.companyNo:
if ns_parser:
category = ns_parser.category if ns_parser.category else ""
self.filingCategory = category
filing_data = companieshouse_view.display_filings(
self.companyNo, category, ns_parser.limit, export=ns_parser.export
)
self.filing_total_count = filing_data.total_count
self.filing_end_index = filing_data.end_index
self.filing_start_index = filing_data.start_index
else:
console.print("Must load a company prior to using this command")
@log_start_end(log=logger)
@check_api_key(["API_COMPANIESHOUSE_KEY"])
def call_filingdocument(self, other_args: List[str]):
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="filingdocument",
description="Select the company number and transaction ID to retrieve filling history for. \
[Source: UK Companies House]",
)
parser.add_argument(
"-t",
"--transactionID",
help="transactionID",
action="store",
required=("-h" not in other_args),
dest="transactionID",
metavar="transactionID",
)
if (
other_args
and "-t" not in other_args[0]
and "--transactionID" not in other_args[0]
and "-h" not in other_args
):
other_args.insert(0, "-t")
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if self.companyNo:
if ns_parser:
companieshouse_view.download_filing_document(
self.companyNo,
self.companyName,
ns_parser.transactionID,
export=ns_parser.export,
)
else:
console.print("Must load a company prior to using this command")
@log_start_end(log=logger)
@check_api_key(["API_COMPANIESHOUSE_KEY"])
def call_charges(self, other_args: List[str]):
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="charges",
description="Select the company number to retrieve officers for. [Source: UK Companies House]",
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
if self.companyNo:
if ns_parser:
companieshouse_view.display_charges(
self.companyNo, export=ns_parser.export
)
else:
console.print("Must load a company prior to using this command")

View File

@@ -0,0 +1,481 @@
""" UK Companies House Model """
__docformat__ = "numpy"
import logging
import pandas as pd
import requests
from openbb_terminal.alternative.companieshouse.company import Company
from openbb_terminal.alternative.companieshouse.company_doc import CompanyDocument
from openbb_terminal.alternative.companieshouse.filing_data import Filing_data
from openbb_terminal.core.session.constants import (
TIMEOUT,
)
from openbb_terminal.core.session.current_user import get_current_user
from openbb_terminal.decorators import check_api_key, log_start_end
logger = logging.getLogger(__name__)
@log_start_end(log=logger)
@check_api_key(["API_COMPANIESHOUSE_KEY"])
def get_search_results(searchStr: str, limit: int = 20) -> pd.DataFrame:
"""All companies with searchStr in their name.
Parameters
----------
searchStr : str
The search string
limit : int
number of rows to return
Returns
-------
pd.DataFrame
All comapanies with the search string in their name.
Example
-------
>>> from openbb_terminal.sdk import openbb
>>> companies = openbb.alt.companieshouse.get_search_results("AstraZeneca")
"""
df = pd.DataFrame()
if not searchStr:
return df
auth = requests.auth.HTTPBasicAuth(
get_current_user().credentials.API_COMPANIESHOUSE_KEY, ""
)
r = requests.get(
"https://api.company-information.service.gov.uk/search/companies?q="
+ searchStr
+ f"&items_per_page={limit}",
auth=auth,
timeout=TIMEOUT,
)
returned_data = r.json()
company_data = []
for index, item in enumerate(returned_data["items"]):
company_data.append(
{
"Name": item["title"],
"Company Number": item["company_number"],
"Status": item["company_status"],
}
)
df = pd.DataFrame(company_data)
return df
@log_start_end(log=logger)
@check_api_key(["API_COMPANIESHOUSE_KEY"])
def get_company_info(company_number: str) -> Company:
"""Gets company info by company number
Parameters
----------
company_number : str
The company number. Use get_search_results() to lookup company numbers.
Returns
-------
self.address: str
Company address.
self.name: str
Company name.
self.dataAvailable(): bool
True if data is available.
self.lastAccounts: str
Period start and end.
Example
-------
>>> companies = openbb.alt.companieshouse.get_search_results("AstraZeneca")
>>> company_info = openbb.alt.companieshouse.get_company_info("02723534")
>>> name = company_info.name
"""
auth = requests.auth.HTTPBasicAuth(
get_current_user().credentials.API_COMPANIESHOUSE_KEY, ""
)
r = requests.get(
f"https://api.company-information.service.gov.uk/company/{company_number}",
auth=auth,
timeout=TIMEOUT,
)
last_accounts = {}
returned_data = r.json()
if returned_data.get("company_name"):
company_name = returned_data["company_name"]
if returned_data.get("accounts"):
last_accounts = returned_data["accounts"]["last_accounts"]
address = returned_data["registered_office_address"]
address_lines = []
if address.get("address_line_1"):
address_lines.append(address.get("address_line_1"))
if address.get("address_line_2"):
address_lines.append(address.get("address_line_2"))
if address.get("locality"):
address_lines.append(address.get("locality"))
if address.get("region"):
address_lines.append(address.get("region"))
if address.get("postal_code"):
address_lines.append(address.get("postal_code"))
pretty_address = (
",".join(address_lines)
if len(address_lines) > 0
else "No address data found"
)
if last_accounts:
pretty_accounts = "Period Start On : " + (
last_accounts.get("period_start_on") or ""
) + " - " + "Type : " + (
last_accounts.get("type") or ""
) + " - " + "Made Up To : " + (
last_accounts.get("made_up_to") or ""
) + " - " "Period End On : " + (
last_accounts.get("period_end_on") or ""
)
else:
pretty_accounts = "No accounting period data found"
data = Company(company_name, pretty_address, pretty_accounts)
return data
else:
return Company()
@log_start_end(log=logger)
@check_api_key(["API_COMPANIESHOUSE_KEY"])
def get_officers(company_number: str) -> pd.DataFrame:
"""Gets information on company officers
Parameters
----------
company_number : str
The company number. Use get_search_results() to lookup company numbers.
Returns
-------
pd.Dataframe
All officers for given company number
Example
-------
>>> companies = openbb.alt.companieshouse.get_search_results("AstraZeneca")
>>> officer_info = openbb.alt.companieshouse.get_officers("02723534")
"""
auth = requests.auth.HTTPBasicAuth(
get_current_user().credentials.API_COMPANIESHOUSE_KEY, ""
)
r = requests.get(
f"https://api.company-information.service.gov.uk/company/{company_number}/officers?items_per_page=100",
auth=auth,
timeout=TIMEOUT,
)
returned_data = r.json()
officers = []
if returned_data.get("items"):
for index, item in enumerate(returned_data["items"]):
officers.append(
{
"Officer Role": (item.get("officer_role") or " - "),
"Appointed On": (item.get("appointed_on") or " - "),
"Resigned On": (item.get("resigned_on") or " - "),
"Name": (item.get("name") or " - "),
}
)
df = pd.DataFrame(officers)
return df
@log_start_end(log=logger)
@check_api_key(["API_COMPANIESHOUSE_KEY"])
def get_persons_with_significant_control(company_number: str) -> pd.DataFrame:
"""Gets information on persons with significant control over the company
Parameters
----------
company_number : str
The company number. Use get_search_results() to lookup company numbers.
Returns
-------
pd.Dataframe
All persons with significant control over given company number
Example
-------
>>> companies = openbb.alt.companieshouse.get_search_results("AstraZeneca")
>>> signif_control_info = openbb.alt.companieshouse.get_persons_with_significant_control("02723534")
"""
auth = requests.auth.HTTPBasicAuth(
get_current_user().credentials.API_COMPANIESHOUSE_KEY, ""
)
r = requests.get(
f"https://api.company-information.service.gov.uk/company/{company_number}/persons-with-significant-control?items_per_page=100",
auth=auth,
timeout=TIMEOUT,
)
returned_data = r.json()
controllers = []
if returned_data.get("items"):
for index, item in enumerate(returned_data["items"]):
controllers.append(
{
"Kind": (item.get("kind") or " - "),
"Name": (item.get("name") or " - "),
"Natures of Control": (item.get("natures_of_control") or " - "),
"Notified On": (item.get("notified_on") or " - "),
}
)
df = pd.DataFrame(controllers)
return pd.DataFrame(df)
@log_start_end(log=logger)
@check_api_key(["API_COMPANIESHOUSE_KEY"])
def get_filings(company_number: str, category: str = "", start_index=0) -> Filing_data:
"""Gets information on filings for given company, e.g. accounts, etc
Parameters
----------
company_number : str
The company number. Use get_search_results() to lookup company numbers.
Returns
-------
pd.Dataframe
All information on all filings for company
Example
-------
>>> companies = openbb.alt.companieshouse.get_search_results("AstraZeneca")
>>> signif_control_info = openbb.alt.companieshouse.get_filings("02723534")
"""
auth = requests.auth.HTTPBasicAuth(
get_current_user().credentials.API_COMPANIESHOUSE_KEY, ""
)
url = (
f"https://api.company-information.service.gov.uk/company/{company_number}/filing-history"
+ f"?start_index={start_index}&items_per_page=100"
)
if category:
url = (
f"https://api.company-information.service.gov.uk/company/{company_number}/filing-history"
+ f"?category={category}&start_index={start_index}&items_per_page=100"
)
r = requests.get(
url,
auth=auth,
timeout=TIMEOUT,
)
returned_data = r.json()
filings = []
for index, item in enumerate(returned_data["items"]):
filings.append(
{
"Category": (item.get("category") or " - "),
"Date": (item.get("date") or " - "),
"Description": (item.get("description") or " - "),
"Type": (item.get("type") or " - "),
"Pages": (item.get("pages") or " - "),
"Transaction ID": (item.get("transaction_id") or " - "),
"Download": "https://find-and-update.company-information.service.gov.uk/company/"
f"{company_number}"
"/filing-history/"
f"{item.get('transaction_id')}"
"/document?format=pdf&download=0",
}
)
start_index = int(returned_data.get("start_index"))
total_count = int(returned_data.get("total_count"))
end_index = start_index + 100
if end_index > total_count:
end_index = total_count
data = Filing_data(pd.DataFrame(filings), start_index, end_index, total_count)
return data
@log_start_end(log=logger)
@check_api_key(["API_COMPANIESHOUSE_KEY"])
def get_filing_document(company_number: str, transactionID: str) -> CompanyDocument:
"""Download given filing document pdf
Parameters
----------
company_number : str
The company number. Use get_search_results() to lookup company numbers.
transactionID : str
The filing transaction id. Use get_filings() to get id for each document
Returns
-------
self.category: str
document category, e.g.confirmation-statement-with-updates, accounts-with-accounts-type-dormant, etc.
self.date: str
date document filed.
self.description: str
description of document files.
self.paper_filed: str
True if documents filed in paper format.
self.pages: str
Number of pages in document.
self.transaction_id: str
Document transaction id.
self.content: bytes
contents of the document.
Example
-------
>>> companies = openbb.alt.companieshouse.get_search_results("AstraZeneca")
>>> company_doc_info = openbb.alt.companieshouse.get_filing_document("02723534","MzM1NzQ0NzI5NWFkaXF6a2N4")
"""
auth = requests.auth.HTTPBasicAuth(
get_current_user().credentials.API_COMPANIESHOUSE_KEY, ""
)
r = requests.get(
f"https://api.company-information.service.gov.uk/company/{company_number}/filing-history/{transactionID}",
auth=auth,
timeout=TIMEOUT,
)
returned_data = r.json()
category = date = description = paper_filed = pages = transaction_id = ""
content = b""
if returned_data.get("links") and returned_data.get("links").get(
"document_metadata"
):
url = returned_data.get("links").get("document_metadata") + "/content"
response = requests.get(
url, auth=auth, headers={"Accept": "application/pdf"}, timeout=TIMEOUT
)
if returned_data.get("category"):
category = returned_data["category"]
if returned_data.get("date"):
date = returned_data["date"]
if returned_data.get("description"):
description = returned_data["description"]
if returned_data.get("paper_filed"):
paper_filed = returned_data["paper_filed"]
if returned_data.get("pages"):
pages = returned_data["pages"]
if returned_data.get("transaction_id"):
transaction_id = returned_data["transaction_id"]
content = bytes(response.content)
return CompanyDocument(
category, date, description, paper_filed, pages, transaction_id, content
)
else:
return CompanyDocument(
category, date, description, paper_filed, pages, transaction_id, content
)
@log_start_end(log=logger)
@check_api_key(["API_COMPANIESHOUSE_KEY"])
def get_charges(company_number: str) -> pd.DataFrame:
auth = requests.auth.HTTPBasicAuth(
get_current_user().credentials.API_COMPANIESHOUSE_KEY, ""
)
r = requests.get(
f"https://api.company-information.service.gov.uk/company/{company_number}/charges",
auth=auth,
timeout=TIMEOUT,
)
returned_data = r.json()
charges = pd.DataFrame()
for index, item in enumerate(returned_data["items"]):
url = item.get("links").get("self")
id = url[url.rfind("/") + 1 :]
charges = pd.concat(
[charges, get_charge(company_number, id)], ignore_index=True
)
return charges
@log_start_end(log=logger)
@check_api_key(["API_COMPANIESHOUSE_KEY"])
def get_charge(company_number: str, charge_id: str) -> pd.DataFrame:
auth = requests.auth.HTTPBasicAuth(
get_current_user().credentials.API_COMPANIESHOUSE_KEY, ""
)
r = requests.get(
f"https://api.company-information.service.gov.uk/company/{company_number}/charges/{charge_id}",
auth=auth,
timeout=TIMEOUT,
)
returned_data = r.json()
charge = {}
if returned_data.get("acquired_on"):
charge.update({"acquired_on": returned_data.get("acquired_on")})
if returned_data.get("assests_ceased_released"):
charge.update(
{"assests_ceased_released": returned_data.get("assests_ceased_released")}
)
if returned_data.get("charge_number"):
charge.update({"charge_number": returned_data.get("charge_number")})
if returned_data.get("covering_instrument_date"):
charge.update(
{"covering_instrument_date": returned_data.get("covering_instrument_date")}
)
if returned_data.get("created_on"):
charge.update({"created_on": returned_data.get("created_on")})
if returned_data.get("delivered_on"):
charge.update({"delivered_on": returned_data.get("delivered_on")})
if returned_data.get("id"):
charge.update({"id": returned_data.get("id")})
if returned_data.get("status"):
charge.update({"status": returned_data.get("status")})
if returned_data.get("particulars"):
part = returned_data.get("particulars")
if part.get("description"):
charge.update({"description": part.get("description")})
if part.get("floating_charge_covers_all"):
charge.update(
{"floating_charge_covers_all": part.get("floating_charge_covers_all")}
)
if part.get("type"):
charge.update({"type": part.get("type")})
if returned_data.get("persons_entitled"):
for entitled in returned_data.get("persons_entitled"):
charge.update({"persons_entitled_name": entitled.get("name")})
return pd.DataFrame([charge])

View File

@@ -0,0 +1,253 @@
""" UK Companies House View """
__docformat__ = "numpy"
import logging
import os
import pandas as pd
from openbb_terminal.alternative.companieshouse import companieshouse_model
from openbb_terminal.alternative.companieshouse.company import Company
from openbb_terminal.alternative.companieshouse.filing_data import Filing_data
from openbb_terminal.core.session.current_user import get_current_user
from openbb_terminal.decorators import log_start_end
from openbb_terminal.helper_funcs import export_data, print_rich_table
from openbb_terminal.rich_config import console
logger = logging.getLogger(__name__)
@log_start_end(log=logger)
def display_search(search_str: str, limit: int, export: str = "") -> None:
"""Display company search results.
Parameters
----------
search_str : str
Company name to search for
limit : int
number of rows to return
"""
results = companieshouse_model.get_search_results(search_str, limit)
if results.empty or len(results) == 0:
console.print(
"[red]" + "No data loaded.\n" + "Try different search string." + "[/red]\n"
)
return
console.print(f"Retrieved {len(results)} records")
print_rich_table(
results,
show_index=False,
title=f"[bold]{search_str}[/bold]",
export=bool(export),
columns_keep_types=["Company Number"],
)
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"results",
results,
)
@log_start_end(log=logger)
def display_company_info(company_number: str, export: str = "") -> Company:
"""Display company search results.
Parameters
----------
company_number : str
company_number to retrieve info for
"""
return companieshouse_model.get_company_info(company_number)
@log_start_end(log=logger)
def display_officers(company_number: str, export: str = "") -> None:
"""Display company officers results.
Parameters
----------
company_number : str
company_number to retrieve officers for
"""
results = companieshouse_model.get_officers(company_number)
if len(results) > 0:
console.print(f"Retrieved {len(results)} records")
print_rich_table(
results,
show_index=False,
title=f"[bold]{company_number}[/bold]",
export=bool(export),
)
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"results",
results,
)
else:
console.print("[red]No Data Found[/red]")
@log_start_end(log=logger)
def display_persons_with_significant_control(
company_number: str, export: str = ""
) -> None:
"""Display company officers results.
Parameters
----------
company_number : str
company_number to retrieve officers for
"""
results = companieshouse_model.get_persons_with_significant_control(company_number)
if len(results) > 0:
console.print(f"Retrieved {len(results)} records")
print_rich_table(
results,
show_index=False,
title=f"[bold]{company_number}[/bold]",
export=bool(export),
)
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"results",
results,
)
else:
console.print("[red]No Data Found[/red]")
@log_start_end(log=logger)
def display_filings(
company_number: str,
category: str = "",
limit: int = 100,
export: str = "",
) -> Filing_data:
"""Display company's filing history.
Parameters
----------
company_number : str
company_number to retrieve filing history for
"""
start_index = 0
results = companieshouse_model.get_filings(company_number, category, start_index)
start = int(results.start_index)
data = results
total = int(results.total_count) if int(results.total_count) < limit else limit
while start < total - 100:
results = companieshouse_model.get_filings(
company_number, start_index=start + 100
)
data.filings = pd.concat([data.filings, results.filings], ignore_index=True)
start = start + 100
data.filings = data.filings.head(limit)
console.print(f"Retrieved {len(data.filings)} filings")
print_rich_table(
data.filings,
show_index=False,
title=f"[bold]{company_number}[/bold]",
export=bool(export),
)
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"results",
data.filings,
)
return results
def download_filing_document(
company_number: str, company_name: str, transactionID: str, export: str = ""
) -> None:
"""Download company's filing document.
Parameters
----------
company_number : str
company_number to retrieve filing history for
company_name : str
company_name to retrieve filing document for, this is used to name the downloaded file for easy access
transactionID : str
transaction id for filing
>>> companies = openbb.alt.companieshouse.get_search_results("AstraZeneca")
>>> openbb.alt.companieshouse.get_filing_document("02723534","AstraZeneca","MzM1NzQ0NzI5NWFkaXF6a2N4")
"""
results = companieshouse_model.get_filing_document(company_number, transactionID)
if results.dataAvailable():
filename = (
company_name.replace(" ", "_")
+ "_"
+ results.category
+ "_"
+ transactionID
+ ".pdf"
)
with open(
f"{get_current_user().preferences.USER_COMPANIES_HOUSE_DIRECTORY}/{filename}",
"wb",
) as f:
f.write(results.content)
console.print(
f"File [green] {filename}[/green] downloaded to \
{get_current_user().preferences.USER_COMPANIES_HOUSE_DIRECTORY}"
)
else:
console.print("[red]" + "Document not found" + "[/red]\n")
@log_start_end(log=logger)
def display_charges(company_number: str, export: str = "") -> None:
results = companieshouse_model.get_charges(company_number)
if len(results) > 0:
print_rich_table(
results,
show_index=False,
title=f"[bold]{company_number}[/bold]",
export=bool(export),
)
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"results",
results,
)
else:
console.print("[red]" + "No Charges found" + "[/red]\n")
return results

View File

@@ -0,0 +1,14 @@
class Company:
"""Holder class for company information"""
name = ""
address = ""
lastAccounts = ""
def __init__(self, name="", address: str = "", lastAccounts: str = ""):
self.name = name
self.address = address
self.lastAccounts = lastAccounts
def dataAvailable(self) -> bool:
return len(self.name) > 0

View File

@@ -0,0 +1,31 @@
class CompanyDocument:
"""Holder class for company document information"""
category = ""
date = ""
description = ""
paper_filed = ""
pages = ""
transaction_id = ""
content = b""
def __init__(
self,
category="",
date: str = "",
description: str = "",
paper_filed: str = "",
pages: str = "",
transaction_id: str = "",
content=b"",
):
self.category = category
self.date = date
self.description = description
self.paper_filed = paper_filed
self.pages = pages
self.transaction_id = transaction_id
self.content = content
def dataAvailable(self) -> bool:
return len(self.content) > 0

View File

@@ -0,0 +1,19 @@
import pandas as pd
class Filing_data:
"""Holder class for company filings information"""
filings = pd.DataFrame()
total_count = 0
start_index = 0
end_index = 0
def __init__(self, filings, start_index, end_index, total_count):
self.filings = filings
self.start_index = start_index
self.end_index = end_index
self.total_count = total_count
def dataAvailable(self) -> bool:
return len(self.filings) > 0

View File

@@ -42,6 +42,7 @@ dirs_list = [
current_user.preferences.USER_DATA_DIRECTORY / "styles",
current_user.preferences.USER_DATA_DIRECTORY / "reports",
current_user.preferences.USER_DATA_DIRECTORY / "reports" / "custom reports",
current_user.preferences.USER_DATA_DIRECTORY / "companies_house",
current_user.preferences.USER_CUSTOM_IMPORTS_DIRECTORY,
current_user.preferences.USER_CUSTOM_IMPORTS_DIRECTORY / "econometrics",
current_user.preferences.USER_CUSTOM_IMPORTS_DIRECTORY / "stocks",

View File

@@ -85,6 +85,7 @@ class PreferencesModel(BaseModel):
USER_FORECAST_MODELS_DIRECTORY = USER_DATA_DIRECTORY / "exports" / "forecast_models"
USER_FORECAST_WHISPER_DIRECTORY = USER_DATA_DIRECTORY / "exports" / "whisper"
USER_STYLES_DIRECTORY = USER_DATA_DIRECTORY / "styles"
USER_COMPANIES_HOUSE_DIRECTORY = USER_DATA_DIRECTORY / "companies_house"
def __repr__(self) -> str: # pylint: disable=useless-super-delegation
return super().__repr__()

View File

@@ -11,6 +11,7 @@ class AltController(model.AltRoot):
`covid`: Covid Module
`oss`: Oss Module
`realestate`: Realestate Module
`companieshouse`: CompaniesHouse Module
Attributes:
`hn`: Get top stories from HackerNews.\n
@@ -66,3 +67,19 @@ class AltController(model.AltRoot):
"""
return model.AltRealestate()
@property
def companieshouse(self):
"""Alternative CompaniesHouse Submodule
Attributes:
`get_search_results`: Search for companies by name\n
`get_company_info`: Get details of company by registration number\n
`get_officers`: Get company officers\n
`get_persons_with_significant_control`: Get people/organisations with significant control over company\n
`get_filings`: Get company filing details\n
`get_filing_document`: Get company filed document\n
`download_filing_document`: Download company filed document\n
"""
return model.AltCompaniesHouse()

View File

@@ -103,3 +103,40 @@ class AltRealestate(Category):
self.get_towns_sold_prices = (
lib.alt_realestate_landRegistry_model.get_towns_sold_prices
)
class AltCompaniesHouse(Category):
"""CompaniesHouse Module.
Attributes:
`get_search_results`: Search for company by name\n
`get_company_info`: Get details of company by registration number\n
`get_officers`: Get company officers\n
`get_persons_with_significant_control`: Get people/organisations with significant control over company\n
`get_charges`: Get company charges\n
`get_filings`: Get company filing details\n
`get_filing_document`: Get company filed document\n
"""
_location_path = "alt.companieshouse"
def __init__(self):
super().__init__()
self.get_search_results = (
lib.alt_companieshouse_companieshouse_model.get_search_results
)
self.get_company_info = (
lib.alt_companieshouse_companieshouse_model.get_company_info
)
self.get_officers = lib.alt_companieshouse_companieshouse_model.get_officers
self.get_persons_with_significant_control = (
lib.alt_companieshouse_companieshouse_model.get_persons_with_significant_control
)
self.get_charges = lib.alt_companieshouse_companieshouse_model.get_charges
self.get_filings = lib.alt_companieshouse_companieshouse_model.get_filings
self.get_filing_document = (
lib.alt_companieshouse_companieshouse_model.get_filing_document
)
self.download_filing_document = (
lib.alt_companieshouse_companieshouse_view.download_filing_document
)

View File

@@ -2,6 +2,8 @@
# noqa: F401
# Alternative
import openbb_terminal.alternative.companieshouse.companieshouse_model as alt_companieshouse_companieshouse_model
import openbb_terminal.alternative.companieshouse.companieshouse_view as alt_companieshouse_companieshouse_view
import openbb_terminal.alternative.hackernews_model as alt_hackernews_model
import openbb_terminal.alternative.hackernews_view as alt_hackernews_view
import openbb_terminal.alternative.oss.github_model as alt_oss_github_model

View File

@@ -16,6 +16,14 @@ alt.oss.top,alt_oss_github_model.get_top_repos,alt_oss_github_view.display_top_r
alt.realestate.get_estate_sales,alt_realestate_landRegistry_model.get_estate_sales,
alt.realestate.get_region_stats,alt_realestate_landRegistry_model.get_region_stats,
alt.realestate.get_towns_sold_prices,alt_realestate_landRegistry_model.get_towns_sold_prices,
alt.companieshouse.get_search_results,alt_companieshouse_companieshouse_model.get_search_results,
alt.companieshouse.get_company_info,alt_companieshouse_companieshouse_model.get_company_info,
alt.companieshouse.get_officers,alt_companieshouse_companieshouse_model.get_officers,
alt.companieshouse.get_persons_with_significant_control,alt_companieshouse_companieshouse_model.get_persons_with_significant_control,
alt.companieshouse.get_filings,alt_companieshouse_companieshouse_model.get_filings,
alt.companieshouse.get_filing_document,alt_companieshouse_companieshouse_model.get_filing_document,
alt.companieshouse.download_filing_document,alt_companieshouse_companieshouse_view.download_filing_document,
alt.companieshouse.get_charges,alt_companieshouse_companieshouse_view.display_charges
crypto.candle,crypto_helpers.plot_candles,
crypto.chart,crypto_helpers.plot_chart,
crypto.dd.active,crypto_dd_glassnode_model.get_active_addresses,crypto_dd_glassnode_view.display_active_addresses
Can't render this file because it has a wrong number of fields in line 26.

View File

@@ -1281,6 +1281,36 @@ class KeysController(BaseController): # pylint: disable=too-many-public-methods
key=ns_parser.key, persist=True, show_output=True
)
@log_start_end(log=logger)
def call_companieshouse(self, other_args: List[str]):
"""Process companies house command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="companieshouse",
description="Set Companies House API key.",
)
parser.add_argument(
"-k",
"--key",
type=str,
dest="key",
help="key",
)
if not other_args:
console.print(
"For your API Key, visit: https://developer.company-information.service.gov.uk/overview"
)
return
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "-k")
ns_parser = self.parse_simple_args(parser, other_args)
if ns_parser:
self.status_dict["companieshouse"] = keys_model.set_companieshouse_key(
key=ns_parser.key, persist=True, show_output=True
)
@log_start_end(log=logger)
def call_nixtla(self, other_args: List[str]):
"""Process nixtla command"""

View File

@@ -83,6 +83,7 @@ API_DICT: Dict = {
"tokenterminal": "TOKEN_TERMINAL",
"stocksera": "STOCKSERA",
"dappradar": "DAPPRADAR",
"companieshouse": "COMPANIES_HOUSE",
"openai": "OPENAI",
"nixtla": "NIXTLA",
}
@@ -2750,6 +2751,82 @@ def check_dappradar_key(show_output: bool = False) -> str:
return str(status)
def set_companieshouse_key(
key: str,
persist: bool = False,
show_output: bool = False,
) -> str:
"""Set Companies House key
Parameters
----------
key: str
API ID
persist: bool, optional
If False, api key change will be contained to where it was changed. For example, a Jupyter notebook session.
If True, api key change will be global, i.e. it will affect terminal environment variables.
By default, False.
show_output: bool, optional
Display status string or not. By default, False.
Returns
-------
str
Status of key set
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> openbb.keys.companieshouse(
api_id="example_id",
)
"""
handle_credential("API_COMPANIESHOUSE_KEY", key, persist)
return check_companieshouse_key(show_output)
def check_companieshouse_key(show_output: bool = False) -> str:
"""Check Companies House key
Parameters
----------
show_output: bool
Display status string or not. By default, False.
Returns
-------
str
Status of key set
"""
current_user = get_current_user()
if current_user.credentials.API_COMPANIESHOUSE_KEY == "REPLACE_ME":
logger.info("Companies House key not defined")
status = KeyStatus.NOT_DEFINED
else:
r = request(
"https://api.company-information.service.gov.uk/company/00000118",
auth=(f"{current_user.credentials.API_COMPANIESHOUSE_KEY}", ""),
)
if r.status_code in [403, 401, 429]:
logger.warning("Companies House key defined, test failed")
status = KeyStatus.DEFINED_TEST_FAILED
elif r.status_code == 200:
logger.info("Companies House key defined, test passed")
status = KeyStatus.DEFINED_TEST_PASSED
else:
logger.warning("Companies House key defined, test inconclusive")
status = KeyStatus.DEFINED_TEST_INCONCLUSIVE
if show_output:
console.print(status.colorize())
return str(status)
# Set OpenAI key
def set_openai_key(key: str, persist: bool = False, show_output: bool = False) -> str:
"""Set OpenAI key

View File

@@ -921,9 +921,10 @@ en:
funds/exclusion: exclusion policy
funds/_country_ms: Current Country MorningStar
alternative/hn: Hacker News most popular stories
alternative/covid: COVID menu cases, deaths, rates
alternative/oss: Open Source menu star history, repos information
alternative/realestate: Real Estate menu sold prices, HPIs
alternative/covid: COVID menu, cases, deaths, rates
alternative/oss: Open Source menu, star history, repos information
alternative/realestate: Real Estate menu, sold prices, HPIs
alternative/companieshouse: Companies House menu, UK company information
alternative/covid/slopes: get countries with highest slope in cases
alternative/covid/country: select country for data
alternative/covid/_country: Country
@@ -943,6 +944,14 @@ en:
alternative/realestate/_enddate: End Date <YYYY-MM-DD>
alternative/realestate/regionstats: retrieve UK sold house price stats by region
alternative/realestate/_region: Region
alternative/companieshouse/_company: Current Company
alternative/companieshouse/search: search for companies by name
alternative/companieshouse/load: retrieve basic company details by company registration number
alternative/companieshouse/officers: retrieve company officies
alternative/companieshouse/signifcontrol: retrieve those with significant control over the company
alternative/companieshouse/filings: retrieve list of documents filed with Companies House (Default 100)
alternative/companieshouse/filingdocument: download filed document
alternative/companieshouse/charges: registered charges against the company
econometrics/_data_loc: Looking for data in
econometrics/load: load a dataset (also works with StatsModels datasets)
econometrics/export: export a processed dataset

View File

@@ -35,6 +35,7 @@
"API_TWITTER_KEY": "",
"API_TWITTER_SECRET_KEY": "",
"API_TWITTER_BEARER_TOKEN": "",
"API_COMPANIESHOUSE_KEY": "",
"API_DAPPRADAR_KEY": "",
"API_KEY_NIXTLA": ""
}

View File

@@ -95,6 +95,7 @@ class OpenBBSDK:
`covid`: Covid Module
`oss`: Oss Module
`realestate`: Realestate Module
`companieshouse`: CompaniesHouse Module
Attributes:
`hn`: Get top stories from HackerNews.\n

87
poetry.lock generated vendored
View File

@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
[[package]]
name = "absl-py"
@@ -7226,24 +7226,24 @@ files = [
[[package]]
name = "pywry"
version = "0.5.13"
version = "0.5.14"
description = ""
optional = false
python-versions = ">=3.8"
files = [
{file = "pywry-0.5.13-cp310-cp310-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:fc2d39cbaa7479ba2170192930288d94cdf17a9e3f9cf44b3884903f264b70fa"},
{file = "pywry-0.5.13-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:de9e75a2b24433c5a12adcf0ba4bf6e29c50edcaac3bae09c0073ba3484237a9"},
{file = "pywry-0.5.13-cp310-none-win_amd64.whl", hash = "sha256:c284428dceb64063026541b37d7786136ce4b281d0cd7aa716ced1e64a276c13"},
{file = "pywry-0.5.13-cp311-cp311-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:0d1f94bcfc9713c6194667f14f472be34640e9f3e8013faa02aea276d4571c8a"},
{file = "pywry-0.5.13-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a3627d92bed1a8e02bf87b18ef0a12868f8468a18b0b74925a73c507b1b8964f"},
{file = "pywry-0.5.13-cp311-none-win_amd64.whl", hash = "sha256:7d8f884e853466fb1ee8003aaf0ea1c643721adb2d0c75300ebc0f314008420b"},
{file = "pywry-0.5.13-cp38-cp38-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:ba97dc7406086748a16e4f21efc73c006d4f02f71941fcbbd11e17946ab878cb"},
{file = "pywry-0.5.13-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:c142839551c5e468cae3171dee7b230ba753cb38027a03079f3fa283671e190a"},
{file = "pywry-0.5.13-cp38-none-win_amd64.whl", hash = "sha256:646e41c18d3a0f762d2b59e14281fca223d7fe1f0b2a882ecf054840ee28d10d"},
{file = "pywry-0.5.13-cp39-cp39-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:9e950d076f1a2d7980753576c65e6540ea05f6b5b0a29b734035aaae64027dea"},
{file = "pywry-0.5.13-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:d6654cd7d45d66f359872d4e9dd2e70372daf96ea2458706eeafda198471a45e"},
{file = "pywry-0.5.13-cp39-none-win_amd64.whl", hash = "sha256:03b5fa018f1e5be8d42aff2f969b4cc068d13c894521341e8e59ec33af794324"},
{file = "pywry-0.5.13.tar.gz", hash = "sha256:b5344ddbe63b72ed22c5a3f7f6c4a59a5bd27f250fbca7af979f3972d3a1855c"},
{file = "pywry-0.5.14-cp310-cp310-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:bf30e8e6e59863614fe06144e90c3c95c43256f944222d070be07e71c0303ad6"},
{file = "pywry-0.5.14-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:46fc00c02dfbbd3d81aa0f43cb9c5111c06e748a850cc795dfcd800a3b78958f"},
{file = "pywry-0.5.14-cp310-none-win_amd64.whl", hash = "sha256:ecd8e4c4d95f5fb114d0baa6572d2392ae0cc866f200c5fccd6c4bfa3b1a29cc"},
{file = "pywry-0.5.14-cp311-cp311-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:db5a298d57b80cc33c6a03f572b80f14d0018b2c4204e2137072ee9dfb23b1c1"},
{file = "pywry-0.5.14-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:335e97b0f242bfa6037ec7fdc90b3625611be0ad40496a8cf152dae0c6ae6605"},
{file = "pywry-0.5.14-cp311-none-win_amd64.whl", hash = "sha256:66cba8d3144bcd118808d09c33044e27544561f5c9a7b397edb9c3b3fcf6d3d9"},
{file = "pywry-0.5.14-cp38-cp38-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:713fe32afa2b61595057344b6c0b47772080dc05ee9b2f210448ba8cb2b8ac76"},
{file = "pywry-0.5.14-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:d0479f3a52f27658eedb37128e230c3ea620eb113d1371326ae235006a29f3e0"},
{file = "pywry-0.5.14-cp38-none-win_amd64.whl", hash = "sha256:3f42fd9e1fe6ae40be6f13195719e648fbbebda29fe9ba846c59797f71839add"},
{file = "pywry-0.5.14-cp39-cp39-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:b2690978499d63dfac3fe712ed3e3752866d7eb40ed7dcb21590b0ff109cecb7"},
{file = "pywry-0.5.14-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:2bcba7d64b9cdf2f1d65f4d468f87b00629157d233de251805b0816a43ba738b"},
{file = "pywry-0.5.14-cp39-none-win_amd64.whl", hash = "sha256:c5b949f5d16a43aa2cc703159caa6de45b91e6008a4fa608d99909b7c89f45c3"},
{file = "pywry-0.5.14.tar.gz", hash = "sha256:a2a03b18f0847366a4347d30429afd8a50c95cc35713371a3d6bbed077db9983"},
]
[package.dependencies]
@@ -7843,8 +7843,7 @@ files = [
{file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win32.whl", hash = "sha256:763d65baa3b952479c4e972669f679fe490eee058d5aa85da483ebae2009d231"},
{file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:d000f258cf42fec2b1bbf2863c61d7b8918d31ffee905da62dede869254d3b8a"},
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e"},
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:1a6391a7cabb7641c32517539ca42cf84b87b667bad38b78d4d42dd23e957c81"},
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:9c7617df90c1365638916b98cdd9be833d31d337dbcd722485597b43c4a215bf"},
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_12_6_arm64.whl", hash = "sha256:721bc4ba4525f53f6a611ec0967bdcee61b31df5a56801281027a3a6d1c2daf5"},
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:41d0f1fa4c6830176eef5b276af04c89320ea616655d01327d5ce65e50575c94"},
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win32.whl", hash = "sha256:f6d3d39611ac2e4f62c3128a9eed45f19a6608670c5a2f4f07f24e8de3441d38"},
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:da538167284de58a52109a9b89b8f6a53ff8437dd6dc26d33b57bf6699153122"},
@@ -8202,7 +8201,7 @@ files = [
[package.dependencies]
packaging = ">=20.0"
setuptools = [
{version = "*"},
{version = "*", optional = true, markers = "extra != \"toml\""},
{version = ">=42", optional = true, markers = "extra == \"toml\""},
]
tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
@@ -8535,7 +8534,7 @@ files = [
]
[package.dependencies]
greenlet = {version = "!=0.4.17", markers = "platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\""}
greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""}
typing-extensions = ">=4.2.0"
[package.extras]
@@ -8677,52 +8676,6 @@ build = ["cython (>=0.29.26)"]
develop = ["cython (>=0.29.26)"]
docs = ["ipykernel", "jupyter-client", "matplotlib", "nbconvert", "nbformat", "numpydoc", "pandas-datareader", "sphinx"]
[[package]]
name = "statsmodels"
version = "0.13.3"
description = "Statistical computations and models for Python"
optional = false
python-versions = ">=3.7"
files = [
{file = "statsmodels-0.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b71bb64c6d4087dd6192eadfad390fbeb4074f676ef34c7e56579cead8c478e7"},
{file = "statsmodels-0.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:658b634c273c2f287a0086e56a5d6b95ec3ddac991cbb020b34f731e932de0bd"},
{file = "statsmodels-0.13.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab9f63f432889b179967ab645aea7480e28731823a3b99850d7f7a561b624f93"},
{file = "statsmodels-0.13.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f432fb7f54ce5edccc83aa36566653cd04ee35bbbefdf0a2b7bd9c97c5da443"},
{file = "statsmodels-0.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:4cd64076c3ad366b10fd4e6f8ca6aeb1e398ec5480bddb65fba8889dd9eb550d"},
{file = "statsmodels-0.13.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:33f9caff2dbdfef22505678407d2f549b32a4a2729eb8675b60eb2932fc0e883"},
{file = "statsmodels-0.13.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:393f6a7ec85f65be9ac1a13be152dd14c65084436c48bcdf94cb21ef0b6cb79c"},
{file = "statsmodels-0.13.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12b56d13d9a2af7a1aadc3fe9f3d3c18a5727a651323d94e7c2047177adfb9ce"},
{file = "statsmodels-0.13.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a61e0652f62b01981d8e857aa77550b42cf316c9d8e569b559869c248e3de834"},
{file = "statsmodels-0.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:5368bccd471bb8cef0a8957ba5f2a3e5b5ecc433b0783d9f602039df45c780d3"},
{file = "statsmodels-0.13.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1ecfb191958de187ba44b93316f4953b8b6588b5f68dcab218f76498a862dd7c"},
{file = "statsmodels-0.13.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ea2b481b15e9e501904a1c36efc5f9a202f87529e600a99c364fd7e4598ae88"},
{file = "statsmodels-0.13.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d270a11aac6720a8024e1136ab44036d0878f62995617bb5b9fc5c77ea3d3b8"},
{file = "statsmodels-0.13.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2185ed356823cd1c258c09b790f0c21d2fd49321e82c79f8f6dc546f1c671d7a"},
{file = "statsmodels-0.13.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9da39a36d114abcdcf8ebd351ed69229e23cb12b8a607996cb6511fa88e78b4d"},
{file = "statsmodels-0.13.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3b3a9942d0b462af4c68c3895095d304869cbec9d97f3c268f19a6ba7ba294dc"},
{file = "statsmodels-0.13.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fff0316420bc4f6fbd80dd77eb74f3834fcd0e4ca98ba9611b8a6d41ebbb979"},
{file = "statsmodels-0.13.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352041bc04eaf90232e54a86861a460365ef45f34f58529578487e6f640dadf3"},
{file = "statsmodels-0.13.3-cp38-cp38-win_amd64.whl", hash = "sha256:61a0f39848ebacf5560e1539ca0037b8fc25cc9d1d7444bbef5bdc0a3c56087b"},
{file = "statsmodels-0.13.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:78cd12b0ee543fa955d2bace18518fc7d2b57f13c65929b54445bf3e54955b08"},
{file = "statsmodels-0.13.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:afccb80e3ddc969bfb5285f846ac2622861ffe192423087214d60e4c6e40e384"},
{file = "statsmodels-0.13.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3609824e1ced44722bd905564d8ce94df29d24e32a6dd67cc9255932aedcd7b"},
{file = "statsmodels-0.13.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81f8e71963a7bd169338fbb1472e34ec85ae4447414ac37bdae5cf6d1ac223bb"},
{file = "statsmodels-0.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:000c7a1ce6780834f5fbb63f9ae07a00863a00f602c7c470c942153692f5bbc3"},
{file = "statsmodels-0.13.3.tar.gz", hash = "sha256:ed71df887334b1d332e71d33215122bdd54494dcb2248606b30bcfa6112e860a"},
]
[package.dependencies]
numpy = {version = ">=1.17", markers = "python_version != \"3.10\" or platform_system != \"Windows\" or platform_python_implementation == \"PyPy\""}
packaging = ">=21.3"
pandas = ">=0.25"
patsy = ">=0.5.2"
scipy = {version = ">=1.3", markers = "(python_version > \"3.7\" or platform_system != \"Windows\" or platform_machine != \"x86\") and python_version < \"3.12\""}
[package.extras]
build = ["cython (>=0.29.32)"]
develop = ["Jinja2", "colorama", "cython (>=0.29.32)", "cython (>=0.29.32,<3.0.0)", "flake8", "isort", "joblib", "matplotlib (>=3)", "oldest-supported-numpy (>=2022.4.18)", "pytest (>=7.0.1,<7.1.0)", "pytest-randomly", "pytest-xdist", "pywinpty", "setuptools-scm[toml] (>=7.0.0,<7.1.0)"]
docs = ["ipykernel", "jupyter-client", "matplotlib", "nbconvert", "nbformat", "numpydoc", "pandas-datareader", "sphinx"]
[[package]]
name = "statsmodels"
version = "0.13.5"
@@ -8765,7 +8718,7 @@ numpy = [
packaging = ">=21.3"
pandas = ">=0.25"
patsy = ">=0.5.2"
scipy = {version = ">=1.3", markers = "(python_version > \"3.9\" or platform_system != \"Windows\" or platform_machine != \"x86\") and python_version < \"3.12\""}
scipy = {version = ">=1.3", markers = "python_version > \"3.9\" and python_version < \"3.12\" or platform_system != \"Windows\" and python_version < \"3.12\" or platform_machine != \"x86\" and python_version < \"3.12\""}
[package.extras]
build = ["cython (>=0.29.32)"]
@@ -10558,4 +10511,4 @@ optimization = ["Riskfolio-Lib"]
[metadata]
lock-version = "2.0"
python-versions = "^3.8.1,<3.11, !=3.9.7"
content-hash = "1852494c8df358b3c3631c50ee1d081b113b9b2f6c2c728f1570e8ecae8a3fea"
content-hash = "8347de5903ddbda7e77ce2bcfe4349702d41233891c9ea65a407611c96b57d87"

2
pyproject.toml vendored
View File

@@ -101,7 +101,7 @@ jupyterlab-widgets = "^3.0.3"
mstarpy = "^0.0.4"
packaging = ">=22.0"
streamlit = "^1.24.1"
pywry = "^0.5.13"
pywry = "^0.5.14"
svglib = "^1.5.0"
sparqlwrapper = "^2.0.0"
y-py = "!=0.5.5" # Untraceable third level dependency that requires cargo to be built

View File

@@ -122,7 +122,7 @@ google-auth-oauthlib==1.0.0 ; python_full_version >= "3.8.1" and python_full_ver
google-auth==2.22.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
graphlib-backport==1.0.3 ; python_full_version >= "3.8.1" and python_version < "3.9"
graphviz==0.20.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
greenlet==2.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and (platform_machine == "win32" or platform_machine == "WIN32" or platform_machine == "AMD64" or platform_machine == "amd64" or platform_machine == "x86_64" or platform_machine == "ppc64le" or platform_machine == "aarch64")
greenlet==2.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and (platform_machine == "aarch64" or platform_machine == "ppc64le" or platform_machine == "x86_64" or platform_machine == "amd64" or platform_machine == "AMD64" or platform_machine == "win32" or platform_machine == "WIN32")
grpcio==1.56.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
h11==0.14.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7"
hijri-converter==2.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
@@ -315,7 +315,7 @@ pytz==2023.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7"
pywin32-ctypes==0.2.2 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7" and sys_platform == "win32"
pywin32==306 ; sys_platform == "win32" and platform_python_implementation != "PyPy" and python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
pywinpty==2.0.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and os_name == "nt"
pywry==0.5.13 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
pywry==0.5.14 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
pyyaml==6.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
pyzmq==25.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
qdldl==0.1.7.post0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"

4
requirements.txt vendored
View File

@@ -80,7 +80,7 @@ future==0.18.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.
gitdb==4.0.10 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
gitpython==3.1.32 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
graphlib-backport==1.0.3 ; python_full_version >= "3.8.1" and python_version < "3.9"
greenlet==2.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and (platform_machine == "win32" or platform_machine == "WIN32" or platform_machine == "AMD64" or platform_machine == "amd64" or platform_machine == "x86_64" or platform_machine == "ppc64le" or platform_machine == "aarch64")
greenlet==2.0.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and (platform_machine == "aarch64" or platform_machine == "ppc64le" or platform_machine == "x86_64" or platform_machine == "amd64" or platform_machine == "AMD64" or platform_machine == "win32" or platform_machine == "WIN32")
grpcio==1.56.2 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
h11==0.14.0 ; python_full_version >= "3.8.1" and python_version < "3.11" and python_full_version != "3.9.7"
hijri-converter==2.3.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
@@ -217,7 +217,7 @@ pytz-deprecation-shim==0.1.0.post0 ; python_full_version >= "3.8.1" and python_f
pytz==2023.3 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
pywin32==306 ; sys_platform == "win32" and platform_python_implementation != "PyPy" and python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
pywinpty==2.0.11 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11" and os_name == "nt"
pywry==0.5.13 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
pywry==0.5.14 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
pyyaml==6.0.1 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
pyzmq==25.1.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"
quandl==3.7.0 ; python_full_version >= "3.8.1" and python_full_version != "3.9.7" and python_version < "3.11"

View File

@@ -0,0 +1,172 @@
"""Test the companieshouse controller."""
import os
import pytest
from openbb_terminal.alternative.companieshouse import companieshouse_controller
from openbb_terminal.core.session.current_user import PreferencesModel, copy_user
@pytest.mark.vcr(record_mode="none")
@pytest.mark.record_stdout
def test_print_help():
controller = companieshouse_controller.CompaniesHouseController(queue=None)
controller.print_help()
@pytest.mark.vcr(record_mode="none")
def test_menu_without_queue_completion(mocker):
preferences = PreferencesModel(USE_PROMPT_TOOLKIT=True)
mock_current_user = copy_user(preferences=preferences)
mocker.patch(
target="openbb_terminal.core.session.current_user.__current_user",
new=mock_current_user,
)
mocker.patch(
target="openbb_terminal.parent_classes.session",
)
mocker.patch(
target="openbb_terminal.parent_classes.session.prompt",
return_value="quit",
)
mocker.patch(
target="openbb_terminal.alternative.companieshouse.companieshouse_controller.session",
)
mocker.patch(
target="openbb_terminal.alternative.companieshouse.companieshouse_controller.session.prompt",
return_value="quit",
)
result_menu = companieshouse_controller.CompaniesHouseController().menu()
assert result_menu == ["help"]
@pytest.mark.parametrize(
"mock_input",
["help", "homee help", "home help", "mock"],
)
@pytest.mark.vcr(record_mode="none")
def test_menu_without_queue_sys_exit(mock_input, mocker):
path_controller = (
"openbb_terminal.alternative.companieshouse.companieshouse_controller"
)
# DISABLE AUTO-COMPLETION
preferences = PreferencesModel(
USE_PROMPT_TOOLKIT=False,
)
mock_current_user = copy_user(preferences=preferences)
mocker.patch(
target="openbb_terminal.core.session.current_user.__current_user",
new=mock_current_user,
)
mocker.patch(
target=f"{path_controller}.session",
return_value=None,
)
# MOCK USER INPUT
mocker.patch("builtins.input", return_value=mock_input)
# MOCK SWITCH
mocker.patch(
target=f"{path_controller}.CompaniesHouseController.switch",
return_value=["quit"],
)
# MOCK SWITCH
class SystemExitSideEffect:
def __init__(self):
self.first_call = True
def __call__(self, *args, **kwargs):
if self.first_call:
self.first_call = False
raise SystemExit()
return ["quit"]
mock_switch = mocker.Mock(side_effect=SystemExitSideEffect())
mocker.patch(
target=f"{path_controller}.CompaniesHouseController.switch",
new=mock_switch,
)
result_menu = companieshouse_controller.CompaniesHouseController().menu()
assert result_menu == ["help"]
@pytest.mark.parametrize(
"an_input, expected_queue",
[
("", []),
("/help", ["home", "help"]),
("help/help", ["help", "help"]),
("q", ["quit"]),
("h", []),
("home", ["quit", "quit"]),
(
"r",
["quit", "quit", "reset", "alternative", "companieshouse"],
),
],
)
def test_switch(an_input, expected_queue):
controller = companieshouse_controller.CompaniesHouseController(queue=None)
queue = controller.switch(an_input=an_input)
assert queue == expected_queue
@pytest.mark.vcr(record_mode="none")
def test_call_cls(mocker):
mocker.patch("os.system")
controller = companieshouse_controller.CompaniesHouseController(queue=None)
controller.call_cls([])
assert controller.queue == []
os.system.assert_called_once_with("cls||clear")
@pytest.mark.parametrize(
"func, queue, expected_queue",
[
(
"call_exit",
[],
["quit", "quit", "quit"],
),
("call_exit", ["help"], ["quit", "quit", "quit", "help"]),
("call_home", [], ["quit", "quit"]),
("call_help", [], []),
("call_quit", [], ["quit"]),
("call_quit", ["help"], ["quit", "help"]),
(
"call_reset",
[],
["quit", "quit", "reset", "alternative", "companieshouse"],
),
(
"call_reset",
["help"],
["quit", "quit", "reset", "alternative", "companieshouse", "help"],
),
],
)
def test_call_func_expect_queue(expected_queue, func, queue):
controller = companieshouse_controller.CompaniesHouseController(queue=queue)
result = getattr(controller, func)([])
assert result is None
assert controller.queue == expected_queue
@pytest.mark.vcr(record_mode="none")
def test_quit():
controller = companieshouse_controller.CompaniesHouseController(queue=None)
controller.call_quit([])
assert controller.queue == ["quit"]

View File

@@ -0,0 +1,299 @@
import requests
# IMPORTATION INTERNAL
from openbb_terminal.alternative.companieshouse import companieshouse_model
from openbb_terminal.alternative.companieshouse.company_doc import CompanyDocument
def test_get_search_results(mocker):
mock_response = mocker.Mock()
mock_response.json.return_value = {
"total_results": 2369,
"items": [
{
"kind": "searchresults#company",
"matches": {"title": [1, 5], "snippet": [13, 17]},
"links": {"self": "/company/04366849"},
"company_type": "plc",
"address": {
"premises": "Shell Centre",
"address_line_1": "London",
"postal_code": "SE1 7NA",
},
"description": "04366849 - Incorporated on 5 February 2002",
"snippet": "ROYAL DUTCH SHELL ",
"title": "SHELL PLC",
"company_status": "active",
"date_of_creation": "2002-02-05",
"address_snippet": "Shell Centre, London, SE1 7NA",
"description_identifier": ["incorporated-on"],
"company_number": "04366849",
},
{
"address_snippet": "Shell Centre, York Road, London, United Kingdom, SE1 7NA",
"description_identifier": ["incorporated-on"],
"company_number": "03323845",
"date_of_creation": "1997-02-25",
"links": {"self": "/company/03323845"},
"matches": {"title": [1, 5], "snippet": [1, 5]},
"company_type": "ltd",
"address": {
"address_line_1": "York Road",
"postal_code": "SE1 7NA",
"country": "United Kingdom",
"premises": "Shell Centre",
"locality": "London",
},
"kind": "searchresults#company",
"snippet": "SHELL ",
"title": "SHELL GROUP LIMITED",
"company_status": "active",
"description": "03323845 - Incorporated on 25 February 1997",
},
],
"items_per_page": 2,
"kind": "search#companies",
"start_index": 0,
"page_number": 1,
}
mocker.patch.object(requests, "get", return_value=mock_response)
# Call the function that makes a request to the remote API
result = companieshouse_model.get_search_results("shell", 2)
assert len(result) == 2
assert result["Name"].iloc[0] == "SHELL PLC"
assert result["Company Number"].iloc[0] == "04366849"
def test_get_persons_with_significant_control(mocker):
mock_response = mocker.Mock()
mock_response.json.return_value = {
"ceased_count": 0,
"items_per_page": 25,
"total_results": 1,
"active_count": 1,
"start_index": 0,
"items": [
{
"notified_on": "2020-12-18",
"links": {
"self": "/company/13090621/persons-with-significant-control/individual/vLLtcUU1xdVC4x3tXu2szkiDluE"
},
"etag": "69ad137b49c648c6dd013761b82b52e6a7d474cb",
"kind": "individual-person-with-significant-control",
"country_of_residence": "England",
"name": "Mrs Shelley Ann Fray",
"natures_of_control": [
"ownership-of-shares-75-to-100-percent",
"voting-rights-75-to-100-percent",
"right-to-appoint-and-remove-directors",
],
"nationality": "British",
"address": {
"address_line_1": "Broadway",
"premises": "476-478",
"address_line_2": "Chadderton",
"country": "England",
"locality": "Oldham",
"postal_code": "OL9 9NS",
},
"date_of_birth": {"month": 6, "year": 1985},
"name_elements": {
"surname": "Fray",
"middle_name": "Ann",
"title": "Mrs",
"forename": "Shelley",
},
}
],
"links": {"self": "/company/13090621/persons-with-significant-control"},
}
mocker.patch.object(requests, "get", return_value=mock_response)
# Call the function that makes a request to the remote API
result = companieshouse_model.get_persons_with_significant_control("13090621")
assert result["Name"].iloc[0] == "Mrs Shelley Ann Fray"
def test_get_officers(mocker):
mock_response = mocker.Mock()
mock_response.json.return_value = {
"total_results": 1,
"links": {"self": "/company/13090621/officers"},
"items_per_page": 35,
"items": [
{
"links": {
"self": "/company/13090621/appointments/y2Xs68vEDqfz-zbmxzc4QucY8fQ",
"officer": {
"appointments": "/officers/MSVxvbR-XzoxCZazKccwdkkmCYE/appointments"
},
},
"date_of_birth": {"month": 6, "year": 1985},
"address": {
"country": "England",
"address_line_2": "Chadderton",
"address_line_1": "Broadway",
"locality": "Oldham",
"premises": "476-478",
"postal_code": "OL9 9NS",
},
"name": "FRAY, Shelley Ann",
"occupation": "Director",
"appointed_on": "2020-12-18",
"country_of_residence": "England",
"officer_role": "director",
"nationality": "British",
}
],
"etag": "1138d965d225f6a0e5685c6d35e84e6a2dc0fc4f",
"resigned_count": 0,
"start_index": 0,
"inactive_count": 0,
"active_count": 1,
"kind": "officer-list",
}
mocker.patch.object(requests, "get", return_value=mock_response)
# Call the function that makes a request to the remote API
result = companieshouse_model.get_officers("13090621")
assert len(result) == 1
assert result["Officer Role"].iloc[0] == "director"
def test_get_charges(mocker):
mock_response = mocker.Mock()
mock_response.json.return_value = {
"etag": "0921370a2f5d8d191cc0c96de8de4ad7acdf68db",
"total_count": 1,
"unfiltered_count": 1,
"satisfied_count": 0,
"part_satisfied_count": 0,
"items": [
{
"etag": "679e0c72b80543b2dc390b3d102163cf6cb89430",
"charge_code": "027235340001",
"classification": {
"type": "charge-description",
"description": "A registered charge",
},
"charge_number": 1,
"status": "outstanding",
"delivered_on": "2014-12-04",
"created_on": "2014-11-26",
"particulars": {
"contains_fixed_charge": True,
"contains_negative_pledge": True,
},
"persons_entitled": [{"name": "Astrazeneca Pensions Trustee Limited "}],
"transactions": [
{
"filing_type": "create-charge-with-deed",
"delivered_on": "2014-12-04",
"links": {
"filing": "/company/02723534/filing-history/MzExMzExMzg3N2FkaXF6a2N4"
},
}
],
"links": {
"self": "/company/02723534/charges/ObJFuAILDHfp00ro3wjqXEonL7k"
},
}
],
}
mocker.patch.object(requests, "get", return_value=mock_response)
# Call the function that makes a request to the remote API
result = companieshouse_model.get_charges("13090621")
assert len(result) == 1
def test_get_filings(mocker):
mock_response = mocker.Mock()
mock_response.json.return_value = {
"total_count": 3,
"items_per_page": 25,
"items": [
{
"action_date": "2022-12-17",
"category": "confirmation-statement",
"date": "2023-01-06",
"description": "confirmation-statement-with-no-updates",
"description_values": {"made_up_date": "2022-12-17"},
"links": {
"self": "/company/13090621/filing-history/MzM2NDcxNTAzMWFkaXF6a2N4",
"document_metadata": "https://frontend-doc-api.company-information.service.gov.uk/document/ZIvX",
},
"type": "CS01",
"pages": 3,
"barcode": "XBUK9D4X",
"transaction_id": "MzM2NDcxNTAzMWFkaXF6a2N4",
},
{
"category": "accounts",
"date": "2022-09-14",
"description": "accounts-with-accounts-type-unaudited-abridged",
"description_values": {"made_up_date": "2021-12-31"},
"links": {
"self": "/company/13090621/filing-history/MzM1MTY4NjU5NmFkaXF6a2N4",
"document_metadata": "https://frontend-doc-api.company-information.service.gov.uk/document/8_jg",
},
"type": "AA",
"pages": 9,
"barcode": "XBCG6V3N",
"transaction_id": "MzM1MTY4NjU5NmFkaXF6a2N4",
},
{
"action_date": "2022-09-09",
"category": "address",
"date": "2022-09-09",
"description": "change-registered-office-address-company-with-date-old-address-new-address",
"description_values": {
"change_date": "2022-09-09",
"old_address": "1J Shillington Old School Este Road London SW11 2TB",
"new_address": "476-478 Broadway Chadderton Oldham OL9 9NS",
},
"links": {
"self": "/company/13090621/filing-history/MzM1MTIwMjE4NGFkaXF6a2N4",
"document_metadata": "https://frontend-doc-api.company-information.service.gov.uk/document/h1b5B84v",
},
"type": "AD01",
"pages": 1,
"barcode": "XBC30YEG",
"transaction_id": "MzM1MTIwMjE4NGFkaXF6a2N4",
},
],
"start_index": 0,
"filing_history_status": "filing-history-available",
}
mocker.patch.object(requests, "get", return_value=mock_response)
# Call the function that makes a request to the remote API
result = companieshouse_model.get_filings("13090621", 0)
assert len(result.filings) == 3
assert result.filings["Category"].iloc[0] == "confirmation-statement"
def test_get_filing_document(mocker):
result = CompanyDocument(
"confirmation-statement",
"2023-01-06",
"confirmation-statement-with-no-updates",
"paper_filed",
3,
"MzM2NDcxNTAzMWFkaXF6a2N4",
"content",
)
assert result.description == "confirmation-statement-with-no-updates"
assert result.pages == 3

View File

@@ -0,0 +1,283 @@
import pandas as pd
import pytest
import requests
# IMPORTATION INTERNAL
from openbb_terminal.alternative.companieshouse import companieshouse_view
from openbb_terminal.helper_funcs import print_rich_table
@pytest.mark.vcr
@pytest.mark.record_stdout
def test_display_search(mocker):
mock_response = mocker.Mock()
mock_response.json.return_value = {
"total_results": 2369,
"items": [
{
"kind": "searchresults#company",
"matches": {"title": [1, 5], "snippet": [13, 17]},
"links": {"self": "/company/04366849"},
"company_type": "plc",
"address": {
"premises": "Shell Centre",
"address_line_1": "London",
"postal_code": "SE1 7NA",
},
"description": "04366849 - Incorporated on 5 February 2002",
"snippet": "ROYAL DUTCH SHELL ",
"title": "SHELL PLC",
"company_status": "active",
"date_of_creation": "2002-02-05",
"address_snippet": "Shell Centre, London, SE1 7NA",
"description_identifier": ["incorporated-on"],
"company_number": "04366849",
},
{
"address_snippet": "Shell Centre, York Road, London, United Kingdom, SE1 7NA",
"description_identifier": ["incorporated-on"],
"company_number": "03323845",
"date_of_creation": "1997-02-25",
"links": {"self": "/company/03323845"},
"matches": {"title": [1, 5], "snippet": [1, 5]},
"company_type": "ltd",
"address": {
"address_line_1": "York Road",
"postal_code": "SE1 7NA",
"country": "United Kingdom",
"premises": "Shell Centre",
"locality": "London",
},
"kind": "searchresults#company",
"snippet": "SHELL ",
"title": "SHELL GROUP LIMITED",
"company_status": "active",
"description": "03323845 - Incorporated on 25 February 1997",
},
],
"items_per_page": 2,
"kind": "search#companies",
"start_index": 0,
"page_number": 1,
}
mocker.patch.object(requests, "get", return_value=mock_response)
# Call the function that makes a request to the remote API
companieshouse_view.display_search("shell", 2)
@pytest.mark.vcr
@pytest.mark.record_stdout
def test_display_persons_with_significant_control(mocker):
mock_response = mocker.Mock()
mock_response.json.return_value = {
"ceased_count": 0,
"items_per_page": 25,
"total_results": 1,
"active_count": 1,
"start_index": 0,
"items": [
{
"notified_on": "2020-12-18",
"links": {
"self": "/company/13090621/persons-with-significant-control/individual/vLLtcUU1xdVC4x3tXu2szkiDluE"
},
"etag": "69ad137b49c648c6dd013761b82b52e6a7d474cb",
"kind": "individual-person-with-significant-control",
"country_of_residence": "England",
"name": "Mrs Shelley Ann Fray",
"natures_of_control": [
"ownership-of-shares-75-to-100-percent",
"voting-rights-75-to-100-percent",
"right-to-appoint-and-remove-directors",
],
"nationality": "British",
"address": {
"address_line_1": "Broadway",
"premises": "476-478",
"address_line_2": "Chadderton",
"country": "England",
"locality": "Oldham",
"postal_code": "OL9 9NS",
},
"date_of_birth": {"month": 6, "year": 1985},
"name_elements": {
"surname": "Fray",
"middle_name": "Ann",
"title": "Mrs",
"forename": "Shelley",
},
}
],
"links": {"self": "/company/13090621/persons-with-significant-control"},
}
mocker.patch.object(requests, "get", return_value=mock_response)
# Call the function that makes a request to the remote API
companieshouse_view.display_persons_with_significant_control("13090621")
@pytest.mark.vcr
@pytest.mark.record_stdout
def test_display_officers(mocker):
mock_response = mocker.Mock()
mock_response.json.return_value = {
"total_results": 1,
"links": {"self": "/company/13090621/officers"},
"items_per_page": 35,
"items": [
{
"links": {
"self": "/company/13090621/appointments/y2Xs68vEDqfz-zbmxzc4QucY8fQ",
"officer": {
"appointments": "/officers/MSVxvbR-XzoxCZazKccwdkkmCYE/appointments"
},
},
"date_of_birth": {"month": 6, "year": 1985},
"address": {
"country": "England",
"address_line_2": "Chadderton",
"address_line_1": "Broadway",
"locality": "Oldham",
"premises": "476-478",
"postal_code": "OL9 9NS",
},
"name": "FRAY, Shelley Ann",
"occupation": "Director",
"appointed_on": "2020-12-18",
"country_of_residence": "England",
"officer_role": "director",
"nationality": "British",
}
],
"etag": "1138d965d225f6a0e5685c6d35e84e6a2dc0fc4f",
"resigned_count": 0,
"start_index": 0,
"inactive_count": 0,
"active_count": 1,
"kind": "officer-list",
}
mocker.patch.object(requests, "get", return_value=mock_response)
# Call the function that makes a request to the remote API
companieshouse_view.display_officers("13090621")
@pytest.mark.vcr
@pytest.mark.record_stdout
def test_display_charges(mocker):
mock_response = mocker.Mock()
mock_response.json.return_value = {
"etag": "0921370a2f5d8d191cc0c96de8de4ad7acdf68db",
"total_count": 1,
"unfiltered_count": 1,
"satisfied_count": 0,
"part_satisfied_count": 0,
"items": [
{
"etag": "679e0c72b80543b2dc390b3d102163cf6cb89430",
"charge_code": "027235340001",
"classification": {
"type": "charge-description",
"description": "A registered charge",
},
"charge_number": 1,
"status": "outstanding",
"delivered_on": "2014-12-04",
"created_on": "2014-11-26",
"particulars": {
"contains_fixed_charge": True,
"contains_negative_pledge": True,
},
"persons_entitled": [{"name": "Astrazeneca Pensions Trustee Limited "}],
"transactions": [
{
"filing_type": "create-charge-with-deed",
"delivered_on": "2014-12-04",
"links": {
"filing": "/company/02723534/filing-history/MzExMzExMzg3N2FkaXF6a2N4"
},
}
],
"links": {
"self": "/company/02723534/charges/ObJFuAILDHfp00ro3wjqXEonL7k"
},
}
],
}
mocker.patch.object(requests, "get", return_value=mock_response)
df = pd.DataFrame.from_records(mock_response.json.return_value["items"]).iloc[0]
# Call the function that makes a request to the remote API
print_rich_table(df, show_index=True, title="Charges")
@pytest.mark.vcr
@pytest.mark.record_stdout
def test_display_filings(mocker):
mock_response = mocker.Mock()
mock_response.json.return_value = {
"total_count": 3,
"items_per_page": 25,
"items": [
{
"action_date": "2022-12-17",
"category": "confirmation-statement",
"date": "2023-01-06",
"description": "confirmation-statement-with-no-updates",
"description_values": {"made_up_date": "2022-12-17"},
"links": {
"self": "/company/13090621/filing-history/MzM2NDcxNTAzMWFkaXF6a2N4",
"document_metadata": "https://frontend-doc-api.company-information.service.gov.uk/document/ZIvX",
},
"type": "CS01",
"pages": 3,
"barcode": "XBUK9D4X",
"transaction_id": "MzM2NDcxNTAzMWFkaXF6a2N4",
},
{
"category": "accounts",
"date": "2022-09-14",
"description": "accounts-with-accounts-type-unaudited-abridged",
"description_values": {"made_up_date": "2021-12-31"},
"links": {
"self": "/company/13090621/filing-history/MzM1MTY4NjU5NmFkaXF6a2N4",
"document_metadata": "https://frontend-doc-api.company-information.service.gov.uk/document/8_jg",
},
"type": "AA",
"pages": 9,
"barcode": "XBCG6V3N",
"transaction_id": "MzM1MTY4NjU5NmFkaXF6a2N4",
},
{
"action_date": "2022-09-09",
"category": "address",
"date": "2022-09-09",
"description": "change-registered-office-address-company-with-date-old-address-new-address",
"description_values": {
"change_date": "2022-09-09",
"old_address": "1J Shillington Old School Este Road London SW11 2TB",
"new_address": "476-478 Broadway Chadderton Oldham OL9 9NS",
},
"links": {
"self": "/company/13090621/filing-history/MzM1MTIwMjE4NGFkaXF6a2N4",
"document_metadata": "https://frontend-doc-api.company-information.service.gov.uk/document/h1b5B84v",
},
"type": "AD01",
"pages": 1,
"barcode": "XBC30YEG",
"transaction_id": "MzM1MTIwMjE4NGFkaXF6a2N4",
},
],
"start_index": 0,
"filing_history_status": "filing-history-available",
}
mocker.patch.object(requests, "get", return_value=mock_response)
# Call the function that makes a request to the remote API
companieshouse_view.display_filings("13090621", 0)

View File

@@ -0,0 +1,10 @@
Current Company:
search search for companies by name
load retrieve basic company details by company registration number
officers retrieve company officies
signifcontrol retrieve those with significant control over the company
filings retrieve list of documents filed with Companies House (Default 100)
filingdocument download filed document
charges registered charges against the company

View File

@@ -0,0 +1,11 @@
etag 679e0c72b80543b2dc390b3d102163cf6cb89430
charge_code 027235340001
classification {'type': 'charge-description', 'description': ...
charge_number 1
status outstanding
delivered_on 2014-12-04
created_on 2014-11-26
particulars {'contains_fixed_charge': True, 'contains_nega...
persons_entitled [{'name': 'Astrazeneca Pensions Trustee Limite...
transactions [{'filing_type': 'create-charge-with-deed', 'd...
links {'self': '/company/02723534/charges/ObJFuAILDH...

View File

@@ -0,0 +1,5 @@
Retrieved 3 filings
Category Date Description Type Pages Transaction ID Download
0 confirmation-statement 2023-01-06 confirmation-statement-with-no-updates CS01 3 MzM2NDcxNTAzMWFkaXF6a2N4 https://find-and-update.company-information.service.gov.uk/company/13090621/filing-history/MzM2NDcxNTAzMWFkaXF6a2N4/document?format=pdf&download=0
1 accounts 2022-09-14 accounts-with-accounts-type-unaudited-abridged AA 9 MzM1MTY4NjU5NmFkaXF6a2N4 https://find-and-update.company-information.service.gov.uk/company/13090621/filing-history/MzM1MTY4NjU5NmFkaXF6a2N4/document?format=pdf&download=0
2 address 2022-09-09 change-registered-office-address-company-with-date-old-address-new-address AD01 1 MzM1MTIwMjE4NGFkaXF6a2N4 https://find-and-update.company-information.service.gov.uk/company/13090621/filing-history/MzM1MTIwMjE4NGFkaXF6a2N4/document?format=pdf&download=0

View File

@@ -0,0 +1,3 @@
Retrieved 1 records
Officer Role Appointed On Resigned On Name
0 director 2020-12-18 - FRAY, Shelley Ann

View File

@@ -0,0 +1,3 @@
Retrieved 1 records
Kind Name Natures of Control Notified On
0 individual-person-with-significant-control Mrs Shelley Ann Fray [ownership-of-shares-75-to-100-percent, voting-rights-75-to-100-percent, right-to-appoint-and-remove-directors] 2020-12-18

View File

@@ -0,0 +1,4 @@
Retrieved 2 records
Name Company Number Status
0 SHELL PLC 04366849 active
1 SHELL GROUP LIMITED 03323845 active

View File

@@ -1,6 +1,7 @@
> covid COVID menu cases, deaths, rates
> oss Open Source menu star history, repos information
> realestate Real Estate menu sold prices, HPIs
> covid COVID menu, cases, deaths, rates
> oss Open Source menu, star history, repos information
> realestate Real Estate menu, sold prices, HPIs
> companieshouse Companies House menu, UK company information
hn Hacker News most popular stories [HackerNews]

View File

@@ -4,10 +4,9 @@ sidebar_position: 1
description: API (Application Programming Interface) keys are access credentials for accessing data from a particular source. Learn how to set, manage, and access data APIs for the OpenBB SDK.
keywords: [api, keys, api keys, data provider, data, free, alpha vantage, fred, iex, twitter, degiro, binance, coinglass, polygon, intrinio, sdk, alphavantage, bitquery, coinbase, databento, finnhub, FRED, github, glassnode, iex cloud, news API, robinhood, santiment, shroomdk, token terminal, tradier, twitter, whale alert]
---
import HeadTitle from '@site/src/components/General/HeadTitle.tsx';
<HeadTitle title="API Keys - SDK | OpenBB Docs" />
`<HeadTitle title="API Keys - SDK | OpenBB Docs" />`
## The Keys Module
@@ -176,8 +175,6 @@ openbb.keys.biztoc(key = "REPLACE_WITH_KEY", persist=True)
### CoinMarketCap
### CoinMarketCap
> CoinMarketCap is the world's most-referenced price-tracking website for cryptoassets in the rapidly growing cryptocurrency space. Its mission is to make crypto discoverable and efficient globally by empowering retail users with unbiased, high quality and accurate information for drawing their own informed conclusions.
<details>
@@ -250,6 +247,29 @@ openbb.keys.coinglass(key = 'REPLACE_WITH_KEY', persist = True)
</details>
### Companies House
> The Companies House API lets you retrieve information about limited companies (and other companies that fall within the Companies Act 2006). The data returned is live and real-time, and is simple to use and understand.
<details>
<summary>Instructions</summary>
Setup an account by following the instructions [here](https://developer-specs.company-information.service.gov.uk/guides/gettingStarted#set-up-a-companies-house-account).
![Companies House](https://user-images.githubusercontent.com/85772166/234980988-352e36f7-334b-4e4c-846f-2cea128f9464.png)
After creating the account and logging in, click on, [Create an Application](https://developer.company-information.service.gov.uk/manage-applications/add). Fill out the form and then click the `Create` button at the bottom of the page.
![Create an Application](https://user-images.githubusercontent.com/85772166/234981083-e78cf4f7-3be0-404e-b7cc-b452679bbb06.png)
Once the application is created, an API key can be generated by clicking on `View all Applications`, and then the `Create new key` button at the bottom. Copy the API key value to the clipboard and then enter it into the OpenBB SDK with:
```console
openbb.keys.companieshouse('REPLACE_WITH_KEY', persist = True)
```
</details>
### Crypto Panic
> CryptoPanic is a news aggregator platform indicating impact on price and market for traders and cryptocurrency enthusiasts.

View File

@@ -223,6 +223,29 @@ With the account created, find the assigned API key within the account profile p
</details>
### Companies House
> The Companies House API lets you retrieve information about limited companies (and other companies that fall within the Companies Act 2006). The data returned is live and real-time, and is simple to use and understand.
<details>
<summary>Instructions</summary>
Setup an account by following the instructions [here](https://developer-specs.company-information.service.gov.uk/guides/gettingStarted#set-up-a-companies-house-account).
![Companies House](https://user-images.githubusercontent.com/85772166/234980988-352e36f7-334b-4e4c-846f-2cea128f9464.png)
After creating the account and logging in, click on, [Create an Application](https://developer.company-information.service.gov.uk/manage-applications/add). Fill out the form and then click the `Create` button at the bottom of the page.
![Create an Application](https://user-images.githubusercontent.com/85772166/234981083-e78cf4f7-3be0-404e-b7cc-b452679bbb06.png)
Once the application is created, an API key can be generated by clicking on `View all Applications`, and then the `Create new key` button at the bottom. Copy the API key value to the clipboard and then enter it into the OpenBB Terminal with:
```console
keys/companieshouse -k REPLACE_WITH_KEY
```
</details>
### Crypto Panic
> CryptoPanic is a news aggregator platform indicating impact on price and market for traders and cryptocurrency enthusiasts.