summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arial.ttfbin0 -> 367112 bytes
-rw-r--r--assets/arial.ttfbin0 -> 367112 bytes
-rw-r--r--main.py164
-rw-r--r--main.spec56
-rw-r--r--poetry.lock380
-rw-r--r--pyproject.toml20
6 files changed, 620 insertions, 0 deletions
diff --git a/arial.ttf b/arial.ttf
new file mode 100644
index 0000000..ff0815c
--- /dev/null
+++ b/arial.ttf
Binary files differ
diff --git a/assets/arial.ttf b/assets/arial.ttf
new file mode 100644
index 0000000..ff0815c
--- /dev/null
+++ b/assets/arial.ttf
Binary files differ
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..628ff25
--- /dev/null
+++ b/main.py
@@ -0,0 +1,164 @@
+import pandas as pd
+import tkinter as tk
+from tkinter import ttk
+from tkinter.filedialog import askopenfilename, asksaveasfilename
+
+class CompanySelector:
+ def __init__(self, master):
+ self.master = master
+ self.master.title("Company Selector")
+
+ self.select_file_button = tk.Button(self.master, text="Select Excel File", command=self.load_file)
+ self.select_file_button.pack()
+
+ def load_file(self):
+ file_path = askopenfilename(filetypes=[("Excel files", "*.xlsx *.xls")])
+ if not file_path:
+ return
+
+ self.df = pd.read_excel(file_path)
+ self.show_companies()
+
+ def show_companies(self):
+ self.select_file_button.pack_forget()
+
+ self.company_listbox = tk.Listbox(self.master, selectmode=tk.MULTIPLE, exportselection=False)
+ self.company_listbox.pack()
+
+ for company in self.df["Company Name"]:
+ self.company_listbox.insert(tk.END, company)
+
+ self.create_pyramid_button = tk.Button(self.master, text="Create Pyramid", command=self.create_pyramid)
+ self.create_pyramid_button.pack()
+
+ def create_pyramid(self):
+ selected_companies = [self.company_listbox.get(index) for index in self.company_listbox.curselection()]
+ selected_df = self.df[self.df["Company Name"].isin(selected_companies)].sort_values(by="Weighting", ascending=False)
+ pyramid_file_path = asksaveasfilename(defaultextension=".xlsx", filetypes=[("Excel files", "*.xlsx *.xls")])
+
+ if not pyramid_file_path:
+ return
+
+ with pd.ExcelWriter(pyramid_file_path) as writer:
+ selected_df.to_excel(writer, index=False)
+
+ image_file_path = asksaveasfilename(defaultextension=".png", filetypes=[("Image files","*.png")])
+
+ company_scores = {}
+
+ # For each 'Company Name', company_scores[company] = 'Weighting' for that company
+ for company, ticker, weighting in zip(selected_df["Company Name"], selected_df["Symbol"], selected_df["Weighting"]):
+ company_scores[f'{company} ({ticker})'] = weighting
+
+ from PIL import Image, ImageDraw, ImageFont
+ import math
+ # Sort the dictionary by score in ascending order
+ company_scores = {k: v for k, v in sorted(company_scores.items(), key=lambda item: item[1])}
+
+ # Group the companies by their scores
+ pyramid = {i: [] for i in range(1, 7)}
+ for company, score in company_scores.items():
+ pyramid[score].append(company)
+
+ # Initialize some parameters
+ img_width = 3300
+ img_height = 2550
+ block_color = (0, 0, 255) # Blue color
+ font_color = (255, 255, 255) # White color
+ padding = 10 # Padding around blocks
+ radius = 20
+
+ # Group the companies by their scores
+ pyramid = {}
+ for company, score in company_scores.items():
+ if score not in pyramid:
+ pyramid[score] = []
+ pyramid[score].append(company)
+
+ # Sort the pyramid keys in ascending order to have a pyramid shape
+ sorted_keys = sorted(pyramid.keys())
+
+ # Calculate the maximum number of companies in a group (this will be the width of your pyramid)
+ max_companies = max(len(v) for v in pyramid.values())
+
+ # Calculate the total number of groups (the height of your pyramid)
+ total_groups = len(pyramid)
+
+ # Calculate the total number of groups (the height of your pyramid)
+ total_groups = len(pyramid)
+
+ # Calculate the size of each block based on the width and height of the image and the number of blocks
+ block_size = min((img_width - padding) // max_companies - padding, (img_height - padding) // total_groups - padding)
+
+ # Calculate the size of each block based on the width and height of the image and the number of blocks
+ block_width = (img_width - padding) // max_companies - padding
+ block_height = (img_height - padding) // total_groups - padding
+
+ # Calculate the total width and height of the blocks (including padding)
+ total_width = max_companies * (block_width + padding)
+ total_height = total_groups * (block_height + padding)
+
+ # Calculate the starting position for the first block
+ start_x = (img_width - total_width) // 2
+ start_y = (img_height - total_height) // 2
+
+ # Create an image big enough to hold the pyramid
+ img = Image.new('RGB', (img_width, img_height), "white")
+ d = ImageDraw.Draw(img)
+
+ def wrap_text(text, max_length):
+ words = text.split()
+ lines = []
+ current_line = []
+
+ for word in words:
+ if len(' '.join(current_line + [word])) <= max_length:
+ current_line.append(word)
+ else:
+ lines.append(' '.join(current_line))
+ current_line = [word]
+ lines.append(' '.join(current_line))
+
+ return '\n'.join(lines)
+
+
+ # Loop over each level of the pyramid
+ for i, score in enumerate(sorted_keys):
+ companies = pyramid[score]
+ for j, company in enumerate(companies):
+ # Calculate the position of the block
+ x = start_x + j * (block_width + padding) + (total_width - len(companies) * (block_width + padding)) // 2
+ y = start_y + i * (block_size + padding)
+
+ # Draw the block
+ d.rounded_rectangle([x, y, x + block_width, y + block_height], fill=block_color, radius=radius)
+
+ # Adjust font size based on the length of the company name and block size
+ font_size = min(block_width // (len(company) // 2 + 1), block_height // 2)
+ fnt = ImageFont.truetype('./assets/arial.ttf', font_size)
+
+ # Implement word wrap for the company name
+ wrapped_company = wrap_text(company, block_width // font_size)
+
+ # Draw the company name
+ bbox = d.textbbox((x, y), wrapped_company, font=fnt)
+ text_width = bbox[2] - bbox[0]
+ text_height = bbox[3] - bbox[1]
+ text_x = x + (block_width - text_width) // 2
+ text_y = y + (block_height - text_height) // 2
+ d.text((text_x, text_y), wrapped_company, font=fnt, fill=font_color)
+
+
+ # Save the image
+ if not image_file_path:
+ return
+
+ img.save(image_file_path)
+
+
+ self.master.quit()
+
+if __name__ == "__main__":
+ root = tk.Tk()
+ app = CompanySelector(root)
+ root.mainloop()
diff --git a/main.spec b/main.spec
new file mode 100644
index 0000000..0d3af44
--- /dev/null
+++ b/main.spec
@@ -0,0 +1,56 @@
+# -*- mode: python ; coding: utf-8 -*-
+
+
+block_cipher = None
+
+
+a = Analysis(
+ ['main.py'],
+ pathex=[],
+ binaries=[],
+ datas=[('assets', 'assets')],
+ hiddenimports=[],
+ hookspath=[],
+ hooksconfig={},
+ runtime_hooks=[],
+ excludes=[],
+ win_no_prefer_redirects=False,
+ win_private_assemblies=False,
+ cipher=block_cipher,
+ noarchive=False,
+)
+pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
+
+exe = EXE(
+ pyz,
+ a.scripts,
+ [],
+ exclude_binaries=True,
+ name='main',
+ debug=False,
+ bootloader_ignore_signals=False,
+ strip=False,
+ upx=True,
+ console=False,
+ disable_windowed_traceback=False,
+ argv_emulation=False,
+ target_arch=None,
+ codesign_identity=None,
+ entitlements_file=None,
+)
+coll = COLLECT(
+ exe,
+ a.binaries,
+ a.zipfiles,
+ a.datas,
+ strip=False,
+ upx=True,
+ upx_exclude=[],
+ name='main',
+)
+app = BUNDLE(
+ coll,
+ name='main.app',
+ icon=None,
+ bundle_identifier=None,
+)
diff --git a/poetry.lock b/poetry.lock
new file mode 100644
index 0000000..8593ea0
--- /dev/null
+++ b/poetry.lock
@@ -0,0 +1,380 @@
+[[package]]
+name = "altgraph"
+version = "0.17.3"
+description = "Python graph (network) package"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "contourpy"
+version = "1.0.7"
+description = "Python library for calculating contours of 2D quadrilateral grids"
+category = "main"
+optional = false
+python-versions = ">=3.8"
+
+[package.dependencies]
+numpy = ">=1.16"
+
+[package.extras]
+bokeh = ["bokeh", "chromedriver", "selenium"]
+docs = ["furo", "sphinx-copybutton"]
+mypy = ["contourpy", "docutils-stubs", "mypy (==0.991)", "types-pillow"]
+test = ["matplotlib", "pillow", "pytest"]
+test-no-images = ["pytest"]
+
+[[package]]
+name = "cycler"
+version = "0.11.0"
+description = "Composable style cycles"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "et-xmlfile"
+version = "1.1.0"
+description = "An implementation of lxml.xmlfile for the standard library"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "fonttools"
+version = "4.39.4"
+description = "Tools to manipulate font files"
+category = "main"
+optional = false
+python-versions = ">=3.8"
+
+[package.extras]
+all = ["fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "zopfli (>=0.1.4)", "lz4 (>=1.7.4.2)", "matplotlib", "sympy", "skia-pathops (>=0.5.0)", "uharfbuzz (>=0.23.0)", "brotlicffi (>=0.8.0)", "scipy", "brotli (>=1.0.1)", "munkres", "unicodedata2 (>=15.0.0)", "xattr"]
+graphite = ["lz4 (>=1.7.4.2)"]
+interpolatable = ["scipy", "munkres"]
+lxml = ["lxml (>=4.0,<5)"]
+pathops = ["skia-pathops (>=0.5.0)"]
+plot = ["matplotlib"]
+repacker = ["uharfbuzz (>=0.23.0)"]
+symfont = ["sympy"]
+type1 = ["xattr"]
+ufo = ["fs (>=2.2.0,<3)"]
+unicode = ["unicodedata2 (>=15.0.0)"]
+woff = ["zopfli (>=0.1.4)", "brotlicffi (>=0.8.0)", "brotli (>=1.0.1)"]
+
+[[package]]
+name = "importlib-resources"
+version = "5.12.0"
+description = "Read resources from Python packages"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
+
+[package.extras]
+docs = ["sphinx (>=3.5)", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "furo", "sphinx-lint", "jaraco.tidelift (>=1.4)"]
+testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "flake8 (<5)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "pytest-flake8"]
+
+[[package]]
+name = "kiwisolver"
+version = "1.4.4"
+description = "A fast implementation of the Cassowary constraint solver"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "macholib"
+version = "1.16.2"
+description = "Mach-O header analysis and editing"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+altgraph = ">=0.17"
+
+[[package]]
+name = "matplotlib"
+version = "3.7.1"
+description = "Python plotting package"
+category = "main"
+optional = false
+python-versions = ">=3.8"
+
+[package.dependencies]
+contourpy = ">=1.0.1"
+cycler = ">=0.10"
+fonttools = ">=4.22.0"
+importlib-resources = {version = ">=3.2.0", markers = "python_version < \"3.10\""}
+kiwisolver = ">=1.0.1"
+numpy = ">=1.20"
+packaging = ">=20.0"
+pillow = ">=6.2.0"
+pyparsing = ">=2.3.1"
+python-dateutil = ">=2.7"
+setuptools_scm = ">=7"
+
+[[package]]
+name = "numpy"
+version = "1.24.2"
+description = "Fundamental package for array computing in Python"
+category = "main"
+optional = false
+python-versions = ">=3.8"
+
+[[package]]
+name = "openpyxl"
+version = "3.1.2"
+description = "A Python library to read/write Excel 2010 xlsx/xlsm files"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+et-xmlfile = "*"
+
+[[package]]
+name = "packaging"
+version = "23.1"
+description = "Core utilities for Python packages"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "pandas"
+version = "2.0.0"
+description = "Powerful data structures for data analysis, time series, and statistics"
+category = "main"
+optional = false
+python-versions = ">=3.8"
+
+[package.dependencies]
+numpy = [
+ {version = ">=1.20.3", markers = "python_version < \"3.10\""},
+ {version = ">=1.21.0", markers = "python_version >= \"3.10\""},
+ {version = ">=1.23.2", markers = "python_version >= \"3.11\""},
+]
+python-dateutil = ">=2.8.2"
+pytz = ">=2020.1"
+tzdata = ">=2022.1"
+
+[package.extras]
+all = ["beautifulsoup4 (>=4.9.3)", "bottleneck (>=1.3.2)", "brotlipy (>=0.7.0)", "fastparquet (>=0.6.3)", "fsspec (>=2021.07.0)", "gcsfs (>=2021.07.0)", "html5lib (>=1.1)", "hypothesis (>=6.34.2)", "jinja2 (>=3.0.0)", "lxml (>=4.6.3)", "matplotlib (>=3.6.1)", "numba (>=0.53.1)", "numexpr (>=2.7.3)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pandas-gbq (>=0.15.0)", "psycopg2 (>=2.8.6)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "PyQt5 (>=5.15.1)", "pyreadstat (>=1.1.2)", "pytest (>=7.0.0)", "pytest-xdist (>=2.2.0)", "pytest-asyncio (>=0.17.0)", "python-snappy (>=0.6.0)", "pyxlsb (>=1.0.8)", "qtpy (>=2.2.0)", "scipy (>=1.7.1)", "s3fs (>=2021.08.0)", "SQLAlchemy (>=1.4.16)", "tables (>=3.6.1)", "tabulate (>=0.8.9)", "xarray (>=0.21.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)", "zstandard (>=0.15.2)"]
+aws = ["s3fs (>=2021.08.0)"]
+clipboard = ["PyQt5 (>=5.15.1)", "qtpy (>=2.2.0)"]
+compression = ["brotlipy (>=0.7.0)", "python-snappy (>=0.6.0)", "zstandard (>=0.15.2)"]
+computation = ["scipy (>=1.7.1)", "xarray (>=0.21.0)"]
+excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pyxlsb (>=1.0.8)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)"]
+feather = ["pyarrow (>=7.0.0)"]
+fss = ["fsspec (>=2021.07.0)"]
+gcp = ["gcsfs (>=2021.07.0)", "pandas-gbq (>=0.15.0)"]
+hdf5 = ["tables (>=3.6.1)"]
+html = ["beautifulsoup4 (>=4.9.3)", "html5lib (>=1.1)", "lxml (>=4.6.3)"]
+mysql = ["SQLAlchemy (>=1.4.16)", "pymysql (>=1.0.2)"]
+output_formatting = ["jinja2 (>=3.0.0)", "tabulate (>=0.8.9)"]
+parquet = ["pyarrow (>=7.0.0)"]
+performance = ["bottleneck (>=1.3.2)", "numba (>=0.53.1)", "numexpr (>=2.7.1)"]
+plot = ["matplotlib (>=3.6.1)"]
+postgresql = ["SQLAlchemy (>=1.4.16)", "psycopg2 (>=2.8.6)"]
+spss = ["pyreadstat (>=1.1.2)"]
+sql-other = ["SQLAlchemy (>=1.4.16)"]
+test = ["hypothesis (>=6.34.2)", "pytest (>=7.0.0)", "pytest-xdist (>=2.2.0)", "pytest-asyncio (>=0.17.0)"]
+xml = ["lxml (>=4.6.3)"]
+
+[[package]]
+name = "pefile"
+version = "2023.2.7"
+description = "Python PE parsing module"
+category = "main"
+optional = false
+python-versions = ">=3.6.0"
+
+[[package]]
+name = "pillow"
+version = "9.5.0"
+description = "Python Imaging Library (Fork)"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"]
+tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
+
+[[package]]
+name = "pyinstaller"
+version = "5.11.0"
+description = "PyInstaller bundles a Python application and all its dependencies into a single package."
+category = "main"
+optional = false
+python-versions = "<3.12,>=3.7"
+
+[package.dependencies]
+altgraph = "*"
+macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""}
+pefile = {version = ">=2022.5.30", markers = "sys_platform == \"win32\""}
+pyinstaller-hooks-contrib = ">=2021.4"
+pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""}
+
+[package.extras]
+encryption = ["tinyaes (>=1.0.0)"]
+hook_testing = ["pytest (>=2.7.3)", "execnet (>=1.5.0)", "psutil"]
+
+[[package]]
+name = "pyinstaller-hooks-contrib"
+version = "2023.3"
+description = "Community maintained hooks for PyInstaller"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "pyparsing"
+version = "3.0.9"
+description = "pyparsing module - Classes and methods to define and execute parsing grammars"
+category = "main"
+optional = false
+python-versions = ">=3.6.8"
+
+[package.extras]
+diagrams = ["railroad-diagrams", "jinja2"]
+
+[[package]]
+name = "python-dateutil"
+version = "2.8.2"
+description = "Extensions to the standard Python datetime module"
+category = "main"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+
+[package.dependencies]
+six = ">=1.5"
+
+[[package]]
+name = "pytz"
+version = "2023.3"
+description = "World timezone definitions, modern and historical"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "pywin32-ctypes"
+version = "0.2.0"
+description = ""
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "setuptools-scm"
+version = "7.1.0"
+description = "the blessed package to manage your versions by scm tags"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+packaging = ">=20.0"
+tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
+typing-extensions = "*"
+
+[package.extras]
+test = ["pytest (>=6.2)", "virtualenv (>20)"]
+toml = ["setuptools (>=42)"]
+
+[[package]]
+name = "six"
+version = "1.16.0"
+description = "Python 2 and 3 compatibility utilities"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "tomli"
+version = "2.0.1"
+description = "A lil' TOML parser"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "typing-extensions"
+version = "4.5.0"
+description = "Backported and Experimental Type Hints for Python 3.7+"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "tzdata"
+version = "2023.3"
+description = "Provider of IANA time zone data"
+category = "main"
+optional = false
+python-versions = ">=2"
+
+[[package]]
+name = "xlrd"
+version = "2.0.1"
+description = "Library for developers to extract data from Microsoft Excel (tm) .xls spreadsheet files"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+
+[package.extras]
+build = ["wheel", "twine"]
+docs = ["sphinx"]
+test = ["pytest", "pytest-cov"]
+
+[[package]]
+name = "zipp"
+version = "3.15.0"
+description = "Backport of pathlib-compatible object wrapper for zip files"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+docs = ["sphinx (>=3.5)", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "furo", "sphinx-lint", "jaraco.tidelift (>=1.4)"]
+testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "flake8 (<5)", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco.itertools", "jaraco.functools", "more-itertools", "big-o", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "pytest-flake8"]
+
+[metadata]
+lock-version = "1.1"
+python-versions = ">=3.9,<3.12"
+content-hash = "31737a76d2d951406282db0245184cb876d50a65624ce7aa9bc4f307c3be8429"
+
+[metadata.files]
+altgraph = []
+contourpy = []
+cycler = []
+et-xmlfile = []
+fonttools = []
+importlib-resources = []
+kiwisolver = []
+macholib = []
+matplotlib = []
+numpy = []
+openpyxl = []
+packaging = []
+pandas = []
+pefile = []
+pillow = []
+pyinstaller = []
+pyinstaller-hooks-contrib = []
+pyparsing = []
+python-dateutil = []
+pytz = []
+pywin32-ctypes = []
+setuptools-scm = []
+six = []
+tomli = []
+typing-extensions = []
+tzdata = []
+xlrd = []
+zipp = []
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..b487a21
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,20 @@
+[tool.poetry]
+name = "gcpyramid"
+version = "0.1.0"
+description = ""
+authors = ["navanchauhan <navanchauhan@gmail.com>"]
+
+[tool.poetry.dependencies]
+python = ">=3.9,<3.12"
+pandas = "^2.0.0"
+xlrd = "^2.0.1"
+openpyxl = "^3.1.2"
+Pillow = "^9.5.0"
+matplotlib = "^3.7.1"
+pyinstaller = "^5.11.0"
+
+[tool.poetry.dev-dependencies]
+
+[build-system]
+requires = ["poetry-core>=1.0.0"]
+build-backend = "poetry.core.masonry.api"