This commit is contained in:
chaos 2024-11-09 00:49:08 +00:00
parent d2da9e1af4
commit 235d7874e5
9 changed files with 437 additions and 382 deletions

2
run.sh
View file

@ -9,7 +9,7 @@ JQ=${JQ:-jq}
export JQ_FLAVOR=${JQ_FLAVOR:-${JQ}}
run() {
${JQ} -rn -L "$(realpath .)" -L "$(realpath lib)" -L "$(realpath dropins)/${JQ_FLAVOR}" \
${JQ} -nr -L "$(realpath .)" -L "$(realpath ./lib)" -L "$(realpath ./dropins)/${JQ_FLAVOR}" \
--slurpfile exportFile "${EXPORT_FILE:-export.json}" \
'include "main"; main' \
--args -- "$@"

203
tool/journalLib.jq Normal file
View file

@ -0,0 +1,203 @@
import "utils" as utils;
def calculateIngestionDose($customUnits):
. as $ingestion |
if .customUnitId != null then
($customUnits | map(select(.id == $ingestion.customUnitId))[0]) as $customUnit |
.dose * $customUnit.dose | . as $dose |
$dose * 100 | round / 100
else
.dose
end;
def ingestionUnit($customUnits):
. as $ingestion |
if .customUnitId != null then
($customUnits | map(select(.id == $ingestion.customUnitId))[0]) as $customUnit |
$customUnit.originalUnit
else
.units
end;
def ingestionConsumerName:
. as $ingestion |
$ingestion.consumerName // "default";
def filterIngestions($substanceFilter; $consumerFilter):
. as $ingestions |
if (($substanceFilter // [] | length) > 0) then
[
$ingestions[] as $ingestion |
if
([$substanceFilter[] | . == $ingestion.substanceName] | any)
and
([$consumerFilter[] | . == ($ingestion | ingestionConsumerName)] | any)
then $ingestion
else null end
] | map(select(. != null))
else $ingestions end;
def ingestionsSubstanceNames:
. as $ingestions |
[$ingestions[].substanceName] | utils::orderedUnique;
def ingestionsConsumerNames:
. as $ingestions |
[$ingestions[] | ingestionConsumerName] | utils::orderedUnique;
def ingestionsByConsumer:
. as $ingestions |
($ingestions | ingestionsConsumerNames) as $consumerNames |
[
$consumerNames[] as $consumerName |
{
key: $consumerName,
value: [$ingestions | map(select(
. | ingestionConsumerName == $consumerName
))],
}
] | from_entries;
def experienceStats($customUnits):
. as $experience |
$experience.ingestions as $ingestions |
(reduce $ingestions[] as $ingestion ({}; . as $stats |
$ingestion |
.substanceName as $name |
.administrationRoute as $administrationRoute |
. | calculateIngestionDose($customUnits) as $dose |
. | ingestionUnit($customUnits) as $unit |
. | ingestionConsumerName as $consumerName |
$stats | .[$consumerName].[$name].[$administrationRoute] |=
($stats.[$consumerName].[$name].[$administrationRoute] // {
unit: "",
# null because null+null = null for ingestions with unknown dose which .dose is null
dose: null
}) |
.[$consumerName].[$name].[$administrationRoute].unit |= $unit |
.[$consumerName].[$name].[$administrationRoute].dose += $dose
));
def statsCalculateCombinedDose($substanceName; $consumerName):
. as $stats |
(.[$consumerName].[$substanceName] | [to_entries[] | .value.dose] | add) as $combinedDose |
(.[$consumerName].[$substanceName] | to_entries[0] | .value.unit) as $combinedDoseUnit |
{dose: $combinedDose, unit: $combinedDoseUnit};
def experiencesWithExtraData($customUnits):
. as $experiences |
(reduce $experiences[] as $experience ([];
($experience | experienceStats($customUnits)) as $stats |
. += [{
$stats,
$experience
}]
));
def filterSortExperiences($customUnits; $substanceFilter; $consumerFilter; $sortMethod; $sortOptions):
. as $experiences |
($consumerFilter // ["default"]) as $consumerFilter |
$sortOptions |
# If filtering results by substances but no sortOptions.substanceName is defined, use the first one as a default
(.substanceName |=
if
$sortOptions.substanceName == null
and
(($substanceFilter | length) >= 1)
then
$substanceFilter[0]
else
null
end
) | . as $sortOptions |
$sortOptions | (.ingestionMethod |= ($sortOptions.ingestionMethod // "ORAL")) | . as $sortOptions |
$sortOptions | (.consumerName |= ($sortOptions.consumerName // "default")) | . as $sortOptions |
def sortFilter:
def oldToNewSort: .experience.sortDate;
def highestCombinedDoseSort: .stats.[$sortOptions.consumerName].[$sortOptions.substanceName] | [to_entries[] | .value.dose] | add;
def highestMethodDoseSort: .stats.[$sortOptions.consumerName].[$sortOptions.substanceName].[$sortOptions.ingestionMethod // "ORAL"].dose;
def filterBySubstanceFilter:
. as $experiencesData |
[
$experiencesData[] as $experienceData |
($experienceData.experience.ingestions) as $ingestions |
if ($experienceData.experience.ingestions |
any(
. as $ingestion |
$substanceFilter |
any(index($ingestion.substanceName))
)
) then $experienceData else null end
] | map(select(. != null));
def filterByConsumerFilter:
. as $experiencesData |
(reduce $experiencesData[] as $experienceData ([];
($experienceData.experience.ingestions) as $ingestions |
. +=
if ($experienceData.experience.ingestions |
any(
(. | ingestionConsumerName) as $consumerName |
$consumerFilter |
any(index($consumerName))
)
) then [$experienceData] else [] end
));
def filterBySubstanceAndConsumer:
. as $experiencesData |
(reduce $experiencesData[] as $experienceData ([];
($experienceData.experience.ingestions) as $ingestions |
. +=
if
($experienceData.experience.ingestions |
any(
.substanceName == $sortOptions.substanceName
and
(. | ingestionConsumerName) == $sortOptions.consumerName
))
then [$experienceData] else [] end
));
def filterByIngestionMethodForSubstance:
. as $experiencesData | filterBySubstanceAndConsumer as $experiencesData |
(reduce $experiencesData[] as $experienceData ([];
($experienceData.experience.ingestions) as $ingestions |
. += if ($experienceData.experience.ingestions | any(.substanceName == $sortOptions.substanceName and .administrationRoute == $sortOptions.ingestionMethod))
then [$experienceData] else [] end
));
# speeds up by excluding everything not containing substances & consumers not in filters, wouldn't show any data anyway
if $substanceFilter != null then filterBySubstanceFilter end |
filterByConsumerFilter |
if
$sortMethod == "old-to-new" or $sortMethod == null
then
sort_by(oldToNewSort)
elif
$sortMethod == "highest-combined-dose"
then
utils::assert($sortOptions.substanceName != null; "substanceName not provided as sortOption") |
filterBySubstanceAndConsumer |
sort_by(highestCombinedDoseSort, oldToNewSort) |
reverse
elif
$sortMethod == "highest-dose-for-method"
then
utils::assert($sortOptions.substanceName != null; "substanceName not provided as sortOption") |
filterByIngestionMethodForSubstance |
sort_by(highestMethodDoseSort, oldToNewSort) |
reverse
end;
$experiences |
experiencesWithExtraData($customUnits) |
sortFilter;
def experienceByTitle($name):
utils::assert((. | type) == "array"; "experienceByTitle takes a array of experiences as input") |
map(select(.title == $name))[0];

View file

@ -1,33 +1,18 @@
include "dropins";
include "utils";
import "lib/stringLib" as stringLib;
import "journalLib" as journalLib;
def formatExperienceTitle:
. as $experience |
"\"\(.title)\": \(.creationDate / 1000 | strftime("%d-%m-%Y"))";
def calculateIngestionDose($customUnits):
. as $ingestion |
if .customUnitId != null then
($customUnits | map(select(.id == $ingestion.customUnitId))[0]) as $customUnit |
.dose * $customUnit.dose | . as $dose |
$dose * 100 | round / 100
else
.dose
end;
def ingestionUnit($customUnits):
. as $ingestion |
if .customUnitId != null then
($customUnits | map(select(.id == $ingestion.customUnitId))[0]) as $customUnit |
$customUnit.originalUnit
else
.units
end;
def formatIngestionDose($customUnits):
. as $ingestion |
. | calculateIngestionDose($customUnits) as $dose |
. | ingestionUnit($customUnits) as $unit |
. | journalLib::calculateIngestionDose($customUnits) as $dose |
. | journalLib::ingestionUnit($customUnits) as $unit |
$customUnits | map(select(.id == $ingestion.customUnitId))[0] as $customUnit |
if $ingestion.dose == null then
"Unknown"
@ -50,7 +35,7 @@ def formatIngestionROA($customUnits; $substitutions):
then
$substitutions | .[$roa]
else
$roa | titleCase
$roa | stringLib::titleCase
end) as $roaText |
$ingestion.customUnitId as $customUnitId |
@ -63,64 +48,5 @@ def formatIngestionROA($customUnits; $substitutions):
$customUnits | map(select(.id == $customUnitId))[0] as $customUnit |
"\($roaText) (\($customUnit.name))"
end;
def formatIngestionROA($customUnits): formatIngestionROA($customUnits; {});
def filterIngestions($substanceFilter; $consumerFilter):
. as $ingestions |
$ingestions | if (($substanceFilter // [] | length) > 0) then
(reduce .[] as $ingestion ([];
if
([$substanceFilter[] | . == $ingestion.substanceName] | any)
and
([$consumerFilter[] | . == ifNullDefault($ingestion.consumerName; "default")] | any)
then . += [$ingestion]
else . end)
)
end;
def ingestionsByConsumer:
. as $ingestions |
(reduce $ingestions[] as $ingestion ({};
ifNullDefault($ingestion.consumerName; "default") as $consumerName |
if .[$consumerName] == null then .[$consumerName] |= [] end |
.[$consumerName] += [$ingestion]
));
def ingestionsSubstanceNames:
. as $ingestions |
[$ingestions[].substanceName] | orderedUnique;
def ingestionsConsumerNames:
. as $ingestions |
[$ingestions[] as $ingestion | ifNullDefault($ingestion.consumerName; "default")] | orderedUnique;
def experienceStats($customUnits):
. as $experience |
$experience.ingestions as $ingestions |
(reduce $ingestions[] as $ingestion ({}; . as $stats |
$ingestion |
.substanceName as $name |
.administrationRoute as $administrationRoute |
. | calculateIngestionDose($customUnits) as $dose |
. | ingestionUnit($customUnits) as $unit |
(.consumerName // "default") as $consumerName |
$stats | .[$consumerName].[$name].[$administrationRoute] |=
($stats.[$consumerName].[$name].[$administrationRoute] // {
unit: "",
# null because null+null = null for ingestions with unknown dose which .dose is null
dose: null
}) |
.[$consumerName].[$name].[$administrationRoute].unit |= $unit |
.[$consumerName].[$name].[$administrationRoute].dose += $dose
));
def calculateCombinedDose($substanceName; $consumerName):
. as $stats |
(.[$consumerName].[$substanceName] | [to_entries[] | .value.dose] | add) as $combinedDose |
(.[$consumerName].[$substanceName] | to_entries[0] | .value.unit) as $combinedDoseUnit |
{dose: $combinedDose, unit: $combinedDoseUnit};
def experienceByTitle($name):
assert((. | type) == "array"; "experienceByTitle takes a array of experiences as input") |
map(select(.title == $name))[0];

10
tool/lib/stringLib.jq Normal file
View file

@ -0,0 +1,10 @@
def titleCase:
[splits("\\b") | select(length>0)]
| map((.[:1]|ascii_upcase) + (.[1:] |ascii_downcase))
| join("");
def lpad(string;len;fill):
if len == 0 then string else (fill * len)[0:len] + string end;
def rpad(string;len;fill):
if len == 0 then string else string + (fill * len)[0:len] end;

84
tool/lib/tableLib.jq Normal file
View file

@ -0,0 +1,84 @@
import "stringLib" as stringLib;
def printPrettyTable($title; $columnTitles; $rows):
if [$rows[] | length] | unique | length > 1 then error("non-even number of columns") end |
$title | if . == null then "" end | . as $title |
" | " as $numColumnseperator |
def padRow($numColumns; $maxColumnLengths):
. as $row |
[
range($numColumns) as $columnNumber |
$row[$columnNumber] as $content |
if $columnNumber == 0 then
stringLib::lpad($content; $maxColumnLengths[$columnNumber] - ($content | length) ; " ")
else
stringLib::rpad($content; $maxColumnLengths[$columnNumber] - ($content | length) ; " ")
end
];
([$rows[] | length] | unique[0]) as $numColumns |
([$columnTitles | length] | unique[0]) as $numColumnTitles |
if $numColumns != $numColumnTitles then error("unequal title rows and columns") end |
[$columnTitles[] | length] as $maxColumnLengths |
(reduce $rows[] as $column ($maxColumnLengths;
[
range($numColumns) as $columnNumber |
[
.[$columnNumber],
($column[$columnNumber] | length)
] | max
]
)) as $maxColumnLengths |
$columnTitles |
padRow($numColumns; $maxColumnLengths) |
join($numColumnseperator) as $columnTitleRow |
[$rows[] | padRow($numColumns; $maxColumnLengths)] as $rows |
$title | length as $titleLength |
((($numColumns - 1) * ($numColumnseperator | length)) + ($maxColumnLengths | add)) as $maxRowLength |
[
$maxRowLength,
$titleLength
] | max as $maxLength |
# Pad title to be centered to maximum row length
$title | if $titleLength < $maxRowLength then
stringLib::lpad($title; ($maxLength - $titleLength) / 2; " ")
end | . as $title |
# if title is longer than maximum row length, pad the first row the difference between them
if $titleLength > $maxRowLength then
[
$rows[] as $row |
[
stringLib::lpad($row[0]; $titleLength - $maxRowLength; " ")
] + $row[1:]
]
else $rows end | . as $rows |
($rows | map(join($numColumnseperator))) as $rows |
if $title != "" then
[
$title,
([range($maxLength) | "-"] | join("")),
$columnTitleRow,
([range($maxLength) | "-"] | join("")),
$rows
] | flatten | join("\n") + "\n"
else
[
$columnTitleRow,
$rows
] | flatten | join("\n") + "\n"
end;

View file

@ -57,6 +57,8 @@ def ensureNullOr(ensureType):
def ensureKey($type; $key): if (. | has($key) | not) then typeError("\($type):\($key)") end;
def ensureKey($value; $type; $key): $value | ensureKey($type; $key);
def ensureIfKey($key; ensureType): if (. | has($key)) then .[$key] | ensureType end;
def ensureWrapError($newType; ensureType):
. as $value |
try (. | ensureType)
@ -69,7 +71,6 @@ def ensureWrapError($newType; ensureType):
def ensureWrapError($value; $newType; ensureType):
$value | ensureWrapError($newType; ensureType);
# JQ_TYPECHECKING=true required for tests
def typeLibTests:
testLib::expectPassed(testLib::runTest(

View file

@ -2,25 +2,30 @@ include "dropins";
import "lib/typeLib" as typeLib;
import "lib/argsLib" as argsLib;
import "lib/stringLib" as stringLib;
import "lib/tableLib" as tableLib;
include "utils";
include "journalUtils";
import "journalLib" as journalLib;
import "journalUtils" as journalUtils;
import "types" as types;
import "utils" as utils;
def printExperienceStats($stats; $substanceFilter; $consumerFilter; $withTitle):
. as $experience |
($consumerFilter // ["default"]) as $consumerFilter |
$experience.ingestions | filterIngestions($substanceFilter; $consumerFilter) as $ingestions |
($ingestions | ingestionsByConsumer) as $ingestionsByConsumer |
$experience.ingestions |
journalLib::filterIngestions($substanceFilter; $consumerFilter) as $ingestions |
($ingestions | journalLib::ingestionsByConsumer) as $ingestionsByConsumer |
($ingestionsByConsumer | keys) as $consumerNames |
"" as $experienceStatsText |
$experienceStatsText |
if $withTitle then
. += ($experience | formatExperienceTitle | . + "\n")
. += ($experience | journalUtils::formatExperienceTitle | . + "\n")
end | . as $experienceStatsText |
reduce $consumerNames[] as $consumerName ($experienceStatsText;
@ -45,16 +50,16 @@ def printExperienceStats($stats; $substanceFilter; $consumerFilter; $withTitle):
$substanceStats |
.key as $ingestionMethod |
ifNullDefault(.value.dose; "Unknown") as $dose |
(.value.dose // "Unknown") as $dose |
.value.unit as $unit |
($experienceStatsText | . += "Dose (\($ingestionMethod | titleCase)): \($dose) \($unit)\n") as $experienceStatsText |
($experienceStatsText | . += "Dose (\($ingestionMethod | stringLib::titleCase)): \($dose) \($unit)\n") as $experienceStatsText |
$experienceStatsText
) | . as $experienceStatsText |
$experienceStatsText | if ($ingestionMethods | length > 1) then
($stats | calculateCombinedDose($substanceName; $consumerName)) as $combinedDose |
. += "Combined Dose: \(ifNullDefault($combinedDose.dose; "Unknown")) \($combinedDose.unit)\n"
($stats | journalLib::statsCalculateCombinedDose($substanceName; $consumerName)) as $combinedDose |
. += "Combined Dose: \($combinedDose.dose // "Unknown") \($combinedDose.unit)\n"
end | . as $experienceStatsText |
$experienceStatsText | . += "\n"
@ -65,167 +70,41 @@ def printExperienceLog($customUnits; $substanceFilter; $consumerFilter; $pretty;
. as $experience |
($consumerFilter // ["default"]) as $consumerFilter |
$experience.ingestions | sort_by(.sortDate) | filterIngestions($substanceFilter; $consumerFilter) as $ingestions |
$experience.ingestions |
sort_by(.sortDate) |
journalLib::filterIngestions($substanceFilter; $consumerFilter) as $ingestions |
$ingestions | ingestionsConsumerNames as $consumerNames |
$ingestions | journalLib::ingestionsConsumerNames as $consumerNames |
if ($consumerNames == ["default"])
then
if ($consumerNames != ["default"]) then
["Substance", "Dose", "ROA", "Consumer", "Time"]
else
["Substance", "Dose", "ROA", "Time"]
end | . as $columnTitles |
(reduce $ingestions[] as $ingestion ([]; . as $rows |
$ingestion |
.substanceName as $substanceName |
ifNullDefault(.consumerName; "default") as $consumerName |
formatIngestionDose($customUnits) as $doseText |
formatIngestionROA($customUnits) as $roaText |
formatIngestionTime as $timeText |
$rows | . += [
[
$ingestions[] as $ingestion |
$ingestion |
.substanceName as $substanceName |
journalLib::ingestionConsumerName as $consumerName |
journalUtils::formatIngestionDose($customUnits) as $doseText |
journalUtils::formatIngestionROA($customUnits) as $roaText |
journalUtils::formatIngestionTime as $timeText |
if ($consumerNames != ["default"])
then [$substanceName, $doseText, $roaText, $consumerName, $timeText]
else [$substanceName, $doseText, $roaText, $timeText] end
]
)) as $rows |
] as $rows |
if $pretty then
printPrettyTable(
if $withTitle then ($experience | formatExperienceTitle) else null end;
$columnTitles;
$rows
)
else
$rows | map(join(" | ")) | rtrimstr(" ") as $rows |
(if $withTitle then [$experience | formatExperienceTitle] else [] end) + $rows | join("\n")
end | . as $ingestionLog |
tableLib::printPrettyTable(
if $withTitle then ($experience | journalUtils::formatExperienceTitle) else null end;
$columnTitles;
$rows
);
$ingestionLog;
def printExperiencesAdvanced($customUnits; $substanceFilter; $consumerFilter; $sortMethod; $sortOptions; $sortFilterString; sortFilter):
def printExperiences($customUnits; $substanceFilter; $consumerFilter):
. as $experiences |
($consumerFilter // ["default"]) as $consumerFilter |
$sortOptions |
# If filtering results by substances but no sortOptions.substanceName is defined, use the first one as a default
(.substanceName |=
if
$sortOptions.substanceName == null
and
(($substanceFilter | length) >= 1)
then
$substanceFilter[0]
else
null
end
) | . as $sortOptions |
$sortOptions | (.ingestionMethod |= ($sortOptions.ingestionMethod // "ORAL")) | . as $sortOptions |
$sortOptions | (.consumerName |= ($sortOptions.consumerName // "default")) | . as $sortOptions |
def sortFilterExperiences:
def oldToNewSort: .experience.sortDate;
def highestCombinedDoseSort: .stats.[$sortOptions.consumerName].[$sortOptions.substanceName] | [to_entries[] | .value.dose] | add;
def highestMethodDoseSort: .stats.[$sortOptions.consumerName].[$sortOptions.substanceName].[$sortOptions.ingestionMethod // "ORAL"].dose;
def filterBySubstanceFilter:
. as $experiencesData |
(reduce $experiencesData[] as $experienceData ([];
($experienceData.experience.ingestions) as $ingestions |
. +=
if ($experienceData.experience.ingestions |
any(
.substanceName as $substanceName |
$substanceFilter |
any(index($substanceName))
)
) then [$experienceData] else [] end
));
def filterByConsumerFilter:
. as $experiencesData |
(reduce $experiencesData[] as $experienceData ([];
($experienceData.experience.ingestions) as $ingestions |
. +=
if ($experienceData.experience.ingestions |
any(
ifNullDefault(.consumerName; "default") as $consumerName |
$consumerFilter |
any(index($consumerName))
)
) then [$experienceData] else [] end
));
def filterBySubstanceAndConsumer:
. as $experiencesData |
(reduce $experiencesData[] as $experienceData ([];
($experienceData.experience.ingestions) as $ingestions |
. +=
if
($experienceData.experience.ingestions |
any(
.substanceName == $sortOptions.substanceName
and
ifNullDefault(.consumerName; "default") == $sortOptions.consumerName
))
then [$experienceData] else [] end
));
def filterByIngestionMethodForSubstance:
. as $experiencesData | filterBySubstanceAndConsumer as $experiencesData |
(reduce $experiencesData[] as $experienceData ([];
($experienceData.experience.ingestions) as $ingestions |
. += if ($experienceData.experience.ingestions | any(.substanceName == $sortOptions.substanceName and .administrationRoute == $sortOptions.ingestionMethod))
then [$experienceData] else [] end
));
# speeds up by excluding everything not containing substances & consumers not in filters, wouldn't show any data anyway
if $substanceFilter != null then filterBySubstanceFilter end |
filterByConsumerFilter |
if
$sortMethod == "old-to-new" or $sortMethod == null
then
sort_by(oldToNewSort)
elif
$sortMethod == "highest-combined-dose"
then
assert($sortOptions.substanceName != null; "substanceName not provided as sortOption") |
filterBySubstanceAndConsumer |
sort_by(highestCombinedDoseSort, oldToNewSort) |
reverse
elif
$sortMethod == "highest-dose-for-method"
then
assert($sortOptions.substanceName != null; "substanceName not provided as sortOption") |
filterByIngestionMethodForSubstance |
sort_by(highestMethodDoseSort, oldToNewSort) |
reverse
end;
def sortFilterFromString($filterString):
(reduce ($filterString | split("|"))[] as $filter (.;
if
$filter == "reverse" then . | reverse
elif ($filter | startswith("firstN")) then
($filter | ltrimstr("firstN(") | rtrimstr(")")) as $arg |
. | firstN($arg | try fromjson catch error("invalid number passed to firstN"))
end
));
def experiencesWithExtraData($customUnits):
. as $experiences |
(reduce $experiences[] as $experience ([];
. += [{
stats: $experience | experienceStats($customUnits),
$experience
}]
));
$experiences |
experiencesWithExtraData($customUnits) |
sortFilterExperiences |
if ($sortFilterString != null) then . | sortFilterFromString($sortFilterString) end |
if (sortFilter != null) then . | sortFilter end |
.[] as $entry |
$entry.experience as $experience |
$entry.stats as $stats |
@ -248,7 +127,7 @@ def printExperiencesAdvanced($customUnits; $substanceFilter; $consumerFilter; $s
def main:
def usage:
[
"psychonaut_journal_stats {printExperience,printExperiencesAdvanced}",
"psychonaut_journal_stats {printExperience,printExperiences}",
""
] | join("\n") | halt_error(1);
@ -269,7 +148,7 @@ def main:
def printExperienceUsage($reason):
[
$reason,
"Usage: printExperience [experienceTitle] --pretty=bool --title=bool --stats=bool --substance-filter=[substanceNames,] --consumer-filter=[consumerNames,]",
"Usage: printExperience [experienceTitle] --title=bool --stats=bool --substance-filter=[substanceNames,] --consumer-filter=[consumerNames,]",
""
] | map(select(. != null)) | join("\n") | halt_error(1);
@ -282,7 +161,6 @@ def main:
{
substanceFilter: null,
consumerFilter: null,
pretty: true,
withTitle: true,
withStats: true,
} as $defaultOptions |
@ -292,14 +170,11 @@ def main:
$longArg.key as $arg |
$longArg.value as $value |
if $arg == "pretty" then
.pretty |= (ifNullDefault($value; $defaultOptions.pretty) | argsLib::parseArgBool)
end |
if $arg == "title" then
.withTitle |= (ifNullDefault($value; $defaultOptions.withTitle) | argsLib::parseArgBool)
.withTitle |= ($value // $defaultOptions.withTitle | argsLib::parseArgBool)
end |
if $arg == "stats" then
.withStats |= (ifNullDefault($value; $defaultOptions.withStats) | argsLib::parseArgBool)
.withStats |= ($value // $defaultOptions.withStats | argsLib::parseArgBool)
end |
if $arg == "substance-filter" then
.substanceFilter |= ($parsedArgs.longArgs.["substance-filter"] | split(","))
@ -309,7 +184,7 @@ def main:
end
)) | . as $options |
$exportData.experiences | experienceByTitle($experienceTitle) as $experience |
$exportData.experiences | journalLib::experienceByTitle($experienceTitle) as $experience |
if $experience == null then error("Experience not found") end |
($experience | printExperienceLog(
@ -321,7 +196,7 @@ def main:
)) as $ingestionLog |
if $options.withStats then
($experience | experienceStats($exportData.customUnits)) as $stats |
($experience | journalLib::experienceStats($exportData.customUnits)) as $stats |
$ingestionLog +
"\n\nCumulative Doses:\n" +
($experience | printExperienceStats(
@ -333,16 +208,16 @@ def main:
else
$ingestionLog
end
elif $program == "printExperiencesAdvanced" then
def printExperiencesAdvancedUsage($reason):
elif $program == "printExperiences" then
def printExperiencesUsage($reason):
[
$reason,
"Usage: printExperiencesAdvanced --substance-filter=[substanceNames,] --consumer-filter=[consumerNames,] --sort-method={old-to-new,highest-combined-dose,highest-dose-for-method} --sort-option-substance-name=[string] --sort-options-ingestion-method=[string] --sort-options-consumer-name=[string] --sort-filter={firstN(x),reverse}",
"Usage: printExperiences --substance-filter=[substanceNames,] --consumer-filter=[consumerNames,] --sort-method={old-to-new,highest-combined-dose,highest-dose-for-method} --sort-option-substance-name=[string] --sort-options-ingestion-method=[string] --sort-options-consumer-name=[string] --sort-filter={firstN(x),reverse}",
""
] | map(select(. != null)) | join("\n") | halt_error(1);
if any($parsedArgs.shortArgs[]; . == "h") then printExperiencesAdvancedUsage(null) end |
if ($parsedArgs.longArgs | has("help")) then printExperiencesAdvancedUsage(null) end |
if any($parsedArgs.shortArgs[]; . == "h") then printExperiencesUsage(null) end |
if ($parsedArgs.longArgs | has("help")) then printExperiencesUsage(null) end |
{
substanceFilter: null,
@ -380,13 +255,18 @@ def main:
)) | . as $options |
$exportData.experiences |
printExperiencesAdvanced(
journalLib::filterSortExperiences(
$exportData.customUnits;
$options.substanceFilter;
$options.consumerFilter;
$options.sortMethod;
$options.sortOptions;
$options.sortFilter; null
$options.sortOptions
) |
if ($options.sortFilter != null) then utils::sortFilterFromString($options.sortFilter) end |
printExperiences(
$exportData.customUnits;
$options.substanceFilter;
$options.consumerFilter
)
else
usage

View file

@ -4,27 +4,71 @@ import "lib/typeLib" as typeLib;
#["DEBUG:",{"creationDate":"number","ingestions":[{"administrationRoute":"string","consumerName":"null","creationDate":"number","customUnitId":"null","dose":"number","estimatedDoseStandardDeviation":"null","isDoseAnEstimate":"boolean","notes":"string","stomachFullness":"null","substanceName":"string","time":"number","units":"string"}],"isFavorite":"boolean","location":"null","ratings":"array:empty/unknown","sortDate":"number","text":"string","timedNotes":"array:empty/unknown","title":"string"}]
def ensureAdministrationRoute:
. as $administrationRoute |
if typeLib::typecheckingEnabled then
[
"ORAL",
"SUBLINGUAL",
"BUCCAL",
"INSUFFLATED",
"RECTAL",
"TRANSDERMAL",
"SUBCUTANEOUS",
"INTRAMUSCULAR",
"INTRAVENOUS",
"SMOKED",
"INHALED"
] | any(index($administrationRoute))
end;
def ensureIngestion:
. as $ingestion |
typeLib::ensureObject |
$ingestion | typeLib::ensureKey("ingestion"; "substanceName") |
.substanceName | typeLib::ensureWrapError("experience:substanceName"; typeLib::ensureString);
def ensureTimedNote:
. as $timedNote |
if typeLib::typecheckingEnabled then
$timedNote | typeLib::ensureObject |
$timedNote | typeLib::ensureKey("timedNote"; "color") |
$timedNote.color | typeLib::ensureWrapError("timedNote:color"; typeLib::ensureString) |
$timedNote | typeLib::ensureKey("timedNote"; "creationDate") |
$timedNote.creationDate | typeLib::ensureWrapError("timedNote:creationDate"; typeLib::ensureNumber) |
$timedNote | typeLib::ensureKey("timedNote"; "isPartOfTimeline") |
$timedNote.isPartOfTimeline | typeLib::ensureWrapError("timedNote:isPartOfTimeline"; typeLib::ensureBool) |
$timedNote | typeLib::ensureKey("timedNote"; "note") |
$timedNote.note | typeLib::ensureWrapError("timedNote:note"; typeLib::ensureString) |
$timedNote | typeLib::ensureKey("timedNote"; "time") |
$timedNote.time | typeLib::ensureWrapError("timedNote:time"; typeLib::ensureNumber)
typeLib::ensureObject |
$ingestion | typeLib::ensureKey("ingestion"; "substanceName") |
.substanceName | typeLib::ensureWrapError("experience:substanceName"; typeLib::ensureString) |
$ingestion | typeLib::ensureKey("ingestion"; "time") |
.time | typeLib::ensureWrapError("experience:time"; typeLib::ensureNumber) |
$ingestion | typeLib::ensureKey("ingestion"; "creationDate") |
.creationDate | typeLib::ensureWrapError("experience:creationDate"; typeLib::ensureNumber) |
# TODO: check routes
$ingestion |
typeLib::ensureKey("ingestion"; "administrationRoute") |
$ingestion.administrationRoute |
typeLib::ensureWrapError("experience:administrationRoute"; typeLib::ensureString) |
$ingestion.administrationRoute |
ensureAdministrationRoute |
typeLib::ensureWrapError(
"experience:dose";
$ingestion | typeLib::ensureIfKey("dose"; typeLib::ensureNullOr(typeLib::ensureNumber))
) |
$ingestion | typeLib::ensureKey("ingestion"; "isDoseAnEstimate") |
.isDoseAnEstimate | typeLib::ensureWrapError("experience:isDoseAnEstimate"; typeLib::ensureBool) |
$ingestion | typeLib::ensureKey("ingestion"; "units") |
.units | typeLib::ensureWrapError("experience:units"; typeLib::ensureString) |
$ingestion | typeLib::ensureKey("ingestion"; "notes") |
.notes | typeLib::ensureWrapError("experience:notes"; typeLib::ensureString) |
typeLib::ensureWrapError(
"experience:stomachFullness";
$ingestion | typeLib::ensureIfKey("stomachFullness"; typeLib::ensureNullOr(typeLib::ensureString))
) |
typeLib::ensureWrapError(
"experience:customUnitId";
$ingestion | typeLib::ensureIfKey("customUnitId"; typeLib::ensureNullOr(typeLib::ensureNumber))
) |
$ingestion
end;
def ensureExperience:
@ -44,17 +88,11 @@ def ensureExperience:
$experience | typeLib::ensureKey("experience"; "sortDate") |
$experience.sortDate | typeLib::ensureWrapError("experience:sortDate"; typeLib::ensureNumber) |
$experience | typeLib::ensureKey("experience"; "isFavorite") |
$experience.isFavorite | typeLib::ensureWrapError("experience:isFavorite"; typeLib::ensureBool) |
$experience | typeLib::ensureKey("experience"; "timedNotes") |
$experience.timedNotes | typeLib::ensureWrapError("experience:timedNotes"; typeLib::ensureArray) |
(reduce $experience.timedNotes[] as $timedNote (null; $timedNote | ensureTimedNote)) |
$experience | typeLib::ensureKey("experience"; "ingestions") |
$experience.ingestions | typeLib::ensureWrapError("experience:ingestions"; typeLib::ensureArray) |
(reduce $experience.ingestions[] as $ingestion (null; $ingestion | ensureIngestion))
(reduce $experience.ingestions[] as $ingestion (null; $ingestion | ensureIngestion)) |
$experience
end;
def ensureExportData:

View file

@ -1,5 +1,7 @@
include "dropins";
import "stringLib" as stringLib;
def debugLog($target; $value):
if
($ENV["JQ_DEBUG"] == $target)
@ -14,27 +16,8 @@ def debugLog($target; $value):
def assert(cond; $msg): if cond then . else error($msg) end;
def assert($loc; cond; $msg): assert(cond; "\($loc.file):\($loc.line): " + $msg);
def titleCase:
[splits("\\b") | select(length>0)]
| map((.[:1]|ascii_upcase) + (.[1:] |ascii_downcase))
| join("");
def lpad(string;len;fill):
if len == 0 then string else (fill * len)[0:len] + string end;
def rpad(string;len;fill):
if len == 0 then string else string + (fill * len)[0:len] end;
def firstN($n): .[:$n];
# only use when // does not suffice
def ifNullDefault($value; $default):
if $value != null then
$value
else
$default
end;
def orderedUnique:
(reduce .[] as $value ([];
if
@ -43,81 +26,11 @@ def orderedUnique:
else . end
));
def printPrettyTable($title; $columnTitles; $rows):
if [$rows[] | length] | unique | length > 1 then error("non-even number of columns") end |
$title | if . == null then "" end | . as $title |
" | " as $columnSeperator |
([$rows[] | length] | unique[0] // 0) as $columns |
[$rows[] as $columns | [$columns[] | length]] as $columnLengths |
(reduce $columnLengths[] as $lengths ([range($columns) | 0];
[range($columns) as $columnNumber | [.[$columnNumber], $lengths[$columnNumber]] | max]
)) as $maxColumnLengths |
(reduce $columnLengths[] as $lengths ($maxColumnLengths;
[range($columns) as $columnNumber | [.[$columnNumber], ($columnTitles[$columnNumber] | length)] | max]
)) as $maxColumnLengths |
(reduce range($columns) as $columnNumber ([];
$columnTitles[$columnNumber] as $title |
. += [
if $columnNumber == 0 then
lpad($title; $maxColumnLengths[$columnNumber] - ($title | length) ; " ")
else
rpad($title; $maxColumnLengths[$columnNumber] - ($title | length) ; " ")
end
]
)) | join($columnSeperator) as $columnTitleRow |
(reduce $rows[] as $row ([];
. += [(reduce range($columns) as $columnNumber ([]; . += [
$row[$columnNumber] as $column |
if $columnNumber == 0 then
lpad($column; $maxColumnLengths[$columnNumber] - ($column | length) ; " ")
else
rpad($column; $maxColumnLengths[$columnNumber] - ($column | length) ; " ")
end
]))]
)) as $rows |
$title | length as $titleLength |
((($columns - 1) * ($columnSeperator | length)) + ($maxColumnLengths | add)) as $maxRowLength |
[
$maxRowLength,
$titleLength
] | max as $maxLength |
# Pad title to be centered to maximum row length
$title | if $titleLength < $maxRowLength then
lpad($title; ($maxLength - $titleLength) / 2; " ")
end | . as $title |
# if title is longer than maximum row length, pad the first row the difference between them
$rows | if $titleLength > $maxRowLength then
(reduce .[] as $row ([];
. += [
[
lpad($row[0]; $titleLength - $maxRowLength; " ")
] + $row[1:]
]
))
end | . as $rows |
($rows | map(join($columnSeperator))) as $rows |
if $title != null then
[
$title,
([range($maxLength) | "-"] | join("")),
$columnTitleRow,
([range($maxLength) | "-"] | join("")),
$rows
] | flatten | join("\n") + "\n"
else
[
$columnTitleRow,
$rows
] | flatten | join("\n") + "\n"
end;
def sortFilterFromString($filterString):
($filterString | split("|")).[] as $filter |
if
$filter == "reverse" then . | reverse
elif ($filter | startswith("firstN")) then
($filter | ltrimstr("firstN(") | rtrimstr(")")) as $arg |
. | firstN($arg | try fromjson catch error("invalid number passed to firstN"))
end;