summaryrefslogtreecommitdiff
path: root/awk
diff options
context:
space:
mode:
authorLeon Rische <leon.rische@me.com>2020-01-11 15:24:56 +0100
committerLeon Rische <leon.rische@me.com>2020-01-11 15:24:56 +0100
commit1c7838eb972ac365e648fc231620cb5f18a07788 (patch)
treed0a339cec50b9ecb468f2c7d380e3d3c37e85927 /awk
Initial commit
Diffstat (limited to 'awk')
-rw-r--r--awk/files.awk18
-rw-r--r--awk/filter_due.awk8
-rw-r--r--awk/index_cards.awk46
-rw-r--r--awk/index_positions.awk69
-rw-r--r--awk/stats_cards.awk47
-rw-r--r--awk/stats_positions.awk40
-rw-r--r--awk/stats_reviews.awk53
-rw-r--r--awk/utils.awk23
8 files changed, 304 insertions, 0 deletions
diff --git a/awk/files.awk b/awk/files.awk
new file mode 100644
index 0000000..6926408
--- /dev/null
+++ b/awk/files.awk
@@ -0,0 +1,18 @@
+BEGIN {
+ FS="|";
+}
+
+BEGINFILE {
+ has_card = 0;
+}
+
+# Flashcard headings
+/^\*+ .*:fc:.*$/ {
+ has_card = 1;
+}
+
+ENDFILE {
+ if (has_card == 1) {
+ print FILENAME;
+ }
+}
diff --git a/awk/filter_due.awk b/awk/filter_due.awk
new file mode 100644
index 0000000..b85fdfe
--- /dev/null
+++ b/awk/filter_due.awk
@@ -0,0 +1,8 @@
+BEGIN {
+ FS="\t";
+ now = strftime("%FT%T", systime(), 1);
+}
+
+$4 == "0" && $9 < now {
+ print $0
+}
diff --git a/awk/index_cards.awk b/awk/index_cards.awk
new file mode 100644
index 0000000..27c50b9
--- /dev/null
+++ b/awk/index_cards.awk
@@ -0,0 +1,46 @@
+BEGIN {
+ FS="|";
+
+ fc_tag = ":" or_default(fc_tag, "fc") ":";
+ suspended_tag = ":" or_default(suspended_tag, "suspended") ":";
+ review_data_drawer = ":" or_default(review_data_drawer, "REVIEW_DATA") ":";
+ type_property = or_default(type_property, "FC_TYPE");
+ created_property = or_default(created_property, "FC_CREATED");
+}
+
+## Heading Parsing
+
+/^\*+[ \t]+.*$/ {
+ # tag re based on org-tag-re
+ match($0, /^\*+[ \t]+.*[ \t]+(:([a-zA-Z0-9_@#%]+:)+)$/, a)
+ tags = a[1]
+ id = "none";
+
+ if (tags ~ fc_tag) {
+ in_card = 1;
+ suspended = (tags ~ suspended_tag);
+ } else {
+ in_card = 0;
+ }
+ next
+}
+
+## Property parsing
+
+in_card && /:PROPERTIES:/ {
+ in_properties = 1;
+ delete properties;
+}
+
+in_properties && match($0, /^[ \t]*:([a-zA-Z0-9_]+):[ \t]*(.+)$/, a) {
+ properties[a[1]] = trim_surrounding(a[2]);
+}
+
+in_properties && /:END:/ {
+ id = properties["ID"];
+ type = properties[type_property];
+ created = properties[created_property];
+ print FILENAME "\t" id "\t" type "\t" suspended "\t" created;
+ in_properties = 0;
+ in_card = 0;
+}
diff --git a/awk/index_positions.awk b/awk/index_positions.awk
new file mode 100644
index 0000000..36e9cbe
--- /dev/null
+++ b/awk/index_positions.awk
@@ -0,0 +1,69 @@
+BEGIN {
+ FS="|";
+
+ fc_tag = ":" or_default(fc_tag, "fc") ":";
+ suspended_tag = ":" or_default(suspended_tag, "suspended") ":";
+ review_data_drawer = ":" or_default(review_data_drawer, "REVIEW_DATA") ":";
+ type_property = or_default(type_property, "FC_TYPE");
+ created_property = or_default(created_property, "FC_CREATED");
+}
+
+## Heading Parsing
+
+/^\*+[ \t]+.*$/ {
+ # tag re based on org-tag-re
+ match($0, /^\*+[ \t]+.*[ \t]+(:([a-zA-Z0-9_@#%]+:)+)$/, a)
+ tags = a[1]
+
+ id = "none";
+
+ if (tags ~ fc_tag) {
+ in_card = 1;
+ suspended = (tags ~ suspended_tag);
+ } else {
+ in_card = 0;
+ }
+ next
+}
+
+## Property parsing
+
+in_card && /:PROPERTIES:/ {
+ in_properties = 1;
+ delete properties;
+}
+
+in_properties && match($0, /^[ \t]*:([a-zA-Z0-9_]+):[ \t]*(.+)$/, a) {
+ properties[a[1]] = trim_surrounding(a[2]);
+}
+
+in_properties && /:END:/ {
+ in_properties = 0;
+}
+
+## Review data parsing
+
+in_card && $0 ~ review_data_drawer {
+ in_data = 1;
+}
+
+in_data && /:END:/ {
+ in_data = 0;
+}
+
+in_data && /^\|.*\|$/ {
+ # Make sure we're inside a data block,
+ # check NF to skip the |--+--| table separator
+ # match on $2 to skip the table header
+ if (in_data == 1 && NF == 7 && $2 !~ "position") {
+ id = properties["ID"];
+ type = properties[type_property];
+
+ position = trim($2);
+ ease = trim($3);
+ box = trim($4);
+ interval = trim($5);
+ due = trim_surrounding($6);
+ print FILENAME "\t" id "\t" type "\t" suspended "\t" position "\t" ease "\t" box "\t" interval "\t" due;
+ }
+}
diff --git a/awk/stats_cards.awk b/awk/stats_cards.awk
new file mode 100644
index 0000000..3f29338
--- /dev/null
+++ b/awk/stats_cards.awk
@@ -0,0 +1,47 @@
+BEGIN {
+ FS="\t";
+ total = 0;
+ suspended = 0;
+
+ t_day = time_days_ago(1);
+ t_week = time_days_ago(7);
+ t_month = time_days_ago(30);
+
+ created["day"] = 0;
+ created["week"] = 0;
+ created["month"] = 0;
+}
+
+{
+ total += 1;
+
+ type = $3;
+ by_type[type] += 1;
+
+ if ($4 == "1") {
+ suspended++;
+ }
+
+ if ($5 > t_day) {
+ created["day"]++;
+ }
+
+ if ($5 > t_week) {
+ created["week"]++;
+ }
+
+ if ($5 > t_month) {
+ created["month"]++;
+ }
+}
+
+END {
+ print "total" "\t" total;
+ print "suspended" "\t" suspended;
+ print "created-day" "\t" created["day"];
+ print "created-week" "\t" created["week"];
+ print "created-month" "\t" created["month"];
+ for (var in by_type) {
+ print "type-" var "\t" by_type[var];
+ }
+}
diff --git a/awk/stats_positions.awk b/awk/stats_positions.awk
new file mode 100644
index 0000000..d38d2cd
--- /dev/null
+++ b/awk/stats_positions.awk
@@ -0,0 +1,40 @@
+BEGIN {
+ FS="\t";
+ total = 0;
+ suspended = 0;
+ ease = 0;
+ interval = 0;
+ box = 0;
+ due = 0;
+ now = strftime("%FT%T", systime(), 1);
+}
+
+{
+ total += 1;
+
+ type = $3;
+ by_type[type] += 1;
+
+ ease += $6;
+ box += $7;
+ interval += $8;
+
+ if ($4 == "1") {
+ suspended += 1;
+ }
+ if ($4 == "0" && $9 < now) {
+ due += 1;
+ }
+}
+
+END {
+ print "total" "\t" total;
+ print "suspended" "\t" suspended;
+ print "due" "\t" due;
+ for (var in by_type) {
+ print "type-" var "\t" by_type[var];
+ }
+ print "avg-ease" "\t" ease / NR;
+ print "avg-box" "\t" box / NR;
+ print "avg-interval" "\t" interval / NR;
+}
diff --git a/awk/stats_reviews.awk b/awk/stats_reviews.awk
new file mode 100644
index 0000000..bacafb2
--- /dev/null
+++ b/awk/stats_reviews.awk
@@ -0,0 +1,53 @@
+BEGIN {
+ FS="\t"
+ t_day = time_days_ago(1);
+ t_week = time_days_ago(7);
+ t_month = time_days_ago(30);
+}
+
+{
+ date = $1;
+ file = $2;
+ id = $3;
+ position = $4;
+ ease = $5;
+ box = $6;
+ interval = $7;
+ rating = $8;
+
+ if (box >= 2) {
+ if (date > t_day) {
+ ratings_day[rating]++;
+ n_day++;
+ }
+
+ if (date > t_week) {
+ ratings_week[rating]++;
+ n_week++;
+ }
+
+ if (date > t_month) {
+ ratings_month[rating]++;
+ n_month++;
+ }
+
+ ratings_all[rating]++;
+ n_all++;
+ }
+}
+
+END {
+ report(ratings_all, n_all);
+ report(ratings_month, n_month);
+ report(ratings_week, n_week);
+ report(ratings_day, n_day);
+}
+
+function report(values, n) {
+ if (n == 0) {
+ print 0 "\t" 0 "\t" 0 "\t" 0 "\t" 0;
+ } else {
+ print n "\t" values["again"] / n "\t" values["hard"] / n "\t" values["good"] /n "\t" values["easy"] / n;
+
+ }
+}
diff --git a/awk/utils.awk b/awk/utils.awk
new file mode 100644
index 0000000..00045de
--- /dev/null
+++ b/awk/utils.awk
@@ -0,0 +1,23 @@
+## Helper functions
+
+# Remove all whitespace in str
+function trim(str) {
+ gsub(/[ \t]/, "", str);
+ return str;
+}
+
+# Remove all whitespace around str
+function trim_surrounding(str) {
+ gsub(/^[ \t]*/, "", str);
+ gsub(/[ \t]*$/, "", str);
+ return str;
+}
+
+# Time n days before the current time
+function time_days_ago(n) {
+ return strftime("%FT%T", systime() - 24 * 60 * 60 * n, 1);
+}
+
+function or_default(var, def) {
+ return var ? var : def;
+}