Browse Source

adjusted the mbp_build script to the new mbp workbook groups

master
Holger Frey 3 weeks ago
parent
commit
bb1dbabd50
  1. 409
      work_helpers/sg_mbp_build.py

409
work_helpers/sg_mbp_build.py

@ -1,195 +1,236 @@
import click import click
import shutil import pathlib
import pyperclip import pyperclip
import shutil
import sys
from pathlib import Path from typing import Iterable
from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from ._natural_sort import natural_sort Pathlike = str | pathlib.Path
DEVELOPER_DRIVE = Path("/mnt/e/") PATH_WIN_DESKTOP = pathlib.Path("/mnt/c/Users/Holgi/Desktop")
PATH_ISSUES = DEVELOPER_DRIVE / "Safeguard-MBP-issues"
PATH_WORKBOOKS = DEVELOPER_DRIVE / "Safeguard MBP Workbooks"
PATH_WIN_DESKTOP = Path("/mnt/c/Users/Holgi/Desktop")
TODAY = datetime.now().strftime("%y%m%d") TODAY = datetime.now().strftime("%y%m%d")
CRLF = "\r\n" CRLF = "\r\n"
EXCEL_CHANGELOGS = { GROUPS = {
"changes mbp {version} asqc.txt": "L1", "QC": ["ASQC, HQC, MQC"],
"changes mbp {version} dry-1.txt": "L1", "Print": ["Dry-1, Dry-2"],
"changes mbp {version} dry-2.txt": "L1", "Production": ["Hyb", "Reg"],
"changes mbp {version} hqc.txt": "J1",
"changes mbp {version} hyb.txt": "J1",
"changes mbp {version} mqc.txt": "J1",
"changes mbp {version} reg.txt": "J1",
} }
GROUP_FOLDER_PREFIX = "Safeguard-MBP-"
GROUP_FOLDER_SUFFIX = "-Changes"
EXCEL_CHANGELOG_HEADERS = [ EXCEL_CHANGELOG_HEADERS = [
"Sheet\tWell\tContents\tComment", "Sheet\tWell\tContents\tComment",
"-----\t----\t--------\t-------", "-----\t----\t--------\t-------",
"", "",
] ]
WORKBOOKS_MAP = { EXCEL_CHANGELOGS = {
"MBP ASQC.xlsx": "MBP {version} ASQC.xlsx", "QC": {
"MBP Dry-1.xlsx": "MBP {version} Dry-1.xlsx", "changes mbp qc asqc {version}.txt": "L1",
"MBP Dry-2.xlsx": "MBP {version} Dry-2.xlsx", "changes mbp qc hqc {version}.txt": "J1",
"MBP HQC.xlsx": "MBP {version} HQC.xlsx", "changes mbp qc mqc {version}.txt": "J1",
"MBP Hyb.xlsx": "MBP {version} Hyb.xlsx", },
"MBP MQC.xlsx": "MBP {version} MQC.xlsx", "Print": {
"MBP Reg.xlsx": "MBP {version} Reg.xlsx", "changes mbp print dry-1 {version}.txt": "L1",
"changes mbp print dry-2 {version}.txt": "L1",
},
"Production": {
"changes mbp production hyb {version}.txt": "J1",
"changes mbp production reg {version}.txt": "J1",
},
} }
def _folder_content(folder): ## Exception classes
class MBPExcecption(Exception):
pass
# common functions and helper functions
def _to_int(text: str, default=0) -> int:
try:
return int(text)
except (ValueError, TypeError):
return default
def _folder_content(folder: Pathlike) -> Iterable[pathlib.Path]:
folder = pathlib.Path(folder)
nondotted = (i for i in folder.iterdir() if not i.stem.startswith(".")) nondotted = (i for i in folder.iterdir() if not i.stem.startswith("."))
return (i for i in nondotted if not i.stem.startswith("~")) return (i for i in nondotted if not i.stem.startswith("~"))
def _files_in_folder(folder, suffix): def _files_in_folder(folder: Pathlike, suffix: str) -> Iterable[pathlib.Path]:
folder = pathlib.Path(folder)
files = (f for f in _folder_content(folder) if f.is_file()) files = (f for f in _folder_content(folder) if f.is_file())
return (f for f in files if f.suffix == suffix) return (f for f in files if f.suffix == suffix)
def get_latest_version(parent=PATH_ISSUES): def _get_workbook_folder(group: str) -> pathlib.Path:
folders = (i for i in _folder_content(parent) if i.is_dir()) path = pathlib.Path("/") / "mnt" / "e" / f"Safeguard MBP {group} Workbooks"
versions = natural_sort(f.name for f in folders if f.stem.lower().startswith("v")) if not path.is_dir():
return versions[-1] msg = "Workbook folder {path} does not exist"
raise MBPExcecption(msg)
return path
def get_next_version(parent=PATH_ISSUES, echo_current=True): def _get_changelog_path(folder: Pathlike) -> pathlib.Path:
latest = get_latest_version(parent) textfiles = _files_in_folder(folder, ".txt")
if echo_current: return next(f for f in textfiles if f.stem.lower().startswith("change"))
print("current version:", latest)
try:
head, tail = latest.rsplit(".", 1)
next_minor = int(tail) + 1
next_version = f"{head}.{next_minor}"
except:
next_version = ""
return next_version def _list_current_frms(group: str, build_version: str):
source = _get_workbook_folder(group)
search_version = build_version.removesuffix(TODAY)
all_folders = (f for f in source.iterdir() if f.is_dir())
all_frms = (f for f in all_folders if "frm" in f.name.lower())
return [f for f in all_frms if search_version in f.name]
def create_new_version_folder(new_version, parent=PATH_ISSUES): def _get_issue_numbers(group: str, build_version: str):
new_folder_path = parent / new_version print(list(_list_current_frms(group, build_version)))
if new_folder_path.exists(): for path in _list_current_frms(group, build_version):
print(f"Folder for version {new_version} already exists, aborting") rest, issue_info = path.name.lower().split("issue")
return issue_info = issue_info.removeprefix("s") # might be "issues"
new_folder_path.mkdir() issue, *rest = issue_info.strip().split()
return new_folder_path yield issue.strip(" ,")
def create_excel_changelogs(new_version, parent): def _extract_changes_from_log(
for name, cell in EXCEL_CHANGELOGS.items(): cwd: Pathlike, group: str, build_version: str
new_file = parent / name.format(version=new_version) ) -> Iterable[str]:
with new_file.open("w") as fh: issue_numbers = set(_get_issue_numbers(group, build_version))
data_line = "\t".join(["Settings", cell, new_version, ""]) changelog = _get_changelog_path(cwd)
content_lines = EXCEL_CHANGELOG_HEADERS + [data_line, "", ""] for line in changelog.read_text().splitlines():
fh.write(CRLF.join(content_lines)) for issue in issue_numbers:
if issue in line:
yield line
def create_changelog_entry(new_version, parent=PATH_ISSUES): def _get_group_from_folder(folder: Pathlike) -> str:
textfiles = _files_in_folder(parent, ".txt") name = pathlib.Path(folder).name
changelog = next(f for f in textfiles if f.stem.lower().startswith("change")) middle = name.removeprefix(GROUP_FOLDER_PREFIX).removesuffix(GROUP_FOLDER_SUFFIX)
content = [] if middle in GROUPS:
with changelog.open("r") as fh: return middle
stripped_lines = (line.rstrip() for line in fh) msg = f"Folder '{name}' is not an MBP group folder"
for line in stripped_lines: raise MBPExcecption(msg)
content.append(line)
if line.startswith("----"):
content.append("")
content.append(f"{new_version}, work in progress:")
content.append(" - The following Workbooks did not have any changes: ASQC, Dry-1, Dry-2, Hyb, HQC, MQC, Reg")
with changelog.open("w") as fh:
fh.write(CRLF.join(content))
def get_changelog_path():
textfiles = _files_in_folder(PATH_ISSUES, ".txt")
return next(f for f in textfiles if f.stem.lower().startswith("change"))
def copy_changelog(destination, build_version): def _get_latest_version(folder: Pathlike) -> "Version":
changelog = get_changelog_path() dir_names = [i.name for i in pathlib.Path(folder).iterdir() if i.is_dir()]
new_path = destination / f"CHANGELOG {build_version}.txt" version_names = [name for name in dir_names if name.startswith("v")]
print(changelog.name, "->", new_path) versions = [Version.from_name(name) for name in version_names]
shutil.copyfile(changelog, new_path) sorted_versions = sorted(versions, key=lambda x: x.to_tuple())
return sorted_versions[-1]
def copy_workbook_changelogs(destination, latest, build_version): ## data classes
source = PATH_ISSUES / latest
textfiles = _files_in_folder(source, ".txt")
logs = (f for f in textfiles if f.stem.lower().startswith("change")) @dataclass
for log_file in logs: class Version:
new_name = log_file.name.replace(latest, build_version) major: int
new_path = destination / new_name layout: int
print(log_file.name, "->", new_path) minor: int
shutil.copyfile(log_file, new_path)
@classmethod
def from_name(cls, name: str) -> "Version":
parts = name.removeprefix("v").split(".") + [None, None]
args = tuple([_to_int(part) for part in parts[:3]])
return cls(*args)
def to_tuple(self) -> tuple[int, int, int]:
return (self.major, self.layout, self.minor)
def bump(self) -> None:
cls = type(self)
return cls(self.major, self.layout, self.minor + 1)
def __str__(self) -> str:
return f"v{self.major}.{self.layout}.{self.minor}"
## functions for `sg_mbp_build`
def copy_workbooks(destination, build_version):
all_xls_files = _files_in_folder(PATH_WORKBOOKS, ".xlsx") def copy_workbooks(group: str, destination: Pathlike, build_version: str) -> None:
source = _get_workbook_folder(group)
all_xls_files = _files_in_folder(source, ".xlsx")
mbp_files = (f for f in all_xls_files if f.name.lower().startswith("mbp")) mbp_files = (f for f in all_xls_files if f.name.lower().startswith("mbp"))
for excel_file in mbp_files: for excel_file in mbp_files:
new_name = WORKBOOKS_MAP[excel_file.name] new_name = f"{excel_file.stem} {build_version}{excel_file.suffix}"
new_path = destination / new_name.format(version=build_version) new_path = destination / new_name.format(version=build_version)
print(excel_file.name, "->", new_path) print(excel_file.name, "->", new_path)
shutil.copyfile(excel_file, new_path) shutil.copyfile(excel_file, new_path)
def collect_current_frms(build_version): def copy_frms(group: str, destination: Pathlike, build_version: str) -> None:
all_folders = (f for f in PATH_WORKBOOKS.iterdir() if f.is_dir()) current_frms = _list_current_frms(group, build_version)
all_frms = (f for f in all_folders if "frm" in f.name.lower())
return [f for f in all_frms if f.name.endswith(build_version)]
def copy_frms(destination, build_version):
all_folders = (f for f in PATH_WORKBOOKS.iterdir() if f.is_dir())
all_frms = (f for f in all_folders if "frm" in f.name.lower())
current_frms = collect_current_frms(build_version)
for folder in current_frms: for folder in current_frms:
new_path = destination / folder.name new_path = destination / folder.name
print(folder.name, "->", new_path) print(folder.name, "->", new_path)
shutil.copytree(folder, new_path) shutil.copytree(folder, new_path)
def get_issue_numbers(build_version): def copy_workbook_changelogs(
for path in collect_current_frms(build_version): cwd: Pathlike, destination: Pathlike, latest: Version, build_version: str
rest, issue_info = path.name.lower().split("issue") ) -> None:
issue, *rest = issue_info.strip().split() source = pathlib.Path(cwd) / str(latest)
yield issue.strip(" ,") textfiles = _files_in_folder(source, ".txt")
logs = (f for f in textfiles if f.stem.lower().startswith("change"))
for log_file in logs:
new_name = log_file.name.replace(str(latest), build_version)
new_path = destination / new_name
print(log_file.name, "->", new_path)
shutil.copyfile(log_file, new_path)
def extract_changes_from_log(build_version):
issue_numbers = set(get_issue_numbers(build_version))
changelog = get_changelog_path()
for line in changelog.read_text().splitlines():
for issue in issue_numbers:
if issue in line:
yield line
def copy_changelog(cwd: Pathlike, destination: Pathlike, build_version: str) -> None:
changelog = _get_changelog_path(cwd)
new_path = pathlib.Path(destination) / f"CHANGELOG {build_version}.txt"
print(changelog.name, "->", new_path)
shutil.copyfile(changelog, new_path)
def get_announcement_text(dev_version, build_version, new_folder_name):
if not dev_version:
return "This is an official release, the message must be hand crafted"
latest = get_latest_version() def get_announcement_text(
changes = list(extract_changes_from_log(build_version)) cwd: Pathlike,
group: str,
latest: Version,
build_version: str,
new_folder_name: Pathlike,
) -> str:
changes = list(_extract_changes_from_log(cwd, group, build_version))
if len(changes) == 1: if len(changes) == 1:
change_msg = "Only one change was introduced:" change_msg = "Only one change was introduced:"
else: else:
change_msg = "The changes made:" change_msg = "The changes made:"
if not build_version.endswith(TODAY):
dev_version = build_version.split(".")[-1]
version_note = (
f"As indicated by the letter '{dev_version}' at the end,"
" this version is intended for Freiburg only."
)
else:
version_note = "This is an official release version."
text = [ text = [
f"# New MBP Workbook Version {build_version}", f"# New MBP Workbook Version {build_version}",
"Good News Everyone,", "Good News Everyone,",
f"there is a new MBP workbook version available: {build_version}", f"there is a new MBP workbook version available: {build_version}",
( version_note,
f"As indicated by the letter '{dev_version}' at the end,"
" this version is intended for Freiburg only."
),
change_msg, change_msg,
"\n".join(changes), "\n".join(changes),
"You can find this version at our Freiburg Shared Drive:", "You can find this version at our Freiburg Shared Drive:",
@ -198,67 +239,127 @@ def get_announcement_text(dev_version, build_version, new_folder_name):
f" {latest} /" f" {latest} /"
f" {new_folder_name}" f" {new_folder_name}"
), ),
"Cheers,\nHolgi" "Cheers,\nHolgi",
] ]
return "\n\n".join(text) return "\n\n".join(text)
@click.command()
@click.option(
"-v",
"--version",
required=True,
prompt="new version",
default=get_next_version,
show_default="next minor version",
)
def sg_mbp_new_version(version):
"""
creates a new version folder, new excel changes files and modifies the overall changelog
in "E:\Safeguard-MBP-issues"
"""
folder = create_new_version_folder(version)
if folder is not None:
create_excel_changelogs(version, folder)
create_changelog_entry(version)
@click.command() @click.command()
@click.option( @click.option(
"-d", "-d",
"--dev_version", "--dev_version",
prompt="Dev version i.e. 'c'", prompt="Dev version i.e. 'c'",
required=True, required=True,
default="", default=TODAY,
) )
def sg_mbp_build(dev_version): def sg_mbp_build(dev_version):
""" """
Before running this command: Before running this command:
\b \b
- create a new versions folder e.g. "v4.9.2"
- make the requiered edits to the workbooks - make the requiered edits to the workbooks
- edit the changelog in "E:\Safeguard-MBP-issues"
- create a new versions folder in "E:\Safeguard-MBP-issues", e.g. "v3.9.49"
- note the changes in the excel changelogs in the created version folder - note the changes in the excel changelogs in the created version folder
- edit the group changelog
The command will collect all data into one folder on the Desktop to be published The command will collect all data into one folder on the Desktop to be published
""" """
latest = get_latest_version() try:
build_version = f"{latest}{dev_version}" cwd = pathlib.Path.cwd()
group = _get_group_from_folder(cwd)
latest = _get_latest_version(cwd)
build_version = f"{latest}.{dev_version}"
new_folder_name = f"{TODAY} {build_version}" new_folder_name = f"{TODAY} MBP {group} {build_version}"
new_folder_path = PATH_WIN_DESKTOP / new_folder_name new_folder_path = PATH_WIN_DESKTOP / new_folder_name
if new_folder_path.exists(): if new_folder_path.exists():
raise IOError(f"Folder exists on desktop: {new_folder_name}") raise MBPExcecption(f"Folder exists on desktop: {new_folder_name}")
else: else:
new_folder_path.mkdir() new_folder_path.mkdir()
copy_workbooks(new_folder_path, build_version) copy_workbooks(group, new_folder_path, build_version)
copy_workbook_changelogs(new_folder_path, latest, build_version) copy_frms(group, new_folder_path, build_version)
copy_changelog(new_folder_path, build_version) copy_workbook_changelogs(cwd, new_folder_path, latest, build_version)
copy_frms(new_folder_path, build_version) copy_changelog(cwd, new_folder_path, build_version)
announcement = get_announcement_text(dev_version, build_version, new_folder_name) announcement = get_announcement_text(
cwd, group, latest, build_version, new_folder_name
)
pyperclip.copy(announcement) pyperclip.copy(announcement)
print(announcement) print(announcement)
except MBPExcecption as e:
sys.exit(str(e))
## functions for `sg_mbp_new_version`
def get_next_version() -> Version:
try:
cwd = pathlib.Path.cwd()
_get_group_from_folder(cwd) # may raise an exception
latest = _get_latest_version(cwd)
return latest.bump()
except MBPExcecption as e:
sys.exit(str(e))
def create_new_version_folder(cwd: Pathlike, new_version: str) -> pathlib.Path:
new_folder_path = pathlib.Path(cwd) / new_version
if new_folder_path.exists():
msg = f"Folder for version {new_version} already exists"
raise MBPExcecption(msg)
new_folder_path.mkdir()
return new_folder_path
def create_excel_changelogs(folder: Pathlike, new_version: str) -> None:
folder = pathlib.Path(folder)
group = _get_group_from_folder(folder.parent)
for name, cell in EXCEL_CHANGELOGS[group].items():
new_file = folder / name.format(version=new_version)
with new_file.open("w") as fh:
data_line = "\t".join(["Settings", cell, new_version, ""])
content_lines = EXCEL_CHANGELOG_HEADERS + [data_line, "", ""]
fh.write(CRLF.join(content_lines))
def create_changelog_entry(cwd: Pathlike, new_version: str) -> None:
group = _get_group_from_folder(cwd)
workbooks = ", ".join(GROUPS[group])
changelog = _get_changelog_path(cwd)
content = []
with changelog.open("r") as fh:
stripped_lines = (line.rstrip() for line in fh)
for line in stripped_lines:
content.append(line)
if line.startswith("----"):
content.append("")
content.append(f"{new_version}, work in progress:")
content.append(
f" - The following Workbooks did not have any changes: {workbooks}"
)
with changelog.open("w") as fh:
fh.write(CRLF.join(content))
@click.command()
@click.option(
"-v",
"--version",
required=True,
prompt="new version",
default=get_next_version,
show_default="next minor version",
)
def sg_mbp_new_version(version):
"""
creates a new version folder, new excel changes files and modifies the overall changelog
in "E:\Safeguard-MBP-issues"
"""
cwd = pathlib.Path.cwd()
folder = create_new_version_folder(cwd, version)
create_excel_changelogs(folder, version)
create_changelog_entry(cwd, version)

Loading…
Cancel
Save