GithubHelp home page GithubHelp logo

hours's Introduction

Hours

Build Status

A minimal CLI for tracking time. Easily track the time you spend on personal tasks or client projects. Generate beautiful reports.

All your data is yours and yours only - we never send any data to the cloud or a remote server.

asciicast

Quick Start

Installation

Hours is written in PHP. You will need a PHP version >= 7.2. You can check this easily with the php --version command. If you don't have PHP installed follow the full installation guide.

Once you have PHP installation you can easily download the executable from Github:

wget https://github.com/matt-allan/hours/releases/latest/download/hours -O hours
chmod a+x ./hours
mv ./hours /usr/local/bin/hours

Usage

To begin tracking time use the start command. You can specify the name of the project to track time for, as well as any tags or notes you would like to add.

$ hours start blog --tag writing --notes 'Updating the about page'
Starting frame for blog (writing) at 12:26 pm

This will create a "frame". Once you have finished your task you can stop tracking time with the stop command.

$ hours stop                                                      
Time tracking for blog stopped (started 1 hour ago).

You can view all of your frames with the report command.

$ hours report                                      
May 1, 2019 to May 7, 2019
+---------+---------+-------------------------+-------------+----------+----------+---------+
| Project | Tags    | Notes                   | Date        | Start    | End      | Elapsed |
+---------+---------+-------------------------+-------------+----------+----------+---------+
| blog    | writing | Updating the about page | May 7, 2019 | 12:26 pm | 1:26 pm  | 1:00    |
+---------+---------+-------------------------+-------------+----------+----------+---------+
Total hours: 1:00

That should be enough information to get you started. To learn more check out the user guide.

Contribute

Are you interested in helping out with development? Check out our contributor's guide to get started.

License

Hours is released under the MIT License. See the bundled LICENSE file for details.

hours's People

Contributors

cmgmyr avatar dependabot[bot] avatar douglasdc3 avatar matt-allan avatar peter279k avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

hours's Issues

add command - automatically detect --to am/pm

Given:

  • I am using a 12 hour clock format
  • I call the add command
  • I specify the --from option with am or pm
  • I don't specify am or pm for the --to option

...then the to date should default to the closest possible time.

For example, when I specify --from '8:00 pm' --to '9:00' the to time becomes 9:00 am:

$ php hours add blog --from '8:00 pm' --to '9:00'
Added frame for blog from May 6, 2019 8:00 pm to May 6, 2019 9:00 am (11 hours).

...it's safe to assume I probably meant 9:00 pm and I only worked one hour rather than working through the night. If this feature was implemented I would see this instead:

$ php hours add blog --from '8:00 pm' --to '9:00'
Added frame for blog from May 6, 2019 8:00 pm to May 6, 2019 9:00 pm (1 hour).

We don't want to default to the start time's am/pm designation as that results in unexpected behavior when starting work just before noon, i.e.

$ php hours add blog --from '11:00 am' --to '1:00'   
Added frame for blog from May 6, 2019 11:00 am to May 6, 2019 1:00 am (10 hours).

Add --at flag to the stop command

Sometimes I forget to stop time tracking. When I realize this and stop the current frame I have two options:

  • stop the frame, then manually edit the stop time
  • cancel the frame, then go back and add the frame manually with the correct start and stop times.

I think it would be a lot simpler if I could pass the --at flag, just like I can with the start command like this:

hours stop --at '15 minutes ago'

This can use the dateOption macro similar to how the add command works.

Make user config easier to override in tests

The user configuration is stored in the XDG home configuration directory. When the tests run they use the normal configuration directory, which will use the user's actual configuration if it exists.

The problem with this is you can accidentally overwrite your own config or make the tests fail if you have a non-default setting and the test expects the default.

You can override this with the WithoutConfig trait but that is annoying - you end up having to add that trait to every test and if you forget you can overwrite your config.

