Skip to content

Commit

Permalink
feat: filter events by patterns before selection (#113)
Browse files Browse the repository at this point in the history
  • Loading branch information
kacperaniolek committed Dec 18, 2023
1 parent 77793bd commit 997bbc0
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 15 deletions.
22 changes: 12 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ For this purpose a calendar entity is used, in which you have entered all appoin
**Features**
- Extra color, icon and text for residual, organic, paper waste and recycling
- Color and icon for all other appointments
- Filter out unexpected items

**Restrictions**
Currently only full day appointments are supported
Expand Down Expand Up @@ -69,9 +70,10 @@ All the options are available in the lovelace editor but you can use `yaml` if y
| `entity` | string | Required | Entity |
| `layout` | string | Optional | Layout of the card. Vertical, horizontal and default layout are supported |
| `fill_container` | boolean | `false` | Fill container or not. Useful when card is in a grid, vertical or horizontal layout |
| `full_size` | boolean | `false` | Show the card without the default card margins |
| `next_days` | number | 2 | How many times the card will look into the future to find the next event |
| `settings` | [Settings](#settings) | Required | Settings to detect the kind of trash and how to display |
| `filter_events` | boolean | `false` | Filter fetched events by patterns (if at least one is defined) before selecting the one to display |
| `full_size` | boolean | `false` | Show the card without the default card margins |
| `next_days` | number | 2 | How many times the card will look into the future to find the next event |
| `settings` | [Settings](#settings) | Required | Settings to detect the kind of trash and how to display |


#### Settings
Expand All @@ -83,24 +85,24 @@ All the options are available in the lovelace editor but you can use `yaml` if y
| `paper` | [TrashTypeConfig](#trash-type-configuration) | Required | Configuration to detect and display that the paper trash is picked up |
| `recycle` | [TrashTypeConfig](#trash-type-configuration) | Required | Configuration to detect and display that the organic trash is picked up |
| `waste` | [TrashTypeConfig](#trash-type-configuration) | Required | Configuration to detect and display that the waste trash is picked up |
| `others` | [OtherConfig](#other-type-trash-configuration) | Required | Configuration what should be display if non of the others types are matching |
| `others` | [OtherConfig](#other-type-trash-configuration) | Required | Configuration what should be display if non of the others types are matching |


#### Trash type configuration

| Name | Type | Default | Description |
| :------------------ | :-------------------------------------------------- | :---------- | :---------------------------------------------------------------------------------- |
| `label` | string | Required | Label which should be shown |
| `icon` | string | Required | Icon which should be displayed |
| `color` | string | Required | Background color of the card which should be used |
| `pattern` | string | Required | Pattern used to detected to display the apply this trash type. (Is tested against the calendar entry title)
| `label` | string | Required | Label which should be shown |
| `icon` | string | Required | Icon which should be displayed |
| `color` | string | Required | Background color of the card which should be used |
| `pattern` | string | Required | Pattern used to detected to display the apply this trash type. (Is tested against the calendar entry title) |

#### Other type trash configuration

| Name | Type | Default | Description |
| :------------------ | :-------------------------------------------------- | :---------- | :---------------------------------------------------------------------------------- |
| `icon` | string | Required | Icon which should be displayed |
| `color` | string | Required | Background color of the card which should be used |
| `icon` | string | Required | Icon which should be displayed |
| `color` | string | Required | Background color of the card which should be used |


### Example YAML configuration
Expand Down
4 changes: 4 additions & 0 deletions src/cards/trash-card/trash-card-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ EntityWithOutIcon & {
// eslint-disable-next-line @typescript-eslint/naming-convention
next_days?: number;
// eslint-disable-next-line @typescript-eslint/naming-convention
filter_events?: boolean;
// eslint-disable-next-line @typescript-eslint/naming-convention
full_size?: boolean;
};

Expand All @@ -30,6 +32,8 @@ export const entityCardConfigStruct = assign(
// eslint-disable-next-line @typescript-eslint/naming-convention
fill_container: optional(boolean()),
// eslint-disable-next-line @typescript-eslint/naming-convention
filter_events: optional(boolean()),
// eslint-disable-next-line @typescript-eslint/naming-convention
full_size: optional(boolean()),
// eslint-disable-next-line @typescript-eslint/naming-convention
next_days: optional(integer()),
Expand Down
2 changes: 2 additions & 0 deletions src/cards/trash-card/trash-card-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const TRASH_LABELS = new Set([
// eslint-disable-next-line @typescript-eslint/naming-convention
const OTHER_LABELS = new Set([
'next_days',
'filter_events',
'full_size'
]);

Expand Down Expand Up @@ -144,6 +145,7 @@ const SCHEMA: HaFormSchema[] = [
{ name: 'layout', selector: { mush_layout: {}}},
{ name: 'fill_container', selector: { boolean: {}}},
{ name: 'full_size', selector: { boolean: {}}},
{ name: 'filter_events', selector: { boolean: {}}},
{ name: 'next_days',
selector: { number: {
min: 1,
Expand Down
7 changes: 6 additions & 1 deletion src/cards/trash-card/trash-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,12 @@ export class TrashCard extends LitElement implements LovelaceCard {
then((response): CalendarItem =>
eventToItem(
findActiveEvent(
normaliseEvents(response), { now: new Date() }
normaliseEvents(response), { config: {
settings: this.config!.settings!,
// eslint-disable-next-line @typescript-eslint/naming-convention
filter_events: this.config!.filter_events
},
now: new Date() }
),
{ settings: this.config!.settings!, useSummary: false }
)).
Expand Down
1 change: 1 addition & 0 deletions src/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"layout": "Layout",
"fill_container": "Container ausfüllen",
"next_days": "Tage in der Zukunft",
"filter_events": "Ereignisse nach Mustern filtern",
"full_size": "Karte ohne Seitenrand"
},
"trash": {
Expand Down
1 change: 1 addition & 0 deletions src/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"layout": "Layout",
"fill_container": "Fill container",
"next_days": "Days in the future",
"filter_events": "Filter events by patterns",
"full_size": "Card without margin"
},
"trash": {
Expand Down
1 change: 1 addition & 0 deletions src/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"layout": "Disposition",
"fill_container": "Remplir le conteneur",
"next_days": "Jours dans le futur",
"filter_events": "Filtrer les événements par motifs",
"full_size": "Carte sans marge"
},
"trash": {
Expand Down
1 change: 1 addition & 0 deletions src/translations/sk.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"layout": "Rozloženie",
"fill_container": "Naplňte nádobu",
"next_days": "Dni v budúcnosti",
"filter_events": "Filtrovanie udalostí podľa vzorov",
"full_size": "Karta bez marže"
},
"trash": {
Expand Down
58 changes: 58 additions & 0 deletions src/utils/findActiveEvent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@ import type { RawCalendarEvent } from './calendarEvents';

describe('findActiveEvent', (): void => {
const offset = getTimeZoneOffset();
const emptyConfig = {
settings: {},
filter_events: undefined
};

test('the whole day event today cause its before 10 o`clock', async () => {
const events = normaliseEvents(calendarEvents as RawCalendarEvent[]);

const result = findActiveEvent(events, {
config: emptyConfig,
now: new Date(`2023-12-10T09:59:59${offset}`)
});

Expand All @@ -29,6 +34,7 @@ describe('findActiveEvent', (): void => {
const events = normaliseEvents(calendarEvents as RawCalendarEvent[]);

const result = findActiveEvent(events, {
config: emptyConfig,
now: new Date(`2023-12-10T10:01:59${offset}`)
});

Expand All @@ -46,6 +52,7 @@ describe('findActiveEvent', (): void => {
const events = normaliseEvents(calendarEvents as RawCalendarEvent[]);

const result = findActiveEvent(events, {
config: emptyConfig,
now: new Date(`2023-12-14T13:45:00+01:00`)
});

Expand All @@ -63,6 +70,57 @@ describe('findActiveEvent', (): void => {
const events = normaliseEvents(calendarEvents as RawCalendarEvent[]);

const result = findActiveEvent(events, {
config: emptyConfig,
now: new Date(`2023-12-14T14:15:00+01:00`)
});

expect(result).toEqual(expect.objectContaining({
isWholeDayEvent: false,
content: {
summary: 'Event 2',
description: null,
location: null
}
}));
});

test('the last event, event 3 is matching the pattern', async () => {
const events = normaliseEvents(calendarEvents as RawCalendarEvent[]);

const result = findActiveEvent(events, {
config: {
settings: {
recycle: {
pattern: 'Event 3'
}
},
filter_events: true
},
now: new Date(`2023-12-14T14:15:00+01:00`)
});

expect(result).toEqual(expect.objectContaining({
isWholeDayEvent: false,
content: {
summary: 'Event 3',
description: null,
location: null
}
}));
});

test('the second event, event 2 is today but today in the past and filtering is off', async () => {
const events = normaliseEvents(calendarEvents as RawCalendarEvent[]);

const result = findActiveEvent(events, {
config: {
settings: {
recycle: {
pattern: 'Event 3'
}
},
filter_events: false
},
now: new Date(`2023-12-14T14:15:00+01:00`)
});

Expand Down
31 changes: 27 additions & 4 deletions src/utils/findActiveEvent.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
import type { CalendarEvent } from './calendarEvents';
import { getDayFromDate } from './getDayFromDate';
import type { TrashCardConfig } from '../cards/trash-card/trash-card-config';

interface Config {
settings: Required<TrashCardConfig>['settings'];
// eslint-disable-next-line @typescript-eslint/naming-convention
filter_events: TrashCardConfig['filter_events'];
}

interface Options {
config: Config;
now: Date;
}

const findActiveEvent = (items: CalendarEvent[], { now }: Options): CalendarEvent | undefined => {
const isMatchingAnyPatterns = (item: CalendarEvent, config: Config) => {
if (!config.filter_events) {
return true;
}

const trashTypes = Object.keys(config.settings).filter(type => type !== 'others');
const patterns = trashTypes.map(type => Reflect.get(config.settings, type).pattern!).filter(pattern => pattern !== null);

return patterns.length === 0 || patterns.find(pattern => item.content.summary.includes(pattern));
};

const isNotPastWholeDayEvent = (item: CalendarEvent, now: Date): boolean =>
(item.isWholeDayEvent && getDayFromDate(item.date.start) === getDayFromDate(now) && now.getHours() < 10) ||
(item.isWholeDayEvent && getDayFromDate(item.date.start) !== getDayFromDate(now));

const findActiveEvent = (items: CalendarEvent[], { config, now }: Options): CalendarEvent | undefined => {
const activeItems = items.
filter((item): boolean => {
if (item.isWholeDayEvent) {
Expand All @@ -22,9 +45,9 @@ const findActiveEvent = (items: CalendarEvent[], { now }: Options): CalendarEven

return activeItems.
find((item): boolean =>
(item.isWholeDayEvent && getDayFromDate(item.date.start) === getDayFromDate(now) && now.getHours() < 10) ||
(item.isWholeDayEvent && getDayFromDate(item.date.start) !== getDayFromDate(now)) ||
!item.isWholeDayEvent);
isMatchingAnyPatterns(item, config) &&
(isNotPastWholeDayEvent(item, now) ||
!item.isWholeDayEvent));
};

export {
Expand Down

0 comments on commit 997bbc0

Please sign in to comment.