From b85aada7bf37b70845e6acfc93aaf53defcd527e Mon Sep 17 00:00:00 2001 From: JoFrhwld Date: Sat, 18 May 2024 12:18:50 -0400 Subject: [PATCH] docs --- .github/workflows/build-docs.yml | 45 ++++ docs/.gitignore | 5 + .../interlinks/.gitignore | 3 + .../interlinks/_extension.yml | 7 + .../interlinks/interlinks.lua | 254 ++++++++++++++++++ .../jofrhwld/codeblocklabel/_extension.yml | 8 + .../codeblocklabel/codeblocklabel.css | 10 + .../codeblocklabel/codeblocklabel.lua | 30 +++ docs/_quarto.yml | 132 +++++++++ docs/assets/logo.png | Bin 0 -> 29422 bytes docs/index.qmd | 15 ++ docs/objects.py | 18 ++ docs/styles/dark.scss | 10 + docs/styles/light.scss | 10 + docs/styles/styles.css | 5 + docs/usage/heuristic.qmd | 79 ++++++ docs/workflow/build-docs.yml | 45 ++++ src/fave_measurement_point/heuristic.py | 25 +- src/fave_measurement_point/processor.py | 2 +- 19 files changed, 690 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/build-docs.yml create mode 100644 docs/.gitignore create mode 100644 docs/_extensions/Forced-Alignment-and-Vowel-Extraction/interlinks/.gitignore create mode 100644 docs/_extensions/Forced-Alignment-and-Vowel-Extraction/interlinks/_extension.yml create mode 100644 docs/_extensions/Forced-Alignment-and-Vowel-Extraction/interlinks/interlinks.lua create mode 100644 docs/_extensions/jofrhwld/codeblocklabel/_extension.yml create mode 100644 docs/_extensions/jofrhwld/codeblocklabel/codeblocklabel.css create mode 100644 docs/_extensions/jofrhwld/codeblocklabel/codeblocklabel.lua create mode 100644 docs/_quarto.yml create mode 100644 docs/assets/logo.png create mode 100644 docs/index.qmd create mode 100644 docs/objects.py create mode 100644 docs/styles/dark.scss create mode 100644 docs/styles/light.scss create mode 100644 docs/styles/styles.css create mode 100644 docs/usage/heuristic.qmd create mode 100644 docs/workflow/build-docs.yml diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml new file mode 100644 index 0000000..06a2103 --- /dev/null +++ b/.github/workflows/build-docs.yml @@ -0,0 +1,45 @@ +name: Build Docs + +on: + push: + branches: ["main", "dev"] + +jobs: + build-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v5 + id: setup-python + with: + python-version: "3.11" + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + virtualenvs-create: true + virtualenvs-in-project: true + installer-parallel: true + - name: Load cached venv + id: cached-poetry-dependencies + uses: actions/cache@v3 + with: + path: .venv + key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} + - name: Install dependencies + if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' + run: poetry install --with docs --no-interaction --no-root + - name: Install project + run: poetry install --with docs --no-interaction + - name: Run quartodoc build + run: | + cd docs + poetry run quartodoc build + poetry run python objects.py + poetry run quartodoc interlinks + - name: Setup quarto + uses: quarto-dev/quarto-actions/setup@v2 + - name: Render and publush to gh-pages + run: | + git config --global user.email "quarto-github-actions-publish@example.com" + git config --global user.name "Quarto GHA Workflow Runner" + poetry run quarto publish gh-pages docs --no-browser \ No newline at end of file diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..b05e321 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,5 @@ +/.quarto/ +_site/ +_inv/ +reference/ +objects.txt \ No newline at end of file diff --git a/docs/_extensions/Forced-Alignment-and-Vowel-Extraction/interlinks/.gitignore b/docs/_extensions/Forced-Alignment-and-Vowel-Extraction/interlinks/.gitignore new file mode 100644 index 0000000..5a1bf0b --- /dev/null +++ b/docs/_extensions/Forced-Alignment-and-Vowel-Extraction/interlinks/.gitignore @@ -0,0 +1,3 @@ +*.html +*.pdf +*_files/ diff --git a/docs/_extensions/Forced-Alignment-and-Vowel-Extraction/interlinks/_extension.yml b/docs/_extensions/Forced-Alignment-and-Vowel-Extraction/interlinks/_extension.yml new file mode 100644 index 0000000..c8a8121 --- /dev/null +++ b/docs/_extensions/Forced-Alignment-and-Vowel-Extraction/interlinks/_extension.yml @@ -0,0 +1,7 @@ +title: Interlinks +author: Michael Chow +version: 1.1.0 +quarto-required: ">=1.2.0" +contributes: + filters: + - interlinks.lua diff --git a/docs/_extensions/Forced-Alignment-and-Vowel-Extraction/interlinks/interlinks.lua b/docs/_extensions/Forced-Alignment-and-Vowel-Extraction/interlinks/interlinks.lua new file mode 100644 index 0000000..47aa61f --- /dev/null +++ b/docs/_extensions/Forced-Alignment-and-Vowel-Extraction/interlinks/interlinks.lua @@ -0,0 +1,254 @@ +local function read_inv_text(filename) + -- read file + local file = io.open(filename, "r") + if file == nil then + return nil + end + local str = file:read("a") + file:close() + + + local project = str:match("# Project: (%S+)") + local version = str:match("# Version: (%S+)") + + local data = {project = project, version = version, items = {}} + + local ptn_data = + "^" .. + "(.-)%s+" .. -- name + "([%S:]-):" .. -- domain + "([%S]+)%s+" .. -- role + "(%-?%d+)%s+" .. -- priority + "(%S*)%s+" .. -- uri + "(.-)\r?$" -- dispname + + + -- Iterate through each line in the file content + for line in str:gmatch("[^\r\n]+") do + if not line:match("^#") then + -- Match each line against the pattern + local name, domain, role, priority, uri, dispName = line:match(ptn_data) + + -- if name is nil, raise an error + if name == nil then + error("Error parsing line: " .. line) + end + + data.items[#data.items + 1] = { + name = name, + domain = domain, + role = role, + priority = priority, + uri = uri, + dispName = dispName + } + end + end + return data +end + +local function read_json(filename) + + local file = io.open(filename, "r") + if file == nil then + return nil + end + local str = file:read("a") + file:close() + + local decoded = quarto.json.decode(str) + return decoded +end + +local function read_inv_text_or_json(base_name) + local file = io.open(base_name .. ".txt", "r") + if file then + -- TODO: refactors so we don't just close the file immediately + io.close(file) + json = read_inv_text(base_name .. ".txt") + + else + json = read_json(base_name .. ".json") + end + + return json +end + +local inventory = {} + +local function lookup(search_object) + + local results = {} + for _, inv in ipairs(inventory) do + for _, item in ipairs(inv.items) do + -- e.g. :external+:::`` + if item.inv_name and item.inv_name ~= search_object.inv_name then + goto continue + end + + if item.name ~= search_object.name then + goto continue + end + + if search_object.role and item.role ~= search_object.role then + goto continue + end + + if search_object.domain and item.domain ~= search_object.domain then + goto continue + else + if search_object.domain or item.domain == "py" then + table.insert(results, item) + end + + goto continue + end + + ::continue:: + end + end + + if #results == 1 then + return results[1] + end + if #results > 1 then + quarto.log.warning("Found multiple matches for " .. search_object.name .. ", using the first match.") + return results[1] + end + if #results == 0 then + quarto.log.warning("Found no matches for object:\n", search_object) + end + + return nil +end + +local function mysplit (inputstr, sep) + if sep == nil then + sep = "%s" + end + local t={} + for str in string.gmatch(inputstr, "([^"..sep.."]+)") do + table.insert(t, str) + end + return t +end + +local function normalize_role(role) + if role == "func" then + return "function" + end + return role +end + +local function build_search_object(str) + local starts_with_colon = str:sub(1, 1) == ":" + local search = {} + if starts_with_colon then + local t = mysplit(str, ":") + if #t == 2 then + -- e.g. :py:func:`my_func` + search.role = normalize_role(t[1]) + search.name = t[2]:match("%%60(.*)%%60") + elseif #t == 3 then + -- e.g. :py:func:`my_func` + search.domain = t[1] + search.role = normalize_role(t[2]) + search.name = t[3]:match("%%60(.*)%%60") + elseif #t == 4 then + -- e.g. :ext+inv:py:func:`my_func` + search.external = true + + search.inv_name = t[1]:match("external%+(.*)") + search.domain = t[2] + search.role = normalize_role(t[3]) + search.name = t[4]:match("%%60(.*)%%60") + else + quarto.log.warning("couldn't parse this link: " .. str) + return {} + end + else + search.name = str:match("%%60(.*)%%60") + end + + if search.name == nil then + quarto.log.warning("couldn't parse this link: " .. str) + return {} + end + + if search.name:sub(1, 1) == "~" then + search.shortened = true + search.name = search.name:sub(2, -1) + end + return search +end + +local function report_broken_link(link, search_object, replacement) + -- TODO: how to unescape html elements like [? + return pandoc.Code(pandoc.utils.stringify(link.content)) +end + +function Link(link) + -- do not process regular links ---- + if not link.target:match("%%60") then + return link + end + + -- lookup item ---- + local search = build_search_object(link.target) + local item = lookup(search) + + -- determine replacement, used if no link text specified ---- + local original_text = pandoc.utils.stringify(link.content) + local replacement = search.name + if search.shortened then + local t = mysplit(search.name, ".") + replacement = t[#t] + end + + -- set link text ---- + if original_text == "" and replacement ~= nil then + link.content = pandoc.Code(replacement) + end + + -- report broken links ---- + if item == nil then + return report_broken_link(link, search) + end + link.target = item.uri:gsub("%$$", search.name) + + + return link +end + +local function fixup_json(json, prefix) + for _, item in ipairs(json.items) do + item.uri = prefix .. item.uri + end + table.insert(inventory, json) +end + +return { + { + Meta = function(meta) + local json + local prefix + if meta.interlinks and meta.interlinks.sources then + for k, v in pairs(meta.interlinks.sources) do + local base_name = quarto.project.offset .. "/_inv/" .. k .. "_objects" + json = read_inv_text_or_json(base_name) + prefix = pandoc.utils.stringify(v.url) + if json ~= nil then + fixup_json(json, prefix) + end + end + end + json = read_inv_text_or_json(quarto.project.offset .. "/objects") + if json ~= nil then + fixup_json(json, "/") + end + end + }, + { + Link = Link + } +} diff --git a/docs/_extensions/jofrhwld/codeblocklabel/_extension.yml b/docs/_extensions/jofrhwld/codeblocklabel/_extension.yml new file mode 100644 index 0000000..3c068e5 --- /dev/null +++ b/docs/_extensions/jofrhwld/codeblocklabel/_extension.yml @@ -0,0 +1,8 @@ +title: Codeblocklabel +author: Josef Fruehwald +version: 1.0.0 +quarto-required: ">=1.3.0" +contributes: + filters: + - codeblocklabel.lua + diff --git a/docs/_extensions/jofrhwld/codeblocklabel/codeblocklabel.css b/docs/_extensions/jofrhwld/codeblocklabel/codeblocklabel.css new file mode 100644 index 0000000..fdb38d0 --- /dev/null +++ b/docs/_extensions/jofrhwld/codeblocklabel/codeblocklabel.css @@ -0,0 +1,10 @@ +.langname { + margin-bottom: 0%; + padding-bottom: 0%; + font-style: italic; + font-size:smaller; +} + +.sourceCode[id]{ + margin-top: 0%; +} diff --git a/docs/_extensions/jofrhwld/codeblocklabel/codeblocklabel.lua b/docs/_extensions/jofrhwld/codeblocklabel/codeblocklabel.lua new file mode 100644 index 0000000..677e3a5 --- /dev/null +++ b/docs/_extensions/jofrhwld/codeblocklabel/codeblocklabel.lua @@ -0,0 +1,30 @@ + +-- function Div(el) +-- if el.content[1].t == "CodeBlock" then +-- return pandoc.Para("CodeBlock!") +-- end +-- end + +quarto.doc.add_html_dependency({ + name = 'codenamelabel', + stylesheets = {'codeblocklabel.css'} + }) + +function CodeBlock(block) + local newblock = block + if (FORMAT:match "html") and + (block.classes[1]) then + local langname = block.classes[1] + out = {pandoc.Div( + pandoc.RawInline("html", + "
"..block.classes[1].."
" + ), + pandoc.Attr("", {"langname"}, {}) + ), + newblock + } + else + out = newblock + end + return out +end \ No newline at end of file diff --git a/docs/_quarto.yml b/docs/_quarto.yml new file mode 100644 index 0000000..fc096c5 --- /dev/null +++ b/docs/_quarto.yml @@ -0,0 +1,132 @@ +project: + type: website + output-dir: _site + +website: + title: "fave-measurement-point" + favicon: assets/logo.png + image: assets/logo.png + page-navigation: true + navbar: + left: + - href: index.qmd + text: Home + - href: reference/ + text: Package reference + right: + - icon: github + href: https://github.com/Forced-Alignment-and-Vowel-Extraction/fave-measurement-point + sidebar: + - id: get-started + logo: assets/logo.png + title: Get Started + style: floating + align: left + contents: + - index.qmd + - auto: usage + # - usage/getting_started.qmd + # - usage/all_arguments.qmd + # - usage/pythonic_use.ipynb + +metadata-files: + - reference/_sidebar.yml + + + +format: + html: + theme: + light: [flatly, styles/light.scss] + dark: [darkly, styles/dark.scss] + css: styles/styles.css + toc: true + toc-depth: 4 + highlight-style: github + +filters: + - interlinks + - codeblocklabel +resources: + - objects.inv + +interlinks: + fast: true + sources: + numpy: + url: https://numpy.org/doc/stable/ + python: + url: https://docs.python.org/3/ + +quartodoc: + style: pkgdown + dir: reference + package: fave_measurement_point + sidebar: "reference/_sidebar.yml" + parser: google + render_interlinks: true + sections: + - title: Hueristics + desc: Classes for defining measurement point heuristics + contents: + - name: heuristic.Heuristic + - name: heuristic.Specific + - title: Formants + desc: Classes for representing formant tracks and points + contents: + - name: formants.Formant + - name: formants.FormantArray + - name: formants.Point + - name: formants.Slice + - title: Processors + desc: Helper functions for parsing landmark expressions + contents: + - processor + # sections: + # - title: Processing Patterns + # desc: Different patterns for processing data + # contents: + # - fave_audio_textgrid + # - fave_corpus + # - fave_subcorpora + # - title: Writers + # contents: + # - write_data + # - title: Built-in resources + # contents: + # - utils.local_resources.local_resources + # - title: Speaker files + # contents: + # - speaker.speaker.Speaker + # - title: Optimization + # desc: Functions for optimizing formant measurements + # contents: + # - optimize.optimize + # - title: Vowel Measurements + # desc: Vowel Measurements + # options: + # dynamic: true + # contents: + # - name: VowelMeasurement + # members: + # - to_tracks_df + # - to_param_df + # - to_point_df + + # - name: VowelClass + # members: + # - to_tracks_df + # - to_param_df + # - to_point_df + + # - name: VowelClassCollection + # members: + # - to_tracks_df + # - to_param_df + # - to_point_df + + # - name: SpeakerCollection + # members: + # - to_tracks_df + # - to_param_df + # - to_point_df \ No newline at end of file diff --git a/docs/assets/logo.png b/docs/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f7b5671b305377419e9be218fa38c6ea4bc1da47 GIT binary patch literal 29422 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 FV1kj(+`j+- literal 0 HcmV?d00001 diff --git a/docs/index.qmd b/docs/index.qmd new file mode 100644 index 0000000..72b3675 --- /dev/null +++ b/docs/index.qmd @@ -0,0 +1,15 @@ +--- +title: fave-measurement-point +date-modified: last-modified +date-format: YYYY-MM-DD HH:mm:ssZ +language: + title-block-modified: "Docs Last Built" +--- + +This is a package to easily define and access vowel formant measurement points. + +# Usage + +The way to define measurement point heuristics is documented here, +but this package is not inteded to be directly used. Rather +see the [new-fave](https://forced-alignment-and-vowel-extraction.github.io/new-fave/). \ No newline at end of file diff --git a/docs/objects.py b/docs/objects.py new file mode 100644 index 0000000..17e9e48 --- /dev/null +++ b/docs/objects.py @@ -0,0 +1,18 @@ +import new_fave +import sphobjinv as soi +from pathlib import Path +inv = soi.Inventory(fname_plain = Path("objects.txt")) + +for idx in range(len(inv.objects)): + obj = inv.objects[idx] + basename = obj.name.split(".")[-1] + if hasattr(new_fave, basename): + obj_dict = obj.json_dict() + if not "new_fave." + basename in obj_dict: + obj_dict["name"] = "aligned_textgrid." + basename + new_obj = soi.DataObjStr(**obj_dict) + inv.objects.append(new_obj) + +df = inv.data_file() +dfc = soi.compress(df) +soi.writebytes("objects.inv", dfc) \ No newline at end of file diff --git a/docs/styles/dark.scss b/docs/styles/dark.scss new file mode 100644 index 0000000..b1ce1d6 --- /dev/null +++ b/docs/styles/dark.scss @@ -0,0 +1,10 @@ +/*-- scss:defaults --*/ + +/*-- scss:rules --*/ + + + +/*-- scss:rules --*/ +.cell:not(.page-full):has(.cell-output){ + background-color: $gray-600; +} \ No newline at end of file diff --git a/docs/styles/light.scss b/docs/styles/light.scss new file mode 100644 index 0000000..a65ae62 --- /dev/null +++ b/docs/styles/light.scss @@ -0,0 +1,10 @@ +/*-- scss:defaults --*/ + +/*-- scss:rules --*/ + + + +/*-- scss:rules --*/ + .cell:not(.page-full):has(.cell-output){ + background-color: $gray-100; + } \ No newline at end of file diff --git a/docs/styles/styles.css b/docs/styles/styles.css new file mode 100644 index 0000000..c24e338 --- /dev/null +++ b/docs/styles/styles.css @@ -0,0 +1,5 @@ +.cell:not(.page-full):has(.cell-output){ + padding: 2%; + border-radius: 10px; + margin-bottom: 1em; + } \ No newline at end of file diff --git a/docs/usage/heuristic.qmd b/docs/usage/heuristic.qmd new file mode 100644 index 0000000..c4c3ada --- /dev/null +++ b/docs/usage/heuristic.qmd @@ -0,0 +1,79 @@ +--- +title: Defining measurement point heuristics +--- + +A heuristic file is a yaml file with the following format: + +```yaml +heuristic: default +default: + prop_time: "1/3" +specifics: [] +``` +- The `heuristic` field gives a name to the heuristic. +- The `default` field defines the default measurement point method. +- The `specifics` field defines specific rules for specific vowels. + + +The yaml file above, in fact, is the same as the default heuristic if none are provided. + + +## Specifics + +Let's say you wanted to define a special measurement point rule for just the vowel /ay/, to measure it at maximum F1. This can be done by adding the following rule to the `specifics` list. + +```yaml +heuristic: ay-rule +default: + prop_time: "1/3" +specifics: + - label: ay + prop_time: f1.max.prop_time +``` + +What this says is: + +- Apply a special measurement point rule when the interval label is "ay". +- Get the measurement point where the vowel's `prop_time` is equal to the + `prop_time` of F1 maximum. + + +## Valid point expressions + +The expression `f1.max.prop_time` defines the proportional time of F1 maximum. An entire point expression will always be of the format: + +``` +formant.anchor.time +``` + +Valid values for each slot are: + +- Formants + - `f1`, `f2`, `f3`, ... + - Any formant that's available +- Anchor + - `min` + - `max` +- Time + - `time` + - `rel_time` + - `prop_time` + +Additionally, any other mathematical expression can be included. For example, the original FAVE suite had a measurement point heuristic for /aw/ and /ow/ defined in the docs as: + +``` +# - OW, AW measured halfway between beginning of segment and F1 maximum ## +``` + +The heuristic file for this would look like: + +```yaml +heuristic: aw-ow-rule +default: + prop_time: "1/3" +specifics: + - label: aw + prop_time: f1.max.prop_time / 2 + - label: ow + prop_time: f1.max.prop_time / 2 +``` \ No newline at end of file diff --git a/docs/workflow/build-docs.yml b/docs/workflow/build-docs.yml new file mode 100644 index 0000000..06a2103 --- /dev/null +++ b/docs/workflow/build-docs.yml @@ -0,0 +1,45 @@ +name: Build Docs + +on: + push: + branches: ["main", "dev"] + +jobs: + build-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v5 + id: setup-python + with: + python-version: "3.11" + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + virtualenvs-create: true + virtualenvs-in-project: true + installer-parallel: true + - name: Load cached venv + id: cached-poetry-dependencies + uses: actions/cache@v3 + with: + path: .venv + key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} + - name: Install dependencies + if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' + run: poetry install --with docs --no-interaction --no-root + - name: Install project + run: poetry install --with docs --no-interaction + - name: Run quartodoc build + run: | + cd docs + poetry run quartodoc build + poetry run python objects.py + poetry run quartodoc interlinks + - name: Setup quarto + uses: quarto-dev/quarto-actions/setup@v2 + - name: Render and publush to gh-pages + run: | + git config --global user.email "quarto-github-actions-publish@example.com" + git config --global user.name "Quarto GHA Workflow Runner" + poetry run quarto publish gh-pages docs --no-browser \ No newline at end of file diff --git a/src/fave_measurement_point/heuristic.py b/src/fave_measurement_point/heuristic.py index 33f460e..98ddca1 100644 --- a/src/fave_measurement_point/heuristic.py +++ b/src/fave_measurement_point/heuristic.py @@ -98,20 +98,21 @@ def read_heuristic(self, heuristic_path: Path | str): class Specific(): - def __init__(self, specific: dict): - """A specific measurement point heuristic + """A specific measurement point heuristic - Args: - specific (dict): - A dictionary with a label and a - measurement point landmark + Args: + specific (dict): + A dictionary with a label and a + measurement point landmark + + Attributes: + label (str): + The label for the specific measurement point + kwarg (dict): + The kwargs to be passed to FormantArray.get_slice_at() + """ + def __init__(self, specific: dict): - Attributes: - label (str): - The label for the specific measurement pint - kwarg (dict): - The kwargs to be passed to FormantArray.get_slice_at() - """ self.label = specific["label"] self.kwarg = { k: specific[k] diff --git a/src/fave_measurement_point/processor.py b/src/fave_measurement_point/processor.py index 4251901..837b1b9 100644 --- a/src/fave_measurement_point/processor.py +++ b/src/fave_measurement_point/processor.py @@ -70,7 +70,7 @@ def inject_values( A landmark expression formants (FormantArray): The formant array from which to get - the numerc values + the numeric values Returns: (str): The numeric string