There are a few options to prevent this:

  • Set the XDG_CONFIG_HOME and XDG_DATA_HOME environment variables in phpunit.xml.dist to testing directories (i.e. storage/framework/testing/disks/config, storage/framework/testing/disks/data).
  • Add a CONFIG_DRIVER environment variable that is used to determine which config driver is bound in the container. Default to file & set it to memory for testing.
  • Call the withoutConfig method automatically in TestCase::setUp.

Not sure which one is the cleanest/most obvious right now.

Install Error: 'failed to open stream: No such file or directory'

Describe the bug
Followed install instructions but got the following error when running the final command

Sams-MBP-2:~ samparkewolfe$ hours --version

In Filesystem.php line 122:

file_put_contents(/Users/samparkewolfe/.local/share/Hours/database.sqlite): failed to open stream: No such file or directory

To Reproduce
Steps to reproduce the behavior:
Just follow the installation instructions.

Desktop (please complete the following information):

  • OS: Mac OSX
  • Version: High Sierra

Additional context
I have the requested version of php and sqlite3 is an extention.

2 things

While trying to dig deep into your code:

  • grep -rn 'if.*(.*!' app/ lists all typecastings: the last one is the only proper one if (! $project instanceof Project)
  • in app/Report.php:120 new CarbonInterval(null) may not be a good idea

frame:edit command

You should be able to easily edit frames and do things like:

  • change the start time
  • change the stop time
  • change tags
  • change notes
  • delete the frame

Unlike projects and tags you can't easily reference a frame by name. We could allow editing frames by ID but that would require you to first do frame:list, remember the id, and then frame:edit.

Instead we can probably make an interactive editor using Zero's interactive menus?

I'm imagining something like this:

$ hours frame:edit
Project
--------------------------------------
○ blog
○ school
Project > blog > Frames
--------------------------------------
○ May 4, 2019, 7:32 pm - 7:42 pm - editing env article [editing, proofreading]
○ May 4, 2019, 9:00 pm - 10:10 pm - publishing env article [publishing]
Project > blog > Frames > 93
--------------------------------------
○ Edit
○ Delete
Project > Blog > Frames > 93 > Edit
--------------------------------------
○ Start Time
○ Stop Time
○ Tags
○ Notes

Add frame:list and frame:forget commands

We already have project:list, project:forget, tag:list, and tag:forget commands. We should add frame:list and frame:forget commands too.

Sometimes I forget to stop a frame. When I remember to stop it a day later I want to go back and delete the frame so I can use frame:add to add one with the correct time.

At the moment I have to manually delete the frame using the sqlite CLI. Ideally I could do this instead:

$ hours frame:list
+----+----------+----------+-------+--------------+----------+---------+---------+
| ID | Project  | Tags     | Notes | Date         | Start    | End     | Elapsed |
+----+----------+----------+-------+--------------+----------+---------+---------+
| 1  | hrs-docs |          |       | May 9, 2019  | 11:27 am | 1:23 pm | 1:55    |
+----+----------+----------+-------+--------------+----------+---------+---------+
$ php hours frame:forget 1
Frame deleted.

The frame:list command should work similarly to the existing project:list command. The table should have the columns from the example above. The frames should be ordered by created_at in descending order (most recent first).

Eventually we should probably limit the results and let you paginate or filter or something but for now it's fine to just list all of them.

The frame:forget command should accept an ID and delete the frame with that ID.

Fatal error

Uncaught Symfony\Component\Debug\Exception\FatalThrowableError: Class 'Illuminate\Config\Repository' not found in vendor/laravel-zero/foundation/src/Illuminate/Foundation/Bootstrap/LoadConfiguration.php:36

my .env file is only DB_DATABASE=hours.sqlite

the trick is Illuminate\Config\Repository does exist

Could you help?

Shell completion

It would be useful to generate an autocompletion file that the user could add to their shell. Once added they could start typing and press tab to have available commands and options completed.

This could be implemented as completion command that prints the completion file to sdout. The user would pipe this to the appropriate place for their shell, i.e.

