Skip to content

Commit 96293e3

Browse files
authored
Habitify - new components (#19416)
* fix file structure * new components * pnpm-lock.yaml * updates
1 parent 9b5bbe2 commit 96293e3

File tree

11 files changed

+315
-53
lines changed

11 files changed

+315
-53
lines changed

components/habitify/app/habitify.app.mjs

Lines changed: 0 additions & 32 deletions
This file was deleted.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { axios } from "@pipedream/platform";
2+
3+
export default {
4+
type: "app",
5+
app: "habitify",
6+
propDefinitions: {
7+
habitIds: {
8+
type: "string[]",
9+
label: "Habit IDs",
10+
description: "The IDs of the habits to watch",
11+
async options() {
12+
const { data } = await this.getHabits();
13+
return data?.map(({
14+
id, name,
15+
}) => ({
16+
label: name,
17+
value: id,
18+
})) || [];
19+
},
20+
},
21+
},
22+
methods: {
23+
_apiKey() {
24+
return this.$auth.api_key;
25+
},
26+
_apiUrl() {
27+
return "https://api.habitify.me";
28+
},
29+
async _makeRequest({
30+
$ = this, path, ...args
31+
}) {
32+
return axios($, {
33+
url: `${this._apiUrl()}${path}`,
34+
headers: {
35+
Authorization: this._apiKey(),
36+
},
37+
...args,
38+
});
39+
},
40+
getHabits(args = {}) {
41+
return this._makeRequest({
42+
path: "/habits",
43+
...args,
44+
});
45+
},
46+
getHabitStatus({
47+
habitId, ...args
48+
}) {
49+
return this._makeRequest({
50+
path: `/status/${habitId}`,
51+
...args,
52+
});
53+
},
54+
getHabitLogs({
55+
habitId, ...args
56+
}) {
57+
return this._makeRequest({
58+
path: `/logs/${habitId}`,
59+
...args,
60+
});
61+
},
62+
},
63+
};

components/habitify/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"name": "@pipedream/habitify",
3-
"version": "0.0.4",
3+
"version": "0.1.0",
44
"description": "Pipedream Habitify Components",
5-
"main": "app/habitify.app.mjs",
5+
"main": "habitify.app.mjs",
66
"keywords": [
77
"pipedream",
88
"habitify"
@@ -13,6 +13,6 @@
1313
"access": "public"
1414
},
1515
"dependencies": {
16-
"@pipedream/platform": "^1.6.8"
16+
"@pipedream/platform": "^3.1.1"
1717
}
1818
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import habitify from "../../habitify.app.mjs";
2+
import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform";
3+
4+
export default {
5+
props: {
6+
habitify,
7+
db: "$.service.db",
8+
timer: {
9+
type: "$.interface.timer",
10+
default: {
11+
intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
12+
},
13+
},
14+
},
15+
methods: {
16+
getCurrentDateTime() {
17+
return new Date().toISOString()
18+
.replace("Z", this.getTimeZoneOffset());
19+
},
20+
getTimeZoneOffset() {
21+
const offset = new Date().getTimezoneOffset();
22+
const sign = offset > 0
23+
? "-"
24+
: "+";
25+
const abs = Math.abs(offset);
26+
const hours = String(Math.floor(abs / 60)).padStart(2, "0");
27+
const minutes = String(abs % 60).padStart(2, "0");
28+
return `${sign}${hours}:${minutes}`;
29+
},
30+
convertToUTCOffset(dateString) {
31+
const date = new Date(dateString);
32+
const iso =
33+
date.getUTCFullYear() +
34+
"-" + String(date.getUTCMonth() + 1).padStart(2, "0") +
35+
"-" + String(date.getUTCDate()).padStart(2, "0") +
36+
"T" + String(date.getUTCHours()).padStart(2, "0") +
37+
":" + String(date.getUTCMinutes()).padStart(2, "0") +
38+
":" + String(date.getUTCSeconds()).padStart(2, "0");
39+
return iso + "+00:00";
40+
},
41+
},
42+
};
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import common from "../common/base-polling.mjs";
2+
import sampleEmit from "./test-event.mjs";
3+
4+
export default {
5+
...common,
6+
key: "habitify-habit-logged",
7+
name: "Habit Logged",
8+
description: "Emit new event when a new log is created for the selected habit(s). [See the documentation](https://docs.habitify.me/core-resources/habits/logs)",
9+
version: "0.0.1",
10+
type: "source",
11+
dedupe: "unique",
12+
props: {
13+
...common.props,
14+
habitIds: {
15+
propDefinition: [
16+
common.props.habitify,
17+
"habitIds",
18+
],
19+
},
20+
},
21+
methods: {
22+
...common.methods,
23+
_getLastTs() {
24+
return this.db.get("lastTs") || this.convertToUTCOffset(this.getCurrentDateTime());
25+
},
26+
_setLastTs(ts) {
27+
this.db.set("lastTs", ts);
28+
},
29+
generateMeta(log) {
30+
return {
31+
id: log.id,
32+
summary: `New log with ID ${log.id} created for habit ${log.habit_id}`,
33+
ts: Date.parse(log.created_date),
34+
};
35+
},
36+
},
37+
async run() {
38+
const lastTs = this._getLastTs();
39+
let maxTs = lastTs;
40+
const params = {
41+
from: lastTs,
42+
to: this.convertToUTCOffset(this.getCurrentDateTime()),
43+
};
44+
const logs = [];
45+
for (const habitId of this.habitIds) {
46+
const { data } = await this.habitify.getHabitLogs({
47+
habitId,
48+
params,
49+
});
50+
if (!data.length) {
51+
continue;
52+
}
53+
logs.push(...data);
54+
if (Date.parse(data[data.length - 1].created_date) > Date.parse(maxTs)) {
55+
maxTs = this.convertToUTCOffset(data[data.length - 1].created_date);
56+
}
57+
}
58+
this._setLastTs(maxTs);
59+
logs.sort((a, b) => Date.parse(a.created_date) - Date.parse(b.created_date));
60+
logs.forEach((log) => {
61+
this.$emit(log, this.generateMeta(log));
62+
});
63+
},
64+
sampleEmit,
65+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default {
2+
"id": "-Og94QB1QHNBaFW1Q12v",
3+
"value": 1,
4+
"created_date": "2025-12-10T21:05:52.838Z",
5+
"unit_type": "rep",
6+
"habit_id": "C0D4CD24-EF3E-4EAB-9D88-17BBDE801FC1"
7+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import common from "../common/base-polling.mjs";
2+
import sampleEmit from "./test-event.mjs";
3+
4+
export default {
5+
...common,
6+
key: "habitify-habit-status-updated",
7+
name: "Habit Status Updated",
8+
description: "Emit new event when the status of a habit is updated. [See the documentation](https://docs.habitify.me/core-resources/habits/status)",
9+
version: "0.0.1",
10+
type: "source",
11+
dedupe: "unique",
12+
props: {
13+
...common.props,
14+
habitIds: {
15+
propDefinition: [
16+
common.props.habitify,
17+
"habitIds",
18+
],
19+
},
20+
statuses: {
21+
type: "string[]",
22+
label: "Statuses",
23+
description: "The statuses to watch for. [See the documentation](https://docs.habitify.me/core-resources/habits/status)",
24+
options: [
25+
"in_progress",
26+
"completed",
27+
"skipped",
28+
"failed",
29+
],
30+
optional: true,
31+
},
32+
},
33+
hooks: {
34+
async deploy() {
35+
const previousStatuses = {};
36+
for (const habitId of this.habitIds) {
37+
const { data } = await this.habitify.getHabitStatus({
38+
habitId,
39+
params: {
40+
target_date: this.convertToUTCOffset(this.getCurrentDateTime()),
41+
},
42+
});
43+
previousStatuses[habitId] = data.status;
44+
}
45+
this._setPreviousStatuses(previousStatuses);
46+
},
47+
},
48+
methods: {
49+
...common.methods,
50+
_getPreviousStatuses() {
51+
return this.db.get("previousStatuses") || {};
52+
},
53+
_setPreviousStatuses(statuses) {
54+
this.db.set("previousStatuses", statuses);
55+
},
56+
generateMeta(status) {
57+
return {
58+
id: `${status.habit_id}-${status.progress.reference_date}`,
59+
summary: `New Status ${status.status} for habit ${status.habit_id}`,
60+
ts: Date.parse(status.progress.reference_date),
61+
};
62+
},
63+
},
64+
async run() {
65+
const previousStatuses = this._getPreviousStatuses();
66+
const currentStatuses = {};
67+
const targetDate = this.convertToUTCOffset(this.getCurrentDateTime());
68+
for (const habitId of this.habitIds) {
69+
const { data } = await this.habitify.getHabitStatus({
70+
habitId,
71+
params: {
72+
target_date: targetDate,
73+
},
74+
});
75+
currentStatuses[habitId] = data.status;
76+
if (
77+
previousStatuses[habitId] !== data.status
78+
&& (!this.statuses || this.statuses.includes(data.status))
79+
) {
80+
data.habit_id = habitId;
81+
this.$emit(data, this.generateMeta(data));
82+
}
83+
}
84+
this._setPreviousStatuses(currentStatuses);
85+
},
86+
sampleEmit,
87+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export default {
2+
"status": "completed",
3+
"progress": {
4+
"current_value": 8,
5+
"target_value": 8,
6+
"unit_type": "rep",
7+
"periodicity": "daily",
8+
"reference_date": "2025-12-10T00:00:00.000Z"
9+
}
10+
}
Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,14 @@
1-
import habitify from "../../app/habitify.app.mjs";
2-
import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform";
1+
import common from "../common/base-polling.mjs";
2+
import sampleEmit from "./test-event.mjs";
33

44
export default {
5+
...common,
56
name: "New Habit Created",
6-
version: "0.0.1",
7+
version: "0.0.2",
78
key: "habitify-new-habit-created",
8-
description: "Emit new event on each created habit.",
9+
description: "Emit new event on each created habit. [See the documentation](https://docs.habitify.me/core-resources/habits#list-habits)",
910
type: "source",
1011
dedupe: "unique",
11-
props: {
12-
habitify,
13-
db: "$.service.db",
14-
timer: {
15-
type: "$.interface.timer",
16-
static: {
17-
intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
18-
},
19-
},
20-
},
2112
methods: {
2213
emitEvent(data) {
2314
this.$emit(data, {
@@ -32,4 +23,5 @@ export default {
3223

3324
habits.reverse().forEach(this.emitEvent);
3425
},
26+
sampleEmit,
3527
};
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
export default {
2+
"id": "C0D4CD24-EF3E-4EAB-9D88-17BBDE801FC1",
3+
"name": "Drink Water",
4+
"is_archived": false,
5+
"start_date": "2025-12-10T19:57:16.566Z",
6+
"time_of_day": [
7+
"any_time"
8+
],
9+
"goal": {
10+
"unit_type": "rep",
11+
"value": 8,
12+
"periodicity": "daily"
13+
},
14+
"goal_history_items": [
15+
{
16+
"unit_type": "rep",
17+
"value": 8,
18+
"periodicity": "daily"
19+
}
20+
],
21+
"log_method": "manual",
22+
"recurrence": "DTSTART:20251210T195716Z\nRRULE:FREQ=DAILY",
23+
"remind": [
24+
"6:30"
25+
],
26+
"area": null,
27+
"created_date": "2025-12-10T19:57:16.566Z",
28+
"priority": 0
29+
}

0 commit comments

Comments
 (0)