Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

various small things #4

Merged
merged 10 commits into from
Oct 28, 2023
Merged
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
43 changes: 43 additions & 0 deletions .github/workflows/CompatHelper.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: CompatHelper
on:
schedule:
- cron: 0 0 * * *
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
CompatHelper:
runs-on: ubuntu-latest
steps:
- name: Check if Julia is already available in the PATH
id: julia_in_path
run: which julia
continue-on-error: true
- name: Install Julia, but only if it is not already available in the PATH
uses: julia-actions/setup-julia@v1
with:
version: '1'
arch: ${{ runner.arch }}
if: steps.julia_in_path.outcome != 'success'
- name: "Add the General registry via Git"
run: |
import Pkg
ENV["JULIA_PKG_SERVER"] = ""
Pkg.Registry.add("General")
shell: julia --color=yes {0}
- name: "Install CompatHelper"
run: |
import Pkg
name = "CompatHelper"
uuid = "aa819f21-2bde-4658-8897-bab36330d9b7"
version = "3"
Pkg.add(; name, uuid, version)
shell: julia --color=yes {0}
- name: "Run CompatHelper"
run: |
import CompatHelper
CompatHelper.main()
shell: julia --color=yes {0}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
15 changes: 15 additions & 0 deletions .github/workflows/TagBot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: TagBot
on:
issue_comment:
types:
- created
workflow_dispatch:
jobs:
TagBot:
if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot'
runs-on: ubuntu-latest
steps:
- uses: JuliaRegistries/TagBot@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
ssh: ${{ secrets.DOCUMENTER_KEY }}
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: CI

on:
push:
branches:
- master
pull_request:

jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
version:
- '1' # This is always the latest stable release in the 1.X series
#- '1.6' # LTS
#- 'nightly'
os:
- ubuntu-latest
#- macOS-latest
#- windows-latest
arch:
- x64
steps:
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@v1
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: julia-actions/cache@v1
- uses: julia-actions/julia-buildpkg@latest
- run: |
git config --global user.name Tester
git config --global user.email [email protected]
- uses: julia-actions/julia-runtest@latest
continue-on-error: ${{ matrix.version == 'nightly' }}
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v1
with:
file: lcov.info
62 changes: 62 additions & 0 deletions .github/workflows/pr-format.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
on:
pull_request:
issue_comment:
types: [created]

name: Formatting

jobs:
formatting:
if: github.event_name == 'pull_request' || (github.event_name == 'issue_comment' && github.event.issue.pull_request && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'COLLABORATOR' || github.event.comment.author_association == 'OWNER' || github.event.issue.user.id == github.event.comment.user.id) && startsWith(github.event.comment.body, '/format') )
runs-on: ubuntu-latest
steps:
- name: Clone the repository
uses: actions/checkout@v4
- name: Checkout the pull request code # this checks out the actual branch so that one can commit into it
if: github.event_name == 'issue_comment'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr checkout ${{ github.event.issue.number }}
- name: Install JuliaFormatter and format
run: |
julia --color=yes -e 'import Pkg; Pkg.add("JuliaFormatter")'
julia --color=yes -e 'using JuliaFormatter; format(".")'
- name: Remove trailing whitespace
run: |
find -name '*.jl' -or -name '*.md' -or -name '*.toml' -or -name '*.yml' | while read filename ; do
# remove any trailing spaces
sed --in-place -e 's/\s*$//' "$filename"
# add a final newline if missing
if [[ -s "$filename" && $(tail -c 1 "$filename" |wc -l) -eq 0 ]] ; then
echo >> "$filename"
fi
# squash superfluous final newlines
sed -i -e :a -e '/^\n*$/{$d;N;};/\n$/ba' "$filename"
done
- name: Fail on formatting problems
if: github.event_name == 'pull_request'
run: |
if git diff --exit-code --quiet
then echo "Looks OK"
else echo "Formatting fixes required!"; git diff -p ; exit 1
fi
- name: Commit fixes
if: github.event_name == 'issue_comment'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if [ `git status -s | wc -l` -ne 0 ] ; then
git config --local user.name "$GITHUB_ACTOR"
git config --local user.email "[email protected]"
git commit -a -m "automatic formatting" -m "triggered by @$GITHUB_ACTOR on PR #${{ github.event.issue.number }}"
if git push
then gh pr comment ${{ github.event.issue.number }} --body \
":heavy_check_mark: Auto-formatting triggered by [this comment](${{ github.event.comment.html_url }}) succeeded, commited as `git rev-parse HEAD`"
else gh pr comment ${{ github.event.issue.number }} --body \
":x: Auto-formatting triggered by [this comment](${{ github.event.comment.html_url }}) failed, perhaps someone pushed to the PR in the meantime?"
fi
else
gh pr comment ${{ github.event.issue.number }} --body \
":sunny: Auto-formatting triggered by [this comment](${{ github.event.comment.html_url }}) succeeded, but the code was already formatted correctly."
fi
13 changes: 8 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
/.vscode
/vscode
*.code-workspace
/docs/build/
Manifest.toml

downloaded/*
test/downloaded/*
*.cov
.*.swp

.vscode
*.code-workspace

/test/test-models
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ ReadableRegex = "cbbcb084-453d-4c4c-b292-e315607ba6a4"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

[compat]
AbstractFBCModels = "0.2"
DocStringExtensions = "0.9"
AbstractFBCModels = "0.1, 0.2"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will have lots of breaking on AFBCMs 0.1

DocStringExtensions = "0.8, 0.9"
JSON = "0.21"

[extras]
Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,19 @@ The primary purpose of this is to provide JSON loading functionality for
[FBCModelTests.jl](https://github.com/LCSB-BioCore/FBCModelTests.jl), but is
otherwise completely generic and can be used independently of these packages.

You should be able to load JSON models (called `JSONFBCModel`) via the
You should be able to load JSON models (of type `JSONFBCModel`) via the
AbstractFBCModels interface:

```julia
import AbstractFBCModels as A
import AbstractFBCModels as M
import JSONFBCModels

model = A.load("my_model.json")
A.reactions(model) # accessors defined by the interface (NB: access through the overloads: A.xyz)
model = M.load("my_model.json")
```
See the docstrings of all the accessors available by entering `A.accessors()` in
the REPL. For example:

Documentation of
[AbstractFBCModels.jl](https://github.com/COBREXA/AbstractFBCModels.jl)
provides details on the use of the loaded model.

#### Acknowledgements

Expand Down
20 changes: 8 additions & 12 deletions src/interface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,28 +47,28 @@ JSONFBCModel(json::Dict{String,Any}) = begin

JSONFBCModel(
json,
Dict(_json_rxn_name(r, i) => i for (i, r) in enumerate(rs)),
Dict(extract_json_reaction_id(r, i) => i for (i, r) in enumerate(rs)),
rs,
Dict(_json_met_name(m, i) => i for (i, m) in enumerate(ms)),
Dict(extract_json_metabolite_id(m, i) => i for (i, m) in enumerate(ms)),
ms,
Dict(_json_gene_name(g, i) => i for (i, g) in enumerate(gs)),
Dict(extract_json_gene_id(g, i) => i for (i, g) in enumerate(gs)),
gs,
)
end

# model
A.reactions(model::JSONFBCModel) =
String[_json_rxn_name(r, i) for (i, r) in enumerate(model.reactions)]
String[extract_json_reaction_id(r, i) for (i, r) in enumerate(model.reactions)]

A.n_reactions(model::JSONFBCModel) = length(model.reactions)

A.metabolites(model::JSONFBCModel) =
String[_json_met_name(m, i) for (i, m) in enumerate(model.metabolites)]
String[extract_json_metabolite_id(m, i) for (i, m) in enumerate(model.metabolites)]

A.n_metabolites(model::JSONFBCModel) = length(model.metabolites)

A.genes(model::JSONFBCModel) =
String[_json_gene_name(g, i) for (i, g) in enumerate(model.genes)]
String[extract_json_gene_id(g, i) for (i, g) in enumerate(model.genes)]

A.n_genes(model::JSONFBCModel) = length(model.genes)

Expand Down Expand Up @@ -169,16 +169,12 @@ A.metabolite_name(model::JSONFBCModel, mid::String) =
A.gene_name(model::JSONFBCModel, gid::String) =
parse_name(get(model.genes[model.gene_index[gid]], "name", nothing))

A.reaction_stoichiometry(model::JSONFBCModel, rid::String) =
A.reaction_stoichiometry(model::JSONFBCModel, rid::String)::Dict{String,Float64} =
model.reactions[model.reaction_index[rid]]["metabolites"]

function A.metabolite_compartment(model::JSONFBCModel, mid::String)
x = get(model.metabolites[model.metabolite_index[mid]], "compartment", nothing)
if isa(x, String)
return x
else
return nothing
end
return isa(x, String) ? x : nothing
end

function Base.convert(::Type{JSONFBCModel}, mm::A.AbstractFBCModel)
Expand Down
22 changes: 12 additions & 10 deletions src/utils.jl
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@

_json_rxn_name(r, i) = string(get(r, "id", "rxn$i"))
extract_json_reaction_id(r, i) = string(get(r, "id", "rxn$i"))

_json_met_name(m, i) = string(get(m, "id", "met$i"))
extract_json_metabolite_id(m, i) = string(get(m, "id", "met$i"))

_json_gene_name(g, i) = string(get(g, "id", "gene$i"))
extract_json_gene_id(g, i) = string(get(g, "id", "gene$i"))

function parse_grr(str::Maybe{String})
isnothing(str) && return nothing
isempty(str) && return nothing

dnf = A.GeneAssociationDNF()
for isozyme in string.(split(str, " or "))
push!(
Expand All @@ -31,15 +31,17 @@ function parse_formula(x::Maybe{String})
return res
end

function parse_charge(x)
function parse_charge(x)::Maybe{Int}
if isa(x, Int)
return x
x
elseif isa(x, Float64)
return Int(x)::Int
Int(x)
elseif isa(x, String)
return parse(Int, x)
else
Int(parse(Float64, x))
elseif isnothing(x)
nothing
else
throw(DomainError(x, "cannot parse charge"))
end
end

Expand All @@ -53,7 +55,7 @@ function parse_annotations_or_notes(x)
if isa(vs, String)
a_or_n[k] = String[vs]
else
a_or_n[k] = String[v for v in vs]
a_or_n[k] = String[v for v in vs]
end
end
return a_or_n
Expand Down
26 changes: 0 additions & 26 deletions test/io.jl

This file was deleted.

25 changes: 25 additions & 0 deletions test/misc.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

@testset "Miscellaneous tests" begin
# the path is inherited from the main testset
model = A.load(JSONFBCModel, joinpath(@__DIR__, "test-models", "iML1515.json"))
# test that no superfluous keys are generated
@test all(
key in ["metabolites", "id", "compartments", "reactions", "version", "genes"] for
key in keys(model.json)
)

# caps suffix is quite common
@test all(suffix in A.filename_extensions(JSONFBCModel) for suffix in ["json", "JSON"])
end

@testset "Corner cases" begin
import JSONFBCModels: parse_charge

@test parse_charge(1) == 1
@test parse_charge(2.0) == 2
@test parse_charge("3") == 3
@test parse_charge("4.0") == 4
@test parse_charge(nothing) == nothing
@test_throws ArgumentError parse_charge("totally positive charge")
@test_throws DomainError parse_charge(["very charged"])
end
Loading
Loading