php hours completion > /etc/bash.completion.d/hours

The --shell option could be used to specify which shell they need completion for (default to bash), i.e.

php hours completion --shell zsh > /usr/local/share/zsh/site-functions/_hours

As a starting point for the completion script you could look at existing completion scripts for composer, artisan, or other popular symfony console applications.

Most of them use the included list command to list the available commands and parse the output for command completion. Here's an example artisan completion for zsh.

Frame meta

This issue builds on #41. You should be able to add custom metadata to frames. You can then use the metadata in reports.

# add custom meta fields
hours meta:add sprint
hours meta:add estimate --interval
hours meta:add velocity --computed 'estimate / elapsed'
hours meta:add points --number

# Track time with meta fields
hours start --tag migrations --notes 'adding emails table' --sprint 'bulbasaur' --estimate '20 minutes' --points 2

# Use meta in reports
hours report --sprint='bulbasaur'

Metadata can be defined as one of the following types:

  • string (default)
  • boolean
  • number
  • interval
  • datetime
  • timestamp
  • computed (will clarify in a separate issue)

Any field can optionally be variadic, which corresponds to passing multiple flags (i.e. how tags work now).

The field type is used to determine how it's (de)serialized for storage and how it's parsed from the CLI input.

Challenges:

Adding the meta option flags at runtime will be challenging, and will require additional database queries every time you run a command that supports meta. If we use the meta field name as the option name (and don't namespace them) adding any options in the future will technically be a breaking change.

Ideally we could store the meta as a JSON column, but to query JSON (i.e. for reports) requires the JSON1 extension for SQLite. Some PHP installations don't load this extension. We might be able to compile and distribute the shared library? It does work with Laravel, so it's a viable option.

I really don't want to have to do an EAV table for this, but that's an option too. Since SQLite doesn't have real date/time types every value would either be integer, real, or text.

Command help text

Every command should have help text that you can see when you use the --help flag.

Currently only the add command has help text. To see what it looks like you can run php hours add --help.

To add help text you need to define a protected $help property on the command, then override the constructor to set the help text like this:

public function __construct()
{
    parent::__construct();

    $this->setHelp($this->help);
}

For an example take a look at the AddCommand.

Extensions

Is your feature request related to a problem? Please describe.

I'd like to be able to use this tool for evidence based scheduling. To do that I need to be able to:

  • Associate estimates with tasks.
  • Calculate velocity for each task (divide estimated time by actual time)

I don't need to do the reporting / estimating part within hours. Instead I can find the velocity of similar tasks and input that into whatever I end up using to run the Monte Carlo simulations. ...But it might be nice to run reports like 'the average velocity for tasks with the tag 'migrations'.

Describe the solution you'd like

This tool should be a general purpose time tracking tool; not an evidence based scheduling tool. So we need to allow extending it with features that aren't built into the core.

This particular feature could probably be supported with a custom field API. So maybe something like:

# add custom meta fields
hours meta:add estimate --interval
hours meta:add velocity --computed 'estimate / elapsed'

# Track time with meta fields
hours start --tag migrations --notes 'adding emails table' --estimate '20 minutes'

# report with an aggregate function on meta fields
hours report --aggregate='AVG(velocity)' --tag=migrations

Describe alternatives you've considered

Alternatively, since it's written in a scripting language it isn't too hard to add extensions. So maybe we add an extension API that lets you run arbitrary PHP code (i.e. installed via Composer). The downside of this is of course you need to know PHP. The upside is there's a lot more flexibilty and we don't have to write an expression language.

Additional context

Allow including open frames in the report

When I'm trying to hit a target i.e. 20 hours per week I will check the report during the day to see how many hours I have left.

$ hours report -f 'this week'

However the report doesn't include the hours I have put in today on my open frame so I either need to check hours status and manually do the math or close the frame and run the report before restarting the frame.

It would be nice if there was a flag on the report command so you could include open frames i.e. hours report -f 'this week' --with-open-frames. The report would show elapsed time from the start to now and the end time would be empty. The total would include the elapsed time of the open frame.

Automatically determine the current project

The add and start commands currently require a project argument. It would be really helpful if the project name could be automatically determined. There are a few ways we could do this.

Default project: Some users may only be tracking time for a single project and they don't want to have to specify it every time. For this scenario we could allow the user to specify a default_project in their config file. If the project name is not specified the default is used.

Current directory: Some users may want to track time based on the current directory. For example, my blog is in the blog folder - when I want to track time for the blog I can just execute hours start from that folder and it will start tracking time for the blog.

Dotfile: Another option is using a dotfile named .hours which contains the project name. If the current working directory has .hours dotfile we read the project name from that.

Some of these rules can't be ran at the same time without some kind of priority - if you have a default do we use that or the current directory? This could be configurable but maybe we should just decide on a default priority and use that.

add command - test automatic date detection

The add command will default to today if you don't specify a date. For example:

$ php hours add blog --from '11:00 am' --to '1:00 pm'
Added frame for blog from May 6, 2019 11:00 am to May 6, 2019 1:00 pm (2 hours).

This is a great feature but we don't currently have a test that guarantees this continues to work.

It would be helpful if we had a feature test that tested the aforementioned scenario.

To write the test you can add a new method to the AddCommandTest.

Pomodoro support

The start command should support a --p|pomodoro option flag. If the flag is enabled the app will start tracking time using the pomodoro technique.

The command will create the frame like normal, but then instead of existing begin a loop. The loop will run for 25 minutes, then close the active frame and send a notification.

After 5 minutes a new frame is created which inherits the tags, notes etc of the previous frame (see RestartCommand for guidance on how to do this). Another notification is sent letting the user know the break is over.

The loop repeats ad infinitum. Every 4th break is 20 minutes instead of 5 minutes.

Future Scope

The following features aren't necessary but would be nice to add eventually.

Configurable times

Instead of hardcoding the length of pomodoros and breaks use the configuration settings pomodoro.time, pomodoro.break_time, pomodoro.long_break_time, and pomodoro.break_interval.

Signal Handling

When the user hits ctrl+c it will kill the process, which by default would keep the current frame open. It's safe to assume you want to close the frame, so we should trap the signal and close the frame on SIGINT. This can be implemented using pcntl_signal and pcntl_async_signals(true);, but should only be done if the pcntl extension is loaded. This feature isn't necessary for the initial version and can be added later.

Setup CI

Setup Travis CI. Travis should run the following commands on master & pull requests:

  • ./vendor/bin/php-cs-fixer fix --dry-run --diff
  • ./vendor/bin/phpunit --testsuite 'Unit'
  • ./vendor/bin/phpunit --testsuite 'Feature'

Write docs

Write full documentation for both users and contributors. Instead of stuffing everything into the README we should add a docs directory that stores markdown files. We can build this using Github Pages eventually.

Rough outline:

  • Overview
  • Getting Started
  • Commands
  • Configuration
  • FAQ
  • Contributing
    • How to
    • Pull requests
    • Internals
    • Releases

PDF report renderer

It would be useful to render reports as PDF so you could attach them to invoices. I would probably use dompdf and blade templates for this.

Blade isn't included with laravel-zero so we would need to add it. This would incidentally make the laravel-ide-helper work as it expects blade to be registered.

It should be possible to define a custom blade template in the config directory that overrides the default template.

This would write to stdout like the rest of the renderers so the user would need to pipe it, i.e.

php hours report --format pdf > report.pdf

This will require a new implementation of the Renderer interface and a new factory method in the RendererManager.

Contribution guidelines / docs

  • Add .github/contributing.MD
  • Add .github/ISSUE_TEMPLATE.md
  • Add .github/PULL_REQUEST_TEMPLATE.md
  • Add composer test script
  • Document internals

It would also be nice to setup a discord server.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.