Troubleshooting pyinstaller.
This commit is contained in:
18
README.md
18
README.md
@@ -112,14 +112,14 @@ This is meant to import .xslx files created from the Design & Analysis Software
|
|||||||
|
|
||||||
## SETUP:
|
## SETUP:
|
||||||
|
|
||||||
## Download:
|
## Download and Setup:
|
||||||
*Python v3.11 or greater must be installed on your system for this.*
|
*Python v3.11 or greater must be installed on your system for this.*
|
||||||
|
|
||||||
1. Clone or download from github.
|
1. Clone or download from github.
|
||||||
2. Enter the downloaded folder.
|
2. Enter the downloaded folder.
|
||||||
3. Open a terminal in the folder with the 'src' folder.
|
3. Open a terminal in the folder with the 'src' folder.
|
||||||
4. Create a new virtual environment: ```python -m venv venv```
|
4. Create a new virtual environment: ```python -m venv .venv```
|
||||||
5. Activate the virtual environment: (Windows) ```venv\Scripts\activate.bat```
|
5. Activate the virtual environment: (Windows) ```.venv\Scripts\activate.bat```
|
||||||
6. Install dependencies: ```pip install -r requirements.txt```
|
6. Install dependencies: ```pip install -r requirements.txt```
|
||||||
|
|
||||||
## Database:
|
## Database:
|
||||||
@@ -133,6 +133,14 @@ This is meant to import .xslx files created from the Design & Analysis Software
|
|||||||
## First Run:
|
## First Run:
|
||||||
|
|
||||||
1. On first run, the application copies src/config.yml to C:\Users\{USERNAME}\Local\submissions\config
|
1. On first run, the application copies src/config.yml to C:\Users\{USERNAME}\Local\submissions\config
|
||||||
2. Initially, the 'directory_path' variable is set to the 'sqlalchemy.url' variable in alembic.ini
|
2. If this folder cannot be found, C:\Users\{USERNAME}\Documents\submissions will be used.
|
||||||
3. If this folder cannot be found, C:\Users\{USERNAME}\Documents\submissions will be used.
|
|
||||||
1. If using Postgres, the 'database_path' and other variables will have to be updated manually.
|
1. If using Postgres, the 'database_path' and other variables will have to be updated manually.
|
||||||
|
3. Initially, the config variables are set parsing the 'sqlalchemy.url' variable in alembic.ini
|
||||||
|
|
||||||
|
## Building Portable Application:
|
||||||
|
*Download and Setup must have been performed beforehand*
|
||||||
|
|
||||||
|
1. Using pyinstaller, an exe can be created.
|
||||||
|
2. Open a terminal in the folder with the 'src' folder.
|
||||||
|
3. Activate the virtual environment: (Windows) ```.venv\Scripts\activate.bat```
|
||||||
|
4. Enter the following command: ```pyinstaller .\submissions.spec --noconfirm```
|
||||||
47
docs/source/conf.py
Normal file
47
docs/source/conf.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# Configuration file for the Sphinx documentation builder.
|
||||||
|
#
|
||||||
|
# For the full list of built-in configuration values, see the documentation:
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||||
|
|
||||||
|
# -- Project information -----------------------------------------------------
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
sys.path.append(Path(__file__).parents[2].joinpath('src').absolute().__str__())
|
||||||
|
from submissions import __version__, __copyright__, __author__
|
||||||
|
|
||||||
|
project = 'RSL Submissions'
|
||||||
|
copyright = __copyright__
|
||||||
|
author = f"{__author__['name']} - {__author__['email']}"
|
||||||
|
release = __version__
|
||||||
|
|
||||||
|
# -- General configuration ---------------------------------------------------
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||||
|
|
||||||
|
master_doc = "index"
|
||||||
|
|
||||||
|
extensions = [
|
||||||
|
'sphinx.ext.doctest',
|
||||||
|
'sphinx.ext.autodoc',
|
||||||
|
'sphinx.ext.autosummary',
|
||||||
|
'sphinx.ext.napoleon',
|
||||||
|
'sphinx_markdown_builder',
|
||||||
|
'sphinx_mdinclude',
|
||||||
|
]
|
||||||
|
|
||||||
|
templates_path = ['_templates']
|
||||||
|
exclude_patterns = []
|
||||||
|
|
||||||
|
sys.path.insert(0, Path(__file__).absolute().resolve().parents[2].joinpath("src").__str__())
|
||||||
|
sys.path.insert(0, Path(__file__).absolute().resolve().parents[2].joinpath("src/submissions").__str__())
|
||||||
|
|
||||||
|
# -- Options for HTML output -------------------------------------------------
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||||
|
|
||||||
|
html_theme = 'alabaster'
|
||||||
|
# html_style = 'custom.css'
|
||||||
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
|
||||||
|
# autodoc_mock_imports = ["backend.db.models.submissions"]
|
||||||
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
@@ -1,5 +1,6 @@
|
|||||||
import sys, os
|
import sys, os
|
||||||
from tools import ctx, setup_logger, check_if_app
|
from tools import ctx, setup_logger, check_if_app
|
||||||
|
|
||||||
# environment variable must be set to enable qtwebengine in network path
|
# environment variable must be set to enable qtwebengine in network path
|
||||||
if check_if_app():
|
if check_if_app():
|
||||||
os.environ['QTWEBENGINE_DISABLE_SANDBOX'] = "1"
|
os.environ['QTWEBENGINE_DISABLE_SANDBOX'] = "1"
|
||||||
|
|||||||
@@ -188,7 +188,10 @@ class App(QMainWindow):
|
|||||||
current_month_bak = current_month_bak.with_suffix(".db")
|
current_month_bak = current_month_bak.with_suffix(".db")
|
||||||
if not current_month_bak.exists() and "demo" not in self.ctx.database_path.__str__():
|
if not current_month_bak.exists() and "demo" not in self.ctx.database_path.__str__():
|
||||||
logger.info("No backup found for this month, backing up database.")
|
logger.info("No backup found for this month, backing up database.")
|
||||||
shutil.copyfile(self.ctx.database_path, current_month_bak)
|
try:
|
||||||
|
shutil.copyfile(self.ctx.backup_path, current_month_bak)
|
||||||
|
except PermissionError as e:
|
||||||
|
logger.error(f"Couldn't backup database due to: {e}")
|
||||||
case "postgresql+psycopg2":
|
case "postgresql+psycopg2":
|
||||||
logger.warning(f"Backup function not yet implemented for psql")
|
logger.warning(f"Backup function not yet implemented for psql")
|
||||||
current_month_bak = current_month_bak.with_suffix(".psql")
|
current_month_bak = current_month_bak.with_suffix(".psql")
|
||||||
|
|||||||
@@ -85,7 +85,10 @@ class ControlsViewer(QWidget):
|
|||||||
self.mode = self.mode_typer.currentText()
|
self.mode = self.mode_typer.currentText()
|
||||||
self.sub_typer.clear()
|
self.sub_typer.clear()
|
||||||
# NOTE: lookup subtypes
|
# NOTE: lookup subtypes
|
||||||
|
try:
|
||||||
sub_types = ControlType.query(name=self.con_type).get_subtypes(mode=self.mode)
|
sub_types = ControlType.query(name=self.con_type).get_subtypes(mode=self.mode)
|
||||||
|
except AttributeError:
|
||||||
|
sub_types = []
|
||||||
if sub_types != []:
|
if sub_types != []:
|
||||||
# NOTE: block signal that will rerun controls getter and update sub_typer
|
# NOTE: block signal that will rerun controls getter and update sub_typer
|
||||||
with QSignalBlocker(self.sub_typer) as blocker:
|
with QSignalBlocker(self.sub_typer) as blocker:
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
import pprint
|
import pprint
|
||||||
import weakref
|
|
||||||
from json import JSONDecodeError
|
from json import JSONDecodeError
|
||||||
import jinja2
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import logging, re, yaml, sys, os, stat, platform, getpass, inspect, csv
|
import logging, re, yaml, sys, os, stat, platform, getpass, inspect, csv
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
@@ -15,7 +13,7 @@ from jinja2 import Environment, FileSystemLoader
|
|||||||
from logging import handlers
|
from logging import handlers
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from sqlalchemy import create_engine, text
|
from sqlalchemy import create_engine, text, MetaData
|
||||||
from pydantic import field_validator, BaseModel, Field
|
from pydantic import field_validator, BaseModel, Field
|
||||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
from typing import Any, Tuple, Literal, List
|
from typing import Any, Tuple, Literal, List
|
||||||
@@ -302,11 +300,11 @@ class Settings(BaseSettings, extra="allow"):
|
|||||||
check = value.exists()
|
check = value.exists()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
check = False
|
check = False
|
||||||
if not check: #and values.data['database_schema'] == "sqlite":
|
if not check: # and values.data['database_schema'] == "sqlite":
|
||||||
# print(f"No directory found, using Documents/submissions")
|
# print(f"No directory found, using Documents/submissions")
|
||||||
# value = Path.home().joinpath("Documents", "submissions")
|
# value = Path.home().joinpath("Documents", "submissions")
|
||||||
value.mkdir(exist_ok=True)
|
value.mkdir(exist_ok=True)
|
||||||
print(f"Final return of directory_path: {value}")
|
# print(f"Final return of directory_path: {value}")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@field_validator('database_path', mode="before")
|
@field_validator('database_path', mode="before")
|
||||||
@@ -366,7 +364,7 @@ class Settings(BaseSettings, extra="allow"):
|
|||||||
alembic_path = project_path.joinpath("alembic.ini")
|
alembic_path = project_path.joinpath("alembic.ini")
|
||||||
# print(f"Getting alembic path: {alembic_path}")
|
# print(f"Getting alembic path: {alembic_path}")
|
||||||
value = cls.get_alembic_db_path(alembic_path=alembic_path, mode='user')
|
value = cls.get_alembic_db_path(alembic_path=alembic_path, mode='user')
|
||||||
print(f"Got {value} for user")
|
# print(f"Got {value} for user")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@field_validator("database_password", mode='before')
|
@field_validator("database_password", mode='before')
|
||||||
@@ -379,10 +377,9 @@ class Settings(BaseSettings, extra="allow"):
|
|||||||
alembic_path = project_path.joinpath("alembic.ini")
|
alembic_path = project_path.joinpath("alembic.ini")
|
||||||
# print(f"Getting alembic path: {alembic_path}")
|
# print(f"Getting alembic path: {alembic_path}")
|
||||||
value = cls.get_alembic_db_path(alembic_path=alembic_path, mode='pass')
|
value = cls.get_alembic_db_path(alembic_path=alembic_path, mode='pass')
|
||||||
print(f"Got {value} for pass")
|
# print(f"Got {value} for pass")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
@field_validator('database_session', mode="before")
|
@field_validator('database_session', mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_database_session(cls, value, values):
|
def create_database_session(cls, value, values):
|
||||||
@@ -395,8 +392,9 @@ class Settings(BaseSettings, extra="allow"):
|
|||||||
value = f"/{values.data['database_path']}"
|
value = f"/{values.data['database_path']}"
|
||||||
db_name = f"{values.data['database_name']}.db"
|
db_name = f"{values.data['database_name']}.db"
|
||||||
case _:
|
case _:
|
||||||
print(pprint.pprint(values.data))
|
# print(pprint.pprint(values.data))
|
||||||
tmp = jinja_template_loading().from_string("{% if values['database_user'] %}{{ values['database_user'] }}{% if values['database_password'] %}:{{ values['database_password'] }}{% endif %}{% endif %}@{{ values['database_path'] }}")
|
tmp = jinja_template_loading().from_string(
|
||||||
|
"{% if values['database_user'] %}{{ values['database_user'] }}{% if values['database_password'] %}:{{ values['database_password'] }}{% endif %}{% endif %}@{{ values['database_path'] }}")
|
||||||
value = tmp.render(values=values.data)
|
value = tmp.render(values=values.data)
|
||||||
db_name = values.data['database_name']
|
db_name = values.data['database_name']
|
||||||
template = jinja_template_loading().from_string(
|
template = jinja_template_loading().from_string(
|
||||||
@@ -422,7 +420,7 @@ class Settings(BaseSettings, extra="allow"):
|
|||||||
# database_path = database_path
|
# database_path = database_path
|
||||||
# else:
|
# else:
|
||||||
# raise FileNotFoundError("No database file found. Exiting program.")
|
# raise FileNotFoundError("No database file found. Exiting program.")
|
||||||
print(f"Using {database_path} for database file.")
|
# print(f"Using {database_path} for database file.")
|
||||||
# engine = create_engine(f"sqlite:///{database_path}") #, echo=True, future=True)
|
# engine = create_engine(f"sqlite:///{database_path}") #, echo=True, future=True)
|
||||||
# engine = create_engine("postgresql+psycopg2://postgres:RE,4321q@localhost:5432/submissions")
|
# engine = create_engine("postgresql+psycopg2://postgres:RE,4321q@localhost:5432/submissions")
|
||||||
engine = create_engine(database_path)
|
engine = create_engine(database_path)
|
||||||
@@ -439,15 +437,22 @@ class Settings(BaseSettings, extra="allow"):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.set_from_db(db_path=kwargs['database_path'])
|
self.set_from_db()
|
||||||
|
|
||||||
def set_from_db(self, db_path: Path):
|
def set_from_db(self):
|
||||||
if 'pytest' in sys.modules:
|
if 'pytest' in sys.modules:
|
||||||
output = dict(power_users=['lwark', 'styson', 'ruwang'])
|
output = dict(power_users=['lwark', 'styson', 'ruwang'])
|
||||||
else:
|
else:
|
||||||
# session = Session(create_engine(f"sqlite:///{db_path}"))
|
# session = Session(create_engine(f"sqlite:///{db_path}"))
|
||||||
logger.debug(self.__dict__)
|
logger.debug(self.__dict__)
|
||||||
session = self.database_session
|
session = self.database_session
|
||||||
|
metadata = MetaData()
|
||||||
|
try:
|
||||||
|
tables = metadata.reflect(bind=session.get_bind()).tables.keys()
|
||||||
|
except AttributeError:
|
||||||
|
return
|
||||||
|
if "_configitem" not in tables:
|
||||||
|
return
|
||||||
config_items = session.execute(text("SELECT * FROM _configitem")).all()
|
config_items = session.execute(text("SELECT * FROM _configitem")).all()
|
||||||
session.close()
|
session.close()
|
||||||
# print(config_items)
|
# print(config_items)
|
||||||
@@ -480,14 +485,14 @@ class Settings(BaseSettings, extra="allow"):
|
|||||||
try:
|
try:
|
||||||
return url[:url.index("@")].split(":")[0]
|
return url[:url.index("@")].split(":")[0]
|
||||||
except (IndexError, ValueError) as e:
|
except (IndexError, ValueError) as e:
|
||||||
print(f"Error on user: {e}")
|
# print(f"Error on user: {e}")
|
||||||
return None
|
return None
|
||||||
case "pass":
|
case "pass":
|
||||||
url = re.sub(r"^.*//", "", url)
|
url = re.sub(r"^.*//", "", url)
|
||||||
try:
|
try:
|
||||||
return url[:url.index("@")].split(":")[1]
|
return url[:url.index("@")].split(":")[1]
|
||||||
except (IndexError, ValueError) as e:
|
except (IndexError, ValueError) as e:
|
||||||
print(f"Error on user: {e}")
|
# print(f"Error on user: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def save(self, settings_path: Path):
|
def save(self, settings_path: Path):
|
||||||
@@ -498,18 +503,18 @@ class Settings(BaseSettings, extra="allow"):
|
|||||||
continue
|
continue
|
||||||
match v:
|
match v:
|
||||||
case Path():
|
case Path():
|
||||||
print("Path")
|
# print("Path")
|
||||||
if v.is_dir():
|
if v.is_dir():
|
||||||
print("dir")
|
# print("dir")
|
||||||
v = v.absolute().__str__()
|
v = v.absolute().__str__()
|
||||||
elif v.is_file():
|
elif v.is_file():
|
||||||
print("file")
|
# print("file")
|
||||||
v = v.parent.absolute().__str__()
|
v = v.parent.absolute().__str__()
|
||||||
else:
|
else:
|
||||||
v = v.__str__()
|
v = v.__str__()
|
||||||
case _:
|
case _:
|
||||||
pass
|
pass
|
||||||
print(f"Key: {k}, Value: {v}")
|
# print(f"Key: {k}, Value: {v}")
|
||||||
dicto[k] = v
|
dicto[k] = v
|
||||||
with open(settings_path, 'w') as f:
|
with open(settings_path, 'w') as f:
|
||||||
yaml.dump(dicto, f)
|
yaml.dump(dicto, f)
|
||||||
@@ -570,7 +575,7 @@ def get_config(settings_path: Path | str | None = None) -> Settings:
|
|||||||
# settings = Settings(**copy_settings(settings_path=CONFIGDIR.joinpath("config.yml"), settings=default_settings))
|
# settings = Settings(**copy_settings(settings_path=CONFIGDIR.joinpath("config.yml"), settings=default_settings))
|
||||||
settings = Settings(**default_settings)
|
settings = Settings(**default_settings)
|
||||||
settings.save(settings_path=CONFIGDIR.joinpath("config.yml"))
|
settings.save(settings_path=CONFIGDIR.joinpath("config.yml"))
|
||||||
print(f"Default settings: {pprint.pprint(settings.__dict__)}")
|
# print(f"Default settings: {pprint.pprint(settings.__dict__)}")
|
||||||
return settings
|
return settings
|
||||||
else:
|
else:
|
||||||
# NOTE: check if user defined path is directory
|
# NOTE: check if user defined path is directory
|
||||||
@@ -758,7 +763,7 @@ def jinja_template_loading() -> Environment:
|
|||||||
if check_if_app():
|
if check_if_app():
|
||||||
loader_path = Path(sys._MEIPASS).joinpath("files", "templates")
|
loader_path = Path(sys._MEIPASS).joinpath("files", "templates")
|
||||||
else:
|
else:
|
||||||
loader_path = Path(__file__).parent.joinpath('templates').absolute() #.__str__()
|
loader_path = Path(__file__).parents[1].joinpath('templates').absolute() # .__str__()
|
||||||
# NOTE: jinja template loading
|
# NOTE: jinja template loading
|
||||||
loader = FileSystemLoader(loader_path)
|
loader = FileSystemLoader(loader_path)
|
||||||
env = Environment(loader=loader)
|
env = Environment(loader=loader)
|
||||||
@@ -7,14 +7,16 @@ import sys, subprocess
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
sys.path.append(Path(".").parent.joinpath('src').absolute().__str__())
|
sys.path.append(Path(".").parent.joinpath('src').absolute().__str__())
|
||||||
from submissions import __version__, __project__, bcolors, project_path
|
from submissions import __version__, __project__, bcolors, project_path
|
||||||
|
print(f"Using {project_path} as project path.")
|
||||||
doc_path = project_path.joinpath("docs").absolute()
|
doc_path = project_path.joinpath("docs").absolute()
|
||||||
build_path = project_path.joinpath(".venv", "Scripts", "sphinx-build").absolute().__str__()
|
build_path = project_path.joinpath(".venv", "Scripts", "sphinx-build").absolute().__str__()
|
||||||
print(bcolors.BOLD + "Running Sphinx subprocess to generate rst files..." + bcolors.ENDC)
|
print(bcolors.BOLD + "Running Sphinx subprocess to generate rst files..." + bcolors.ENDC)
|
||||||
api_path = project_path.joinpath(".venv", "Scripts", "sphinx-apidoc").absolute().__str__()
|
api_path = project_path.joinpath(".venv", "Scripts", "sphinx-apidoc").absolute().__str__()
|
||||||
subprocess.run([api_path, "-o", doc_path.joinpath("source").__str__(), project_path.joinpath("src", "submissions").__str__(), "-f"])
|
subprocess.run([api_path, "-o", doc_path.joinpath("source").__str__(), project_path.joinpath("src", "submissions").__str__(), "-f"])
|
||||||
print(bcolors.BOLD + "Running Sphinx subprocess to generate html docs..." + bcolors.ENDC)
|
print(bcolors.BOLD + "Running Sphinx subprocess to generate html docs..." + bcolors.ENDC)
|
||||||
subprocess.run([build_path, doc_path.joinpath("source").__str__(), doc_path.joinpath("build").__str__(), "-a"])
|
docs_build = doc_path.joinpath("build")
|
||||||
|
#docs_build.mkdir(exist_ok=True, parents=True)
|
||||||
|
subprocess.run([build_path, doc_path.joinpath("source").__str__(), docs_build.__str__(), "-a"])
|
||||||
#########################################################
|
#########################################################
|
||||||
|
|
||||||
a = Analysis(
|
a = Analysis(
|
||||||
|
|||||||
Reference in New Issue
Block a user