Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 90 additions & 60 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ on:
release:
types: [created]
workflow_dispatch:
inputs:
dry_run:
description: "Dry run: build and test everything, but skip publishing to PyPI, GHCR, and GitHub releases"
type: boolean
default: true

jobs:
build-python-package:
Expand All @@ -12,13 +17,13 @@ jobs:
contents: read

steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- uses: actions/setup-python@v5
- uses: actions/setup-python@v6
with:
python-version-file: "pyproject.toml"

- uses: astral-sh/setup-uv@v7
- uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true

Expand Down Expand Up @@ -46,62 +51,87 @@ jobs:
path: dist/

- name: Publish to PyPI
if: github.event_name == 'release' || inputs.dry_run == 'false'
uses: pypa/gh-action-pypi-publish@release/v1

# windows-installer:
# runs-on: windows-latest
# needs: python-package
# steps:
# - uses: actions/checkout@v6
# - uses: actions/setup-python@v5
# with:
# python-version: '3.11'
# - name: Install dependencies
# run: |
# python -m pip install --upgrade pip
# pip install .[gui] pyinstaller
# - name: Install Inno Setup
# uses: crazy-max/ghaction-chocolatey@v3
# with:
# args: install innosetup -y --allow-unofficial --force
# - name: Run pyinstaller
# run: pyinstaller ./deeplc_pyinstaller.spec --clean --noconfirm
# - name: Test built DeepLC exe
# run: dist/deeplc/deeplc.exe --ignore-gooey --help
# - name: Run Inno Setup
# run: ISCC.exe ./deeplc_innosetup.iss /DAppVersion=${{ github.ref_name }}
# - name: Upload installer
# uses: actions/upload-artifact@v4
# with:
# name: windows-installer
# path: dist/*.exe
# - name: Upload installer to release
# uses: svenstaro/upload-release-action@v2
# with:
# repo_token: ${{ secrets.GITHUB_TOKEN }}
# tag: ${{ github.ref }}
# file_glob: true
# file: dist/*.exe

# build-streamlit-image:
# runs-on: ubuntu-latest
# needs: publish-python-package

# steps:
# - uses: actions/checkout@v6

# - name: Login to GitHub Container Registry
# uses: docker/login-action@v3
# with:
# registry: ghcr.io
# username: ${{ github.repository_owner }}
# password: ${{ secrets.GITHUB_TOKEN }}

# - name: Build and push to ghcr.io
# uses: docker/build-push-action@v5
# with:
# context: streamlit
# push: true
# tags: |
# ghcr.io/compomics/deeplc-streamlit:${{ github.ref }}
# ghcr.io/compomics/deeplc-streamlit:latest
windows-installer:
runs-on: windows-latest
needs: build-python-package
steps:
- uses: actions/checkout@v7

- uses: actions/setup-python@v6
with:
python-version: "3.13"

- uses: astral-sh/setup-uv@v8
with:
enable-cache: true

- name: Install dependencies
run: uv pip install --system ".[gui]" pyinstaller

- name: Install Inno Setup
uses: crazy-max/ghaction-chocolatey@v3
with:
args: install innosetup -y --allow-unofficial --force

- name: Run PyInstaller
run: pyinstaller ./deeplc.spec --clean --noconfirm

- name: Test built executable
run: dist/deeplc/deeplc.exe predict --help

- name: Run Inno Setup
run: ISCC.exe ./deeplc_innosetup.iss /DAppVersion=${{ github.ref_name }}

- name: Upload installer artifact
uses: actions/upload-artifact@v7
with:
name: windows-installer
path: dist/*.exe

- name: Upload installer to release
if: github.event_name == 'release' || inputs.dry_run == 'false'
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref }}
file_glob: true
file: dist/*.exe

build-docker-image:
runs-on: ubuntu-latest
needs: publish-python-package
permissions:
packages: write

steps:
- uses: actions/checkout@v7

- name: Download wheel artifact
uses: actions/download-artifact@v8
with:
name: python-package-distributions
path: docker/dist/

- name: Login to GitHub Container Registry
if: github.event_name == 'release' || inputs.dry_run == 'false'
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push to ghcr.io
uses: docker/build-push-action@v7
with:
context: docker
push: ${{ github.event_name == 'release' || inputs.dry_run == 'false' }}
tags: |
ghcr.io/compomics/deeplc:${{ github.ref_name }}
ghcr.io/compomics/deeplc:latest
- name: Test Docker image
if: github.event_name == 'release' || inputs.dry_run == 'false'
run: docker run --rm ghcr.io/compomics/deeplc:${{ github.ref_name }} deeplc --help
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
with:
python-version: ${{ matrix.python-version }}

- uses: astral-sh/setup-uv@v7
- uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true

Expand Down
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# Local
*.ipynb
examples/datasets/*_predictions.csv

# IDE
.vscode/
.claude/

# DeepLC
data/
./*.png
Expand Down
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- NiceGUI-based web interface, launchable as a browser app (`deeplc gui`) or native desktop window (`deeplc gui --native`)
- `[gui]` optional dependency group (nicegui, plotly, pywebview) for desktop use
- `[web]` optional dependency group (nicegui, plotly) for server/Docker use
- Docker image for containerized web server deployment
- Updated Windows installer (PyInstaller and Inno Setup) to new GUI
- `predict_and_calibrate()` core function combining prediction and calibration in one call, with optional automatic reference PSM selection
- `finetune_and_predict()` core function for transfer learning followed by calibrated prediction
- Automatic calibration reference selection from input PSMs using q-value filtering or top-scoring fraction
- Publish workflow with Windows installer build, Docker image build, and dry-run mode for CI testing without publishing

### Changed

- CLI restructured into `predict` and `gui` subcommands
- Example datasets updated from legacy CSV format to psm_utils TSV and peprec formats

## [4.0.0-alpha.1]

### Changed
Expand Down
113 changes: 26 additions & 87 deletions deeplc.spec
Original file line number Diff line number Diff line change
@@ -1,132 +1,71 @@
# -*- mode: python ; coding: utf-8 -*-


import os
import sys
from PyInstaller.building.build_main import Analysis, PYZ, EXE, COLLECT, BUNDLE, TOC
import PyInstaller.utils.hooks
import importlib.metadata

import PyInstaller.utils.hooks

exe_name = 'deeplc'
script_name = 'deeplc/__main__.py'
#if sys.platform[:6] == "darwin":
# icon = 'deeplc_logo.icns'
#else:
# icon = 'deeplc_logo.ico'
block_cipher = None
location = os.getcwd()
project = "deeplc"
remove_tests = True
bundle_name = "deeplc"
#bundle_identifier = f"{bundle_name}.{deeplc.__version__}"


requirements = {
req.split()[0] for req in importlib.metadata.requires(project)
# Collect all dependencies recursively
requirements = {req.split()[0] for req in importlib.metadata.requires("deeplc")}
requirements |= {
"deeplc", "sklearn", "sklearn.utils", "sklearn.neighbors", "sklearn.tree",
"distributed", "nicegui", "plotly", "pywebview", "onnx2torch",
}
requirements.add(project)
requirements.add("sklearn")
requirements.add("sklearn.utils")
requirements.add("sklearn.neighbors")
requirements.add("sklearn.tree")
requirements.add("distributed")

hidden_imports = set()
datas = []
binaries = []
checked = set()
while requirements:
requirement = requirements.pop()
checked.add(requirement)
if requirement in ["pywin32"]:
if requirement in {"pywin32"}:
continue
try:
module_version = importlib.metadata.version(requirement)
except (
importlib.metadata.PackageNotFoundError,
ModuleNotFoundError,
ImportError
):
importlib.metadata.version(requirement)
except (importlib.metadata.PackageNotFoundError, ModuleNotFoundError, ImportError):
continue
try:
datas_, binaries_, hidden_imports_ = PyInstaller.utils.hooks.collect_all(
requirement,
include_py_files=True
datas_, _, hidden_imports_ = PyInstaller.utils.hooks.collect_all(
requirement, include_py_files=True
)
except ImportError:
continue
datas += datas_
# binaries += binaries_
hidden_imports_ = set(hidden_imports_)
if "" in hidden_imports_:
hidden_imports_.remove("")
if None in hidden_imports_:
hidden_imports_.remove(None)
hidden_imports_.discard("")
hidden_imports_.discard(None)
requirements |= hidden_imports_ - checked
hidden_imports |= hidden_imports_

if remove_tests:
hidden_imports = sorted(
[h for h in hidden_imports if "tests" not in h.split(".")]
)
else:
hidden_imports = sorted(hidden_imports)


hidden_imports = [h for h in hidden_imports if "__pycache__" not in h]

datas = [d for d in datas if ("__pycache__" not in d[0]) and (d[1] not in [".", "build","dist","Output"])]

block_cipher = None

hidden_imports = sorted(
h for h in hidden_imports if "tests" not in h.split(".") and "__pycache__" not in h
)
datas = [
d for d in datas
if "__pycache__" not in d[0] and d[1] not in {".", "build", "dist", "Output"}
]

a = Analysis(
[script_name],
pathex=[location],
binaries=binaries,
["deeplc/__main__.py"],
datas=datas,
hiddenimports=hidden_imports,
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
)
pyz = PYZ(a.pure, a.zipped_data)

exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='deeplc',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True,
disable_windowed_traceback=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None
name="deeplc",
console=False,
icon="img/deeplc.ico",
)

coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name=exe_name
name="deeplc",
)
Loading
Loading