friends 0.30 → 0.31
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -0
- data/PULL_REQUEST_TEMPLATE.md +2 -0
- data/README.md +95 -61
- data/friends.gemspec +2 -3
- data/friends.md +4 -4
- data/lib/friends.rb +0 -1
- data/lib/friends/activity.rb +2 -6
- data/lib/friends/commands/graph.rb +1 -18
- data/lib/friends/graph.rb +70 -19
- data/lib/friends/introvert.rb +19 -2
- data/lib/friends/version.rb +1 -1
- data/test/commands/add/activity_spec.rb +2 -0
- data/test/commands/clean_spec.rb +3 -0
- data/test/commands/graph_spec.rb +120 -26
- data/test/commands/help_spec.rb +1 -0
- data/test/commands/list/activities_spec.rb +3 -1
- data/test/commands/list/favorite/friends_spec.rb +2 -0
- data/test/commands/list/favorite/locations_spec.rb +2 -0
- data/test/commands/list/friends_spec.rb +2 -0
- data/test/commands/list/locations_spec.rb +2 -0
- data/test/commands/list/tags_spec.rb +2 -0
- data/test/commands/stats_spec.rb +2 -0
- data/test/commands/suggest_spec.rb +2 -0
- data/test/commands/update_spec.rb +1 -0
- data/test/helper.rb +1 -1
- metadata +6 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d644149046a9df58031ed1aefcd34ca28d45fb91
|
4
|
+
data.tar.gz: f4964423489ad13fcdfc789c3e9ccc025c65a0e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c5189966fe28ac95307bab8375cd31075deb6e928847950dd7e7af1e5f9c4b4b186d2da8ca91da9021324dbf8f720a9799c0a4ef6327afe2a7bac67b2573c4c
|
7
|
+
data.tar.gz: dd219dc75e1f7a01e7f8f0131f5980490b122641fbafbdf87d09221aa18a672cce34707a7c9cb4d9c91282e84710ff874783e570c49d8988f73180c5e593c152
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,28 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## [v0.31](https://github.com/JacobEvelyn/friends/tree/v0.31) (2017-06-02)
|
4
|
+
[Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.30...v0.31)
|
5
|
+
|
6
|
+
**Implemented enhancements:**
|
7
|
+
|
8
|
+
- Change graph commands to display filtered graphs within total [\#171](https://github.com/JacobEvelyn/friends/issues/171)
|
9
|
+
|
10
|
+
**Fixed bugs:**
|
11
|
+
|
12
|
+
- Graph does not display more than 42 activities per month [\#172](https://github.com/JacobEvelyn/friends/issues/172)
|
13
|
+
|
14
|
+
**Closed issues:**
|
15
|
+
|
16
|
+
- Reduce dependencies [\#167](https://github.com/JacobEvelyn/friends/issues/167)
|
17
|
+
- Add standalone distribution [\#160](https://github.com/JacobEvelyn/friends/issues/160)
|
18
|
+
- Integrate with cron for regular reminders [\#56](https://github.com/JacobEvelyn/friends/issues/56)
|
19
|
+
|
20
|
+
**Merged pull requests:**
|
21
|
+
|
22
|
+
- Improve graph command [\#174](https://github.com/JacobEvelyn/friends/pull/174) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
23
|
+
- Remove Memoist dependency and bump gem dependencies [\#170](https://github.com/JacobEvelyn/friends/pull/170) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
24
|
+
- Remove warnings when running tests [\#169](https://github.com/JacobEvelyn/friends/pull/169) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
25
|
+
|
3
26
|
## [v0.30](https://github.com/JacobEvelyn/friends/tree/v0.30) (2017-05-30)
|
4
27
|
[Full Changelog](https://github.com/JacobEvelyn/friends/compare/v0.29...v0.30)
|
5
28
|
|
@@ -9,6 +32,10 @@
|
|
9
32
|
- Improve display of favorites for ties [\#158](https://github.com/JacobEvelyn/friends/issues/158)
|
10
33
|
- Filter activities based on more than one friend/tag/etc. [\#88](https://github.com/JacobEvelyn/friends/issues/88)
|
11
34
|
|
35
|
+
**Closed issues:**
|
36
|
+
|
37
|
+
- Reduce warnings [\#166](https://github.com/JacobEvelyn/friends/issues/166)
|
38
|
+
|
12
39
|
**Merged pull requests:**
|
13
40
|
|
14
41
|
- Be able to filter output by more than one friend or tag [\#168](https://github.com/JacobEvelyn/friends/pull/168) ([JacobEvelyn](https://github.com/JacobEvelyn))
|
data/PULL_REQUEST_TEMPLATE.md
CHANGED
data/README.md
CHANGED
@@ -18,6 +18,7 @@ lots of help), and give feedback!**
|
|
18
18
|
- [Core concepts](#core-concepts)
|
19
19
|
- [Global flags](#global-flags)
|
20
20
|
- [Syncing across multiple machines](#syncing-across-multiple-machines)
|
21
|
+
- [Setting reminders](#setting-reminders)
|
21
22
|
- [Command reference](#command-reference)
|
22
23
|
- `add`
|
23
24
|
- [`add activity`](#add-activity)
|
@@ -124,10 +125,10 @@ The `friends.md` Markdown file that stores all of your data contains:
|
|
124
125
|
|
125
126
|
```markdown
|
126
127
|
### Activities:
|
127
|
-
-
|
128
|
-
-
|
129
|
-
-
|
130
|
-
-
|
128
|
+
- 2018-11-01: **Grace Hopper** and I went to _Marie's Diner_. George had to cancel at the last minute.
|
129
|
+
- 2018-01-04: Got lunch with **Grace Hopper** and **George Washington Carver**.
|
130
|
+
- 2017-12-31: Celebrated the new year in _Paris_ with **Marie Curie**.
|
131
|
+
- 2017-11-15: Talked to **George Washington Carver** on the phone for an hour.
|
131
132
|
```
|
132
133
|
|
133
134
|
See the example
|
@@ -188,6 +189,29 @@ do this for you, like so:
|
|
188
189
|
alias friends="friends --filename '~/Dropbox/friends.md'"
|
189
190
|
```
|
190
191
|
|
192
|
+
### Setting reminders
|
193
|
+
|
194
|
+
Though `friends` has no built-in reminder functionality, it's easy to use a
|
195
|
+
system like `cron` (Mac or Linux) or `Task Scheduler` (Windows) to set various
|
196
|
+
reminders.
|
197
|
+
|
198
|
+
For example, on a Mac, the following `crontab` configuration results in every day
|
199
|
+
at 9:00 p.m. a Terminal tab opening, printing "Time to journal!" and then launching
|
200
|
+
an `add activity` prompt through `friends`:
|
201
|
+
|
202
|
+
```
|
203
|
+
0 21 * * * osascript -e 'activate application "Terminal"' &> /dev/null && osascript -e 'tell application "Terminal" to do script "clear && echo Time to journal! && friends add activity"' &> /dev/null
|
204
|
+
```
|
205
|
+
|
206
|
+
Here's another example (also for Mac) of using `friends` to suggest some people to
|
207
|
+
hang out with every Saturday morning:
|
208
|
+
|
209
|
+
```
|
210
|
+
0 10 * * 6 osascript -e 'activate application "Terminal"' &> /dev/null && osascript -e 'tell application "Terminal" to do script "clear && echo Consider hanging out with one of these friends today: && friends suggest"' &> /dev/null
|
211
|
+
```
|
212
|
+
|
213
|
+
(If you use other tools, please share and we'll add to these examples!)
|
214
|
+
|
191
215
|
### Command reference*
|
192
216
|
|
193
217
|
*Note that the command-line output is colored, which this README cannot show.
|
@@ -196,7 +220,7 @@ alias friends="friends --filename '~/Dropbox/friends.md'"
|
|
196
220
|
|
197
221
|
```bash
|
198
222
|
$ friends add activity Got lunch with Grace and George.
|
199
|
-
Activity added: "
|
223
|
+
Activity added: "2018-01-04: Got lunch with Grace Hopper and George Washington Carver."
|
200
224
|
```
|
201
225
|
|
202
226
|
`friends` will **automatically** figure out which "Grace" and "George" you're referring to, *even if you're friends with lots of different Graces and Georges*.
|
@@ -206,7 +230,7 @@ just like formal names:
|
|
206
230
|
|
207
231
|
```bash
|
208
232
|
$ friends add activity Invented debugging with The Admiral.
|
209
|
-
Activity added: "
|
233
|
+
Activity added: "2017-01-06: Invented debugging with Grace Hopper."
|
210
234
|
```
|
211
235
|
|
212
236
|
You can also use the first initial of a last name instead of the whole thing.
|
@@ -215,14 +239,14 @@ them) based on whether you're in the middle of a sentence or not:
|
|
215
239
|
|
216
240
|
```bash
|
217
241
|
$ friends add activity Got lunch with Earnest H and Earnest S. in the park. Man, I like Earnest H. but really love Earnest S.
|
218
|
-
Activity added: "
|
242
|
+
Activity added: "2017-05-01: Got lunch with Earnest Hemingway and Earnest Shackleton in the park. Man, I like Earnest Hemingway but really love Earnest Shackleton."
|
219
243
|
```
|
220
244
|
|
221
245
|
And locations will be matched as well:
|
222
246
|
|
223
247
|
```bash
|
224
248
|
$ friends add activity Went swimming near atlantis with George.
|
225
|
-
Activity added: "
|
249
|
+
Activity added: "2017-01-06: Went swimming near Atlantis with George Washington Carver."
|
226
250
|
```
|
227
251
|
|
228
252
|
Tags will be colored if they're provided (though this README can't display
|
@@ -230,35 +254,35 @@ color so you'll just have to have faith here):
|
|
230
254
|
|
231
255
|
```bash
|
232
256
|
$ friends add activity The office softball team wins a game! @work @exercise
|
233
|
-
Activity added: "
|
257
|
+
Activity added: "2017-05-05: The office softball team wins a game! @work @exercise"
|
234
258
|
```
|
235
259
|
|
236
260
|
You can of course specify a date for the activity:
|
237
261
|
|
238
262
|
```bash
|
239
263
|
$ friends add activity Yesterday: Celebrated the new year with Marie.
|
240
|
-
Activity added: "
|
264
|
+
Activity added: "2017-12-31: Celebrated the new year with Marie Curie."
|
241
265
|
```
|
242
266
|
|
243
267
|
Or get an **interactive prompt** by just typing `friends add activity`, with or without a date specified:
|
244
268
|
|
245
269
|
```bash
|
246
|
-
$ friends add activity
|
247
|
-
|
270
|
+
$ friends add activity 2018-11-01
|
271
|
+
2018-11-01: <type description here>
|
248
272
|
```
|
249
273
|
|
250
274
|
**Natural-language dates** work just fine:
|
251
275
|
|
252
276
|
```bash
|
253
277
|
$ friends add activity last Monday
|
254
|
-
|
278
|
+
2017-03-07: <type description here>
|
255
279
|
```
|
256
280
|
|
257
281
|
You can escape the names of friends you don't want `friends` to match with a backslash:
|
258
282
|
|
259
283
|
```bash
|
260
|
-
$ friends add activity "
|
261
|
-
Activity added: "
|
284
|
+
$ friends add activity "2018-11-01: Grace and I went to \Marie's Diner. \George had to cancel at the last minute."
|
285
|
+
Activity added: "2018-11-01: Grace Hopper and I went to Marie's Diner. George had to cancel at the last minute."
|
262
286
|
```
|
263
287
|
|
264
288
|
#### `add friend`
|
@@ -327,78 +351,88 @@ Graphs (in color!) your activities over time:
|
|
327
351
|
|
328
352
|
```bash
|
329
353
|
$ friends graph
|
330
|
-
Nov
|
331
|
-
Dec
|
332
|
-
Jan
|
333
|
-
Feb
|
354
|
+
Nov 2017 |███
|
355
|
+
Dec 2017 |██
|
356
|
+
Jan 2018 |███████
|
357
|
+
Feb 2018 |█████
|
334
358
|
```
|
335
359
|
|
336
360
|
Or graph only activities with a certain friend:
|
337
361
|
|
338
362
|
```bash
|
339
363
|
$ friends graph --with George
|
340
|
-
Nov
|
341
|
-
Dec
|
342
|
-
Jan
|
343
|
-
Feb
|
364
|
+
Nov 2017 |█∙∙|
|
365
|
+
Dec 2017 |∙∙|
|
366
|
+
Jan 2018 |█████∙∙|
|
367
|
+
Feb 2018 |███∙∙|
|
344
368
|
```
|
345
369
|
|
346
|
-
|
370
|
+
The dots represent the total activities that month, so you can get a feel for the
|
371
|
+
proportion of activities with that friend vs. the total you've logged.
|
372
|
+
|
373
|
+
You can also graph a certain group of friends:
|
347
374
|
|
348
375
|
```bash
|
349
376
|
$ friends graph --with George --with Grace
|
350
|
-
|
377
|
+
Nov 2017 |∙∙∙|
|
378
|
+
Dec 2017 |∙∙|
|
379
|
+
Jan 2018 |█∙∙∙∙∙∙|
|
380
|
+
Feb 2018 |∙∙∙∙∙|
|
351
381
|
```
|
352
382
|
|
353
383
|
Or graph only activities with a certain tag:
|
354
384
|
|
355
385
|
```bash
|
356
386
|
$ friends graph --tagged food
|
357
|
-
Nov
|
358
|
-
Dec
|
359
|
-
Jan
|
360
|
-
Feb
|
387
|
+
Nov 2017 |█∙∙|
|
388
|
+
Dec 2017 |∙∙|
|
389
|
+
Jan 2018 |∙∙∙∙∙∙∙|
|
390
|
+
Feb 2018 |███∙∙|
|
361
391
|
```
|
362
392
|
|
363
393
|
Or with multiple tags:
|
364
394
|
|
365
395
|
```bash
|
366
396
|
$ friends graph --tagged @fun --tagged @work
|
367
|
-
|
397
|
+
Nov 2017 |∙∙∙|
|
398
|
+
Dec 2017 |█∙|
|
399
|
+
Jan 2018 |∙∙∙∙∙∙∙|
|
400
|
+
Feb 2018 |█∙∙∙∙|
|
368
401
|
```
|
369
402
|
|
370
403
|
Or graph only activities in a certain location:
|
371
404
|
|
372
405
|
```bash
|
373
406
|
$ friends graph --in Paris
|
374
|
-
Nov
|
375
|
-
Dec
|
376
|
-
Jan
|
377
|
-
Feb
|
407
|
+
Nov 2017 |█∙∙|
|
408
|
+
Dec 2017 |∙∙|
|
409
|
+
Jan 2018 |∙∙∙∙∙∙∙|
|
410
|
+
Feb 2018 |█∙∙∙∙|
|
378
411
|
```
|
379
412
|
|
380
413
|
Or graph only activities on or after a certain date:
|
381
414
|
|
382
415
|
```bash
|
383
|
-
$ friends graph --since 'January 1st
|
384
|
-
Jan
|
385
|
-
Feb
|
416
|
+
$ friends graph --since 'January 1st 2018'
|
417
|
+
Jan 2018 |███████
|
418
|
+
Feb 2018 |█████
|
386
419
|
```
|
387
420
|
|
388
421
|
Or graph only activities before or on a certain date:
|
389
422
|
|
390
423
|
```bash
|
391
|
-
$ friends graph --until 'January 1st
|
392
|
-
Nov
|
393
|
-
Dec
|
394
|
-
Jan
|
424
|
+
$ friends graph --until 'January 1st 2018'
|
425
|
+
Nov 2017 |███
|
426
|
+
Dec 2017 |██
|
427
|
+
Jan 2018 |███████
|
395
428
|
```
|
396
429
|
|
397
430
|
And you can use multiple of these flags together:
|
398
431
|
|
399
432
|
```bash
|
400
|
-
$ friends graph --in Paris --tagged food --with George --
|
401
|
-
|
433
|
+
$ friends graph --in Paris --tagged food --with George --since 'Jan 2018'
|
434
|
+
Jan 2018 |∙∙∙∙∙∙∙|
|
435
|
+
Fen 2017 |█∙∙∙∙|
|
402
436
|
```
|
403
437
|
|
404
438
|
#### `help`
|
@@ -436,74 +470,74 @@ Lists recent activities:
|
|
436
470
|
|
437
471
|
```bash
|
438
472
|
$ friends list activities
|
439
|
-
|
440
|
-
|
441
|
-
|
473
|
+
2018-01-04: Got lunch with Grace Hopper and George Washington Carver. @food
|
474
|
+
2017-12-31: Celebrated the new year with Marie Curie in New York City. @partying
|
475
|
+
2017-11-15: Talked to George Washington Carver on the phone for an hour.
|
442
476
|
```
|
443
477
|
|
444
478
|
You can adjust how many activities are shown:
|
445
479
|
|
446
480
|
```bash
|
447
481
|
$ friends list activities --limit 2
|
448
|
-
|
449
|
-
|
482
|
+
2018-01-04: Got lunch with Grace Hopper and George Washington Carver. @food
|
483
|
+
2017-12-31: Celebrated the new year with Marie Curie in New York City. @partying
|
450
484
|
```
|
451
485
|
|
452
486
|
Or only list the activities you did with a certain friend:
|
453
487
|
|
454
488
|
```bash
|
455
489
|
$ friends list activities --with George
|
456
|
-
|
457
|
-
|
490
|
+
2018-01-04: Got lunch with Grace Hopper and George Washington Carver. @food
|
491
|
+
2017-11-15: Talked to George Washington Carver on the phone for an hour.
|
458
492
|
```
|
459
493
|
|
460
494
|
Or only filter activities done with a group of friends:
|
461
495
|
|
462
496
|
```bash
|
463
497
|
$ friends list activities --with George --with Grace
|
464
|
-
|
498
|
+
2018-01-04: Got lunch with Grace Hopper and George Washington Carver. @food
|
465
499
|
```
|
466
500
|
|
467
501
|
Or filter your activities by location:
|
468
502
|
|
469
503
|
```bash
|
470
504
|
$ friends list activities --in "New York"
|
471
|
-
|
505
|
+
2017-12-31: Celebrated the new year with Marie Curie in New York City. @partying
|
472
506
|
```
|
473
507
|
|
474
508
|
Or by tag:
|
475
509
|
|
476
510
|
```bash
|
477
511
|
$ friends list activities --tagged food
|
478
|
-
|
512
|
+
2018-01-04: Got lunch with Grace Hopper and George Washington Carver. @food
|
479
513
|
```
|
480
514
|
|
481
515
|
Or use more than one tag:
|
482
516
|
|
483
517
|
```bash
|
484
518
|
$ friends list activities --tagged @fun --tagged @work
|
485
|
-
|
519
|
+
2018-07-04: Summer picnic with @work colleagues. @fun
|
486
520
|
```
|
487
521
|
|
488
522
|
Or by date:
|
489
523
|
|
490
524
|
```bash
|
491
|
-
$ friends list activities --since 'December 31st
|
492
|
-
|
493
|
-
|
525
|
+
$ friends list activities --since 'December 31st 2017'
|
526
|
+
2018-01-04: Got lunch with Grace Hopper and George Washington Carver. @food
|
527
|
+
2017-12-31: Celebrated the new year with Marie Curie in New York City. @partying
|
494
528
|
```
|
495
529
|
|
496
530
|
```bash
|
497
|
-
$ friends list activities --until 'December 31st
|
498
|
-
|
499
|
-
|
531
|
+
$ friends list activities --until 'December 31st 2017'
|
532
|
+
2017-12-31: Celebrated the new year with Marie Curie in New York City. @partying
|
533
|
+
2017-11-15: Talked to George Washington Carver on the phone for an hour.
|
500
534
|
```
|
501
535
|
|
502
536
|
And you can mix and match these options to your heart's content:
|
503
537
|
|
504
538
|
```bash
|
505
539
|
$ friends list activities --tagged food --with Grace --with George
|
506
|
-
|
540
|
+
2018-01-04: Got lunch with Grace Hopper and George Washington Carver. @food
|
507
541
|
```
|
508
542
|
|
509
543
|
#### `list favorite friends`
|
data/friends.gemspec
CHANGED
@@ -25,9 +25,8 @@ Gem::Specification.new do |spec|
|
|
25
25
|
|
26
26
|
spec.add_dependency "chronic", "~> 0.10"
|
27
27
|
spec.add_dependency "gli", "~> 2.12"
|
28
|
-
spec.add_dependency "
|
29
|
-
spec.add_dependency "
|
30
|
-
spec.add_dependency "semverse", "~> 1.2"
|
28
|
+
spec.add_dependency "paint", "~> 2.0"
|
29
|
+
spec.add_dependency "semverse", "~> 2.0"
|
31
30
|
|
32
31
|
spec.add_development_dependency "bundler", "~> 1.6"
|
33
32
|
spec.add_development_dependency "coveralls", "~> 0.8"
|
data/friends.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
### Activities:
|
2
|
-
-
|
3
|
-
-
|
4
|
-
-
|
5
|
-
-
|
2
|
+
- 2018-11-01: **Grace Hopper** and I went to _Marie's Diner_. George had to cancel at the last minute. @food
|
3
|
+
- 2018-01-04: Got lunch with **Grace Hopper** and **George Washington Carver**. @food
|
4
|
+
- 2017-12-31: Celebrated the new year in _Paris_ with **Marie Curie**. @partying
|
5
|
+
- 2017-11-15: Talked to **George Washington Carver** on the phone for an hour.
|
6
6
|
|
7
7
|
### Friends:
|
8
8
|
- George Washington Carver
|
data/lib/friends.rb
CHANGED
data/lib/friends/activity.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
# Activity represents an activity you've done with one or more Friends.
|
4
4
|
|
5
5
|
require "chronic"
|
6
|
-
require "memoist"
|
7
6
|
require "paint"
|
8
7
|
require "set"
|
9
8
|
|
@@ -12,7 +11,6 @@ require "friends/serializable"
|
|
12
11
|
module Friends
|
13
12
|
class Activity
|
14
13
|
extend Serializable
|
15
|
-
extend Memoist
|
16
14
|
|
17
15
|
SERIALIZATION_PREFIX = "- "
|
18
16
|
DATE_PARTITION = ": "
|
@@ -143,16 +141,14 @@ module Friends
|
|
143
141
|
# Find the names of all friends in this description.
|
144
142
|
# @return [Array] list of all friend names in the description
|
145
143
|
def friend_names
|
146
|
-
@description.scan(/(?<=\*\*)\w[^\*]*(?=\*\*)/).uniq
|
144
|
+
@_friend_names ||= @description.scan(/(?<=\*\*)\w[^\*]*(?=\*\*)/).uniq
|
147
145
|
end
|
148
|
-
memoize :friend_names
|
149
146
|
|
150
147
|
# Find the names of all locations in this description.
|
151
148
|
# @return [Array] list of all location names in the description
|
152
149
|
def location_names
|
153
|
-
@description.scan(/(?<=_)\w[^_]*(?=_)/).uniq
|
150
|
+
@_location_names ||= @description.scan(/(?<=_)\w[^_]*(?=_)/).uniq
|
154
151
|
end
|
155
|
-
memoize :location_names
|
156
152
|
|
157
153
|
private
|
158
154
|
|
@@ -30,29 +30,12 @@ command :graph do |graph|
|
|
30
30
|
type: InputDate
|
31
31
|
|
32
32
|
graph.action do |_, options|
|
33
|
-
|
34
|
-
PI_3 = Math::PI / 3
|
35
|
-
|
36
|
-
colors = (0...(6 * 7)).map do |n|
|
37
|
-
n *= 1.0 / 6
|
38
|
-
r = (3 * Math.sin(n) + 3).to_i
|
39
|
-
g = (3 * Math.sin(n + 2 * PI_3) + 3).to_i
|
40
|
-
b = (3 * Math.sin(n + 4 * PI_3) + 3).to_i
|
41
|
-
|
42
|
-
[r, g, b].map { |c| c * 51 }
|
43
|
-
end
|
44
|
-
|
45
|
-
data = @introvert.graph(
|
33
|
+
@introvert.graph(
|
46
34
|
with: options[:with],
|
47
35
|
location_name: options[:in],
|
48
36
|
tagged: options[:tagged],
|
49
37
|
since_date: options[:since],
|
50
38
|
until_date: options[:until]
|
51
39
|
)
|
52
|
-
|
53
|
-
data.each do |month, count|
|
54
|
-
print "#{month} |"
|
55
|
-
puts colors.take(count).map { |rgb| Paint["█", rgb] }.join
|
56
|
-
end
|
57
40
|
end
|
58
41
|
end
|
data/lib/friends/graph.rb
CHANGED
@@ -7,47 +7,65 @@ module Friends
|
|
7
7
|
DATE_FORMAT = "%b %Y"
|
8
8
|
|
9
9
|
# @param activities [Array<Friends::Activity>] a list of activities to graph
|
10
|
-
def initialize(
|
11
|
-
@
|
12
|
-
|
13
|
-
|
14
|
-
@
|
10
|
+
def initialize(filtered_activities:, all_activities:)
|
11
|
+
@filtered_activities = filtered_activities
|
12
|
+
@all_activities = all_activities
|
13
|
+
unless @all_activities.empty?
|
14
|
+
@start_date = @all_activities.last.date
|
15
|
+
@end_date = @all_activities.first.date
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
19
|
+
# Prints the graph to STDOUT, with colors.
|
20
|
+
def draw
|
21
|
+
to_h.each do |month, (filtered_count, total_count)|
|
22
|
+
str = "#{month} |"
|
23
|
+
str += Array.new(filtered_count) do |count|
|
24
|
+
Paint["█", color(count)]
|
25
|
+
end.join
|
26
|
+
if total_count > filtered_count
|
27
|
+
str += Array.new(total_count - filtered_count) do |count|
|
28
|
+
Paint["∙", color(filtered_count + count)]
|
29
|
+
end.join + Paint["|", color(total_count + 1)]
|
30
|
+
end
|
31
|
+
puts str
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
18
37
|
# Render the graph as a hash in the format:
|
19
38
|
#
|
20
39
|
# {
|
21
|
-
# "Jan 2015" => 3, #
|
22
|
-
# "Feb 2015" => 0,
|
23
|
-
# "Mar 2015" => 9
|
40
|
+
# "Jan 2015" => [3, 4], # [# filtered activities, # total activities]
|
41
|
+
# "Feb 2015" => [0, 0],
|
42
|
+
# "Mar 2015" => [0, 9]
|
24
43
|
# }
|
25
44
|
#
|
26
45
|
# @return [Hash{String => Integer}]
|
27
46
|
def to_h
|
28
47
|
empty_graph.tap do |graph|
|
29
|
-
|
30
|
-
graph[format_date(activity.date)] += 1
|
48
|
+
@filtered_activities.each do |activity|
|
49
|
+
graph[format_date(activity.date)][0] += 1
|
50
|
+
end
|
51
|
+
@all_activities.each do |activity|
|
52
|
+
graph[format_date(activity.date)][1] += 1
|
31
53
|
end
|
32
54
|
end
|
33
55
|
end
|
34
56
|
|
35
|
-
private
|
36
|
-
|
37
|
-
attr_accessor :start_date, :end_date, :activities
|
38
|
-
|
39
57
|
# Render an empty graph as a hash in the format:
|
40
58
|
#
|
41
59
|
# {
|
42
|
-
# "Jan 2015" => 0,
|
43
|
-
# "Feb 2015" => 0,
|
44
|
-
# "Mar 2015" => 0
|
60
|
+
# "Jan 2015" => [0, 0] # [# filtered activities, # total activities]
|
61
|
+
# "Feb 2015" => [0, 0]
|
62
|
+
# "Mar 2015" => [0, 0]
|
45
63
|
# }
|
46
64
|
#
|
47
65
|
# @return [Hash{String => Integer}]
|
48
66
|
def empty_graph
|
49
|
-
Hash[(start_date && end_date ? (start_date
|
50
|
-
[format_date(date), 0]
|
67
|
+
Hash[(@start_date && @end_date ? (@start_date..@end_date) : []).map do |date|
|
68
|
+
[format_date(date), [0, 0]]
|
51
69
|
end]
|
52
70
|
end
|
53
71
|
|
@@ -57,5 +75,38 @@ module Friends
|
|
57
75
|
def format_date(date)
|
58
76
|
date.strftime(DATE_FORMAT)
|
59
77
|
end
|
78
|
+
|
79
|
+
# @param x [Integer] the x coordinate we want to color; x >= 0
|
80
|
+
# @return [Array<Integer>] the color we should use to paint
|
81
|
+
# a point on the graph at the given x coordinate
|
82
|
+
def color(x)
|
83
|
+
COLORS[x % COLORS.size]
|
84
|
+
end
|
85
|
+
|
86
|
+
# Originally generated by executing the code in Minitest's Pride plugin (the PrideLOL class),
|
87
|
+
# and then pulling the unique values out and doubling them to create a more even distribution
|
88
|
+
# of colors.
|
89
|
+
COLORS = [
|
90
|
+
[153, 255, 0], [153, 255, 0],
|
91
|
+
[153, 204, 0], [153, 204, 0],
|
92
|
+
[204, 204, 0], [204, 204, 0],
|
93
|
+
[255, 153, 0], [255, 153, 0],
|
94
|
+
[255, 102, 0], [255, 102, 0],
|
95
|
+
[255, 51, 51], [255, 51, 51],
|
96
|
+
[255, 0, 102], [255, 0, 102],
|
97
|
+
[255, 0, 153], [255, 0, 153],
|
98
|
+
[204, 0, 204], [204, 0, 204],
|
99
|
+
[153, 0, 255], [153, 0, 255],
|
100
|
+
[102, 0, 255], [102, 0, 255],
|
101
|
+
[51, 51, 255], [51, 51, 255],
|
102
|
+
[0, 102, 255], [0, 102, 255],
|
103
|
+
[0, 153, 255], [0, 153, 255],
|
104
|
+
[0, 204, 204], [0, 204, 204],
|
105
|
+
[0, 255, 153], [0, 255, 153],
|
106
|
+
[0, 255, 102], [0, 255, 102],
|
107
|
+
[51, 255, 51], [51, 255, 51],
|
108
|
+
[102, 255, 0], [102, 255, 0]
|
109
|
+
].freeze
|
110
|
+
private_constant :COLORS
|
60
111
|
end
|
61
112
|
end
|
data/lib/friends/introvert.rb
CHANGED
@@ -310,7 +310,7 @@ module Friends
|
|
310
310
|
# @raise [FriendsError] if friend, location or tag cannot be found or
|
311
311
|
# is ambiguous
|
312
312
|
def graph(with:, location_name:, tagged:, since_date:, until_date:)
|
313
|
-
|
313
|
+
filtered_activities_to_graph = filtered_activities(
|
314
314
|
with: with,
|
315
315
|
location_name: location_name,
|
316
316
|
tagged: tagged,
|
@@ -318,7 +318,24 @@ module Friends
|
|
318
318
|
until_date: until_date
|
319
319
|
)
|
320
320
|
|
321
|
-
|
321
|
+
# If the user wants to graph in a specific date range, we explicitly
|
322
|
+
# limit our output to that date range. We don't just use the date range
|
323
|
+
# of the first and last `filtered_activities_to_graph` because those
|
324
|
+
# activities might not include others in the full range (for instance,
|
325
|
+
# if only one filtered activity matches a query, we don't want to only
|
326
|
+
# show unfiltered activities that occurred on that specific day).
|
327
|
+
all_activities_to_graph = filtered_activities(
|
328
|
+
with: [],
|
329
|
+
location_name: nil,
|
330
|
+
tagged: [],
|
331
|
+
since_date: since_date,
|
332
|
+
until_date: until_date
|
333
|
+
)
|
334
|
+
|
335
|
+
Graph.new(
|
336
|
+
filtered_activities: filtered_activities_to_graph,
|
337
|
+
all_activities: all_activities_to_graph
|
338
|
+
).draw
|
322
339
|
end
|
323
340
|
|
324
341
|
# Suggest friends to do something with.
|
data/lib/friends/version.rb
CHANGED
data/test/commands/clean_spec.rb
CHANGED
@@ -4,12 +4,15 @@ require "./test/helper"
|
|
4
4
|
|
5
5
|
clean_describe "clean" do
|
6
6
|
subject { run_cmd("clean") }
|
7
|
+
let(:content) { nil }
|
7
8
|
|
8
9
|
it "outputs a message" do
|
9
10
|
stdout_only "File cleaned: \"#{filename}\""
|
10
11
|
end
|
11
12
|
|
12
13
|
describe "when file does not exist" do
|
14
|
+
let(:content) { nil }
|
15
|
+
|
13
16
|
it "does not create the file" do
|
14
17
|
File.exist?(filename).must_equal false
|
15
18
|
end
|
data/test/commands/graph_spec.rb
CHANGED
@@ -6,6 +6,8 @@ clean_describe "graph" do
|
|
6
6
|
subject { run_cmd("graph") }
|
7
7
|
|
8
8
|
describe "when file does not exist" do
|
9
|
+
let(:content) { nil }
|
10
|
+
|
9
11
|
it "prints no output" do
|
10
12
|
stdout_only ""
|
11
13
|
end
|
@@ -20,13 +22,35 @@ clean_describe "graph" do
|
|
20
22
|
end
|
21
23
|
|
22
24
|
describe "when file has content" do
|
23
|
-
|
25
|
+
# Content must be sorted to avoid errors.
|
26
|
+
let(:content) do
|
27
|
+
<<-FILE
|
28
|
+
### Activities:
|
29
|
+
- 2015-11-01: **Grace Hopper** and I went to _Marie's Diner_. George had to cancel at the last minute. @food
|
30
|
+
- 2015-01-14: Got lunch with **Grace Hopper** and **George Washington Carver**. @food
|
31
|
+
- 2015-01-06: Did some other things in _Paris_.
|
32
|
+
- 2015-01-06: Did even more things in _Paris_.
|
33
|
+
- 2015-01-05: Did even more things in _Paris_.
|
34
|
+
- 2014-12-31: Celebrated the new year in _Paris_ with **Marie Curie**. @partying @food
|
35
|
+
- 2014-11-15: Talked to **George Washington Carver** on the phone for an hour.
|
36
|
+
|
37
|
+
### Friends:
|
38
|
+
- George Washington Carver
|
39
|
+
- Marie Curie [Atlantis] @science
|
40
|
+
- Grace Hopper (a.k.a. The Admiral a.k.a. Amazing Grace) [Paris] @navy @science
|
41
|
+
|
42
|
+
### Locations:
|
43
|
+
- Atlantis
|
44
|
+
- Marie's Diner
|
45
|
+
- Paris
|
46
|
+
FILE
|
47
|
+
end
|
24
48
|
|
25
49
|
it "graphs all activities" do
|
26
50
|
stdout_only <<-OUTPUT
|
27
51
|
Nov 2014 |█
|
28
52
|
Dec 2014 |█
|
29
|
-
Jan 2015
|
53
|
+
Jan 2015 |████
|
30
54
|
Feb 2015 |
|
31
55
|
Mar 2015 |
|
32
56
|
Apr 2015 |
|
@@ -40,6 +64,16 @@ Nov 2015 |█
|
|
40
64
|
OUTPUT
|
41
65
|
end
|
42
66
|
|
67
|
+
describe "when there are more activities than colors" do
|
68
|
+
let(:content) do
|
69
|
+
(["### Activities:"] + (["- 2017-06-01: Did something."] * 100)).join("\n")
|
70
|
+
end
|
71
|
+
|
72
|
+
it "displays the correct number of activities" do
|
73
|
+
stdout_only("Jun 2017 |" + ("█" * 100))
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
43
77
|
describe "--in" do
|
44
78
|
subject { run_cmd("graph --in #{location_name}") }
|
45
79
|
|
@@ -54,7 +88,19 @@ Nov 2015 |█
|
|
54
88
|
let(:location_name) { "paris" }
|
55
89
|
it "matches location case-insensitively" do
|
56
90
|
stdout_only <<-OUTPUT
|
91
|
+
Nov 2014 |∙|
|
57
92
|
Dec 2014 |█
|
93
|
+
Jan 2015 |███∙|
|
94
|
+
Feb 2015 |
|
95
|
+
Mar 2015 |
|
96
|
+
Apr 2015 |
|
97
|
+
May 2015 |
|
98
|
+
Jun 2015 |
|
99
|
+
Jul 2015 |
|
100
|
+
Aug 2015 |
|
101
|
+
Sep 2015 |
|
102
|
+
Oct 2015 |
|
103
|
+
Nov 2015 |∙|
|
58
104
|
OUTPUT
|
59
105
|
end
|
60
106
|
end
|
@@ -85,8 +131,18 @@ Dec 2014 |█
|
|
85
131
|
it "matches friend case-insensitively" do
|
86
132
|
stdout_only <<-OUTPUT
|
87
133
|
Nov 2014 |█
|
88
|
-
Dec 2014
|
89
|
-
Jan 2015
|
134
|
+
Dec 2014 |∙|
|
135
|
+
Jan 2015 |█∙∙∙|
|
136
|
+
Feb 2015 |
|
137
|
+
Mar 2015 |
|
138
|
+
Apr 2015 |
|
139
|
+
May 2015 |
|
140
|
+
Jun 2015 |
|
141
|
+
Jul 2015 |
|
142
|
+
Aug 2015 |
|
143
|
+
Sep 2015 |
|
144
|
+
Oct 2015 |
|
145
|
+
Nov 2015 |∙|
|
90
146
|
OUTPUT
|
91
147
|
end
|
92
148
|
end
|
@@ -97,7 +153,21 @@ Jan 2015 |█
|
|
97
153
|
let(:friend_name2) { "grace" }
|
98
154
|
|
99
155
|
it "matches all friends case-insensitively" do
|
100
|
-
stdout_only
|
156
|
+
stdout_only <<-OUTPUT
|
157
|
+
Nov 2014 |∙|
|
158
|
+
Dec 2014 |∙|
|
159
|
+
Jan 2015 |█∙∙∙|
|
160
|
+
Feb 2015 |
|
161
|
+
Mar 2015 |
|
162
|
+
Apr 2015 |
|
163
|
+
May 2015 |
|
164
|
+
Jun 2015 |
|
165
|
+
Jul 2015 |
|
166
|
+
Aug 2015 |
|
167
|
+
Sep 2015 |
|
168
|
+
Oct 2015 |
|
169
|
+
Nov 2015 |∙|
|
170
|
+
OUTPUT
|
101
171
|
end
|
102
172
|
end
|
103
173
|
end
|
@@ -107,7 +177,9 @@ Jan 2015 |█
|
|
107
177
|
|
108
178
|
it "matches tag case-sensitively" do
|
109
179
|
stdout_only <<-OUTPUT
|
110
|
-
|
180
|
+
Nov 2014 |∙|
|
181
|
+
Dec 2014 |█
|
182
|
+
Jan 2015 |█∙∙∙|
|
111
183
|
Feb 2015 |
|
112
184
|
Mar 2015 |
|
113
185
|
Apr 2015 |
|
@@ -125,33 +197,33 @@ Nov 2015 |█
|
|
125
197
|
subject { run_cmd("graph --tagged #{tag1} --tagged #{tag2}") }
|
126
198
|
let(:tag1) { "food" }
|
127
199
|
let(:tag2) { "partying" }
|
128
|
-
let(:content) do
|
129
|
-
<<-FILE
|
130
|
-
### Activities:
|
131
|
-
- 2015-01-04: Got lunch with **Grace Hopper** and **George Washington Carver**. @food
|
132
|
-
- 2015-11-01: **Grace Hopper** and I went to _Marie's Diner_. George had to cancel at the last minute. @food
|
133
|
-
- 2014-11-15: Talked to **George Washington Carver** on the phone for an hour.
|
134
|
-
- 2014-12-31: Celebrated the new year in _Paris_ with **Marie Curie**. @partying @food
|
135
|
-
|
136
|
-
### Friends:
|
137
|
-
- George Washington Carver
|
138
|
-
- Marie Curie [Atlantis] @science
|
139
|
-
- Grace Hopper (a.k.a. The Admiral a.k.a. Amazing Grace) [Paris] @navy @science
|
140
|
-
FILE
|
141
|
-
end
|
142
200
|
|
143
201
|
it "matches all tags case-sensitively" do
|
144
|
-
stdout_only
|
202
|
+
stdout_only <<-OUTPUT
|
203
|
+
Nov 2014 |∙|
|
204
|
+
Dec 2014 |█
|
205
|
+
Jan 2015 |∙∙∙∙|
|
206
|
+
Feb 2015 |
|
207
|
+
Mar 2015 |
|
208
|
+
Apr 2015 |
|
209
|
+
May 2015 |
|
210
|
+
Jun 2015 |
|
211
|
+
Jul 2015 |
|
212
|
+
Aug 2015 |
|
213
|
+
Sep 2015 |
|
214
|
+
Oct 2015 |
|
215
|
+
Nov 2015 |∙|
|
216
|
+
OUTPUT
|
145
217
|
end
|
146
218
|
end
|
147
219
|
end
|
148
220
|
|
149
221
|
describe "--since" do
|
150
|
-
subject { run_cmd("graph --since 'January
|
222
|
+
subject { run_cmd("graph --since 'January 6th 2015'") }
|
151
223
|
|
152
224
|
it "graphs activities on and after the specified date" do
|
153
225
|
stdout_only <<-OUTPUT
|
154
|
-
Jan 2015
|
226
|
+
Jan 2015 |███
|
155
227
|
Feb 2015 |
|
156
228
|
Mar 2015 |
|
157
229
|
Apr 2015 |
|
@@ -166,14 +238,36 @@ Nov 2015 |█
|
|
166
238
|
end
|
167
239
|
end
|
168
240
|
|
169
|
-
describe "--
|
170
|
-
subject { run_cmd("graph --until 'January
|
241
|
+
describe "--until" do
|
242
|
+
subject { run_cmd("graph --until 'January 6th 2015'") }
|
171
243
|
|
172
244
|
it "graphs activities before and on the specified date" do
|
173
245
|
stdout_only <<-OUTPUT
|
174
246
|
Nov 2014 |█
|
175
247
|
Dec 2014 |█
|
176
|
-
Jan 2015
|
248
|
+
Jan 2015 |███
|
249
|
+
OUTPUT
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
describe "combining filters" do
|
254
|
+
subject { run_cmd("graph --since 'January 6th 2015' --with Grace") }
|
255
|
+
|
256
|
+
it "only shows other activities within the same period as the filtered ones" do
|
257
|
+
# If we just rounded to the month, there would be three unfiltered activities in
|
258
|
+
# January displayed (due to the one on 1/5/2015). Instead, we correctly display two.
|
259
|
+
stdout_only <<-OUTPUT
|
260
|
+
Jan 2015 |█∙∙|
|
261
|
+
Feb 2015 |
|
262
|
+
Mar 2015 |
|
263
|
+
Apr 2015 |
|
264
|
+
May 2015 |
|
265
|
+
Jun 2015 |
|
266
|
+
Jul 2015 |
|
267
|
+
Aug 2015 |
|
268
|
+
Sep 2015 |
|
269
|
+
Oct 2015 |
|
270
|
+
Nov 2015 |█
|
177
271
|
OUTPUT
|
178
272
|
end
|
179
273
|
end
|
data/test/commands/help_spec.rb
CHANGED
@@ -6,6 +6,8 @@ clean_describe "list activities" do
|
|
6
6
|
subject { run_cmd("list activities") }
|
7
7
|
|
8
8
|
describe "when file does not exist" do
|
9
|
+
let(:content) { nil }
|
10
|
+
|
9
11
|
it "does not list anything" do
|
10
12
|
stdout_only ""
|
11
13
|
end
|
@@ -157,7 +159,7 @@ FILE
|
|
157
159
|
end
|
158
160
|
end
|
159
161
|
|
160
|
-
describe "--
|
162
|
+
describe "--until" do
|
161
163
|
subject { run_cmd("list activities --until 'January 4th 2015'") }
|
162
164
|
|
163
165
|
it "lists activities before and on the specified date" do
|
data/test/commands/stats_spec.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -89,6 +89,7 @@ def ensure_trailing_newline_unless_empty(str)
|
|
89
89
|
end
|
90
90
|
|
91
91
|
def stdout_only(expected)
|
92
|
+
puts subject[:stderr] unless subject[:stderr] == ""
|
92
93
|
subject[:stdout].must_equal ensure_trailing_newline_unless_empty(expected)
|
93
94
|
subject[:stderr].must_equal ""
|
94
95
|
subject[:status].must_equal 0
|
@@ -123,7 +124,6 @@ end
|
|
123
124
|
def clean_describe(desc, *additional_desc, &block)
|
124
125
|
describe desc, *additional_desc do
|
125
126
|
let(:filename) { "test/tmp/friends#{SecureRandom.uuid}.md" }
|
126
|
-
let(:content) { nil }
|
127
127
|
|
128
128
|
before { File.write(filename, content) unless content.nil? }
|
129
129
|
after { File.delete(filename) if File.exist?(filename) }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: friends
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.31'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jacob Evelyn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-06-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chronic
|
@@ -38,48 +38,34 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '2.12'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: memoist
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0.15'
|
48
|
-
type: :runtime
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0.15'
|
55
41
|
- !ruby/object:Gem::Dependency
|
56
42
|
name: paint
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
58
44
|
requirements:
|
59
45
|
- - "~>"
|
60
46
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
47
|
+
version: '2.0'
|
62
48
|
type: :runtime
|
63
49
|
prerelease: false
|
64
50
|
version_requirements: !ruby/object:Gem::Requirement
|
65
51
|
requirements:
|
66
52
|
- - "~>"
|
67
53
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
54
|
+
version: '2.0'
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
56
|
name: semverse
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
72
58
|
requirements:
|
73
59
|
- - "~>"
|
74
60
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
61
|
+
version: '2.0'
|
76
62
|
type: :runtime
|
77
63
|
prerelease: false
|
78
64
|
version_requirements: !ruby/object:Gem::Requirement
|
79
65
|
requirements:
|
80
66
|
- - "~>"
|
81
67
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
68
|
+
version: '2.0'
|
83
69
|
- !ruby/object:Gem::Dependency
|
84
70
|
name: bundler
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|