Coder Social home page Coder Social logo

jigarius / toggl2redmine Goto Github PK

View Code? Open in Web Editor NEW
26.0 10.0 6.0 1.35 MB

Import time from Toggl 2 Redmine.

License: GNU General Public License v3.0

HTML 4.65% CSS 0.92% JavaScript 0.06% Ruby 45.34% Dockerfile 0.18% TypeScript 48.85%
toggl redmine redmine-plugin

toggl2redmine's Introduction

Toggl 2 Redmine

Redmine Version

This dandy Redmine plugin imports time entries from Toggl to Redmine using REST API service calls for both Toggl and Redmine.

Additionally, the plugin groups similar Toggl time entries into a single Redmine entry. So, even if you start and stop your timer for a particular task multiple times on Toggl, at the end of day, when you import the time entries to Redmine, they are grouped by the issue ID and the description, which keeps Redmine clean.

Disclaimer

This plugin has been made and tested with love and care. However, the makers of this plugin are in no way responsible for any damages - direct or indirect - caused by the use of this plugin. In short, use it at your own risk.

Installation

  • Copy the plugin directory into the plugins directory of Redmine.
  • Run database migrations.
    RAILS_ENV=production bundle exec rake redmine:plugins:migrate
  • This creates a Toggl API Token field on the user profile.
  • Your database must support transactions.
    • Without transaction support, users might end up importing duplicate time entries.

Usage

Here's a quick video to get you started.

Toggl 2 Redmine Video Tutorial

If a certain topic is not mentioned in the video, you can find more information on it by reading this document.

One-time Setup

  • Go to the My Account page on Redmine (/my/account).
  • Paste in your Toggl API Token and save your profile.
    • You can find this in your Toggl profile settings page.
  • Update your time zone on Toggl and Redmine - this makes your time reports show correctly according to your timezone.
    • Important: Confirm with your Redmine administrator whether you need to update your timezone. Some organizations use Redmine without configuring timezones to avoid certain timezone-related bugs in Redmine.

Regular Usage

  • Login to Toggl and log your time when you're working.
  • Make sure your task description is in one of the following formats:
#1919 Feed the bunny wabbit.
Tracker #1919 Feed the bunny wabbit.
  • You can use the Toggl browser extension to make this easier.
  • #1919 is the Redmine issue ID.
  • Feed the bunny wabbit is the comment.
  • When you're done working for the day, visit the My Timesheet page on Redmine and click on the Toggl tab on Redmine (/toggl2redmine).
    • Most of the options on this page have useful tooltips. If you are confused about what something does, simply hover over the item to see if it has an informational tooltip.
    • You should see the time you've already logged on Redmine (if any) under the heading Time logged on Redmine.
    • You should see the time you've logged on Toggl for the day under the heading Time logged on Toggl.
    • If you want to import entries from some other date, you can change the Date filter and any other options as per your requirements.
    • If you change any options, make sure you press Apply for them to take effect.
  • Now, in the Toggl report, check the entries you want to import into Redmine.
    • For each entry, you can modify the comments, activity and time as per your requirements.
    • You can enter time as in decimal or as hh:mm. For example, 1h 30m can be written as 1.5 or 1:30 in the input boxes.
  • Once you've reviewed everything, click on the Import to Redmine button towards the bottom of the page.
    • After you import the data, you cannot undo it, so BE CAREFUL.
  • You will see a success (or failure) message next to each item.
    • Entries which imported successfully will be marked in green.
    • Entries which failed to import will be marked in red.

Advanced options

Default Activity

You can specify a Default activity in the options form. This activity will be pre-populated in your Toggl report, making it easier to import data.

Toggl Workspace

If you use multiple workspaces on Toggl, you can choose the workspace from which you want to import data using the Toggl Workspace field in the options form.

Date

As mentioned before, the Date option allows you to import time entries from past dates.

Duration rounding

You can use this option to round your time entries as per your requirements. Let's say, the option to round to the nearest 10 minutes. There are 3 ways in which you can round your time entries.

  • Round Up: 1h 26m becomes 1h 30m.
  • Round Down: 1h 26m becomes 1h 20m.
  • Round Off: 1h 26m becomes 1h 30m whereas 1h 24m becomes 1h 20m.

To disable rounding, you can choose the Don't round option.

Development

Want to fiddle with the code? Or just get a demo of the plugin? If you use Docker, you can do so with ease.

  • Clone the code repository.
    # Replace x.y with a real branch name, e.g. 5.x
    git clone --branch x.y [email protected]:jigarius/toggl2redmine.git
    cd toggl2redmine
    
  • Prepare docker containers.
    docker compose up
    # When Redmine is ready, you'll see a message like:
    # INFO  WEBrick::HTTPServer#start: pid=X port=3000
    # At this point, press Ctrl+C and run the next command.
    docker compose start
    
  • Provision the environment, e.g. create seed data, etc.
    rake provision
    

Run rake info to learn how to access your demo installation!

Testing

Thanks to the Docker setup, the plugin code can easily be linted and tested.

  • rake reset RAILS_ENV=test: Prepare/reset the test environment.
  • rake lint: Run Rubocop.
  • rake test: Run tests.

Mailhog

Mailhog has been included in the Docker setup so that you can easily reset your password or test Toggl 2 Redmine with more than one user accounts.

Acknowledgements

  • Thanks Evolving Web for funding the initial development of this plugin.
  • Thanks Jigarius (that's me) for spending many evenings and weekends to make this plugin possible.
  • Thanks JetBrains for their Open Source License, without which development would have been very difficult.

toggl2redmine's People

Contributors

jigarius avatar wate 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

toggl2redmine's Issues

Possible Improvements

Hide duration rounding minutes if Duration rounding is set to Don't round.

This displays the rounding minutes configuration only when it is relevant.

Improve date formats

Currently we are at the mercy of JavaScript as far as date formats go. A better date format like MMM DD, YYYY will be much better.

Add a better confirmation message for Import to Redmine

Show the user exactly how many hours they have currently logged on Redmine and how many they will have logged after the import. Also, say that the process cannot be undone.

Strikethrough tickets / projects which are closed

If a issue or project is marked as closed, then strike through the issue description to make it clear.

Link Projects to their respective pages

Currently, the user can click on issues, but they cannot click on projects! Maybe make the projects clickable as well?

Disable time logging on closed projects

Toggl2Redmine can bypass permissions and log your time for a project where you are not an authorized user or manager. It can even log time in closed projects.

Provision for rounding up / down activity time

Toggl computes time very accurately, however, it might make more sense to round time up / down to the nearest 5 or 15 minutes.

Proposed solution

When on a time input field,

  • Round up to the nearest 5 minute mark when the user presses the Up key
  • Round down to the nearest 5 minute mark when the user presses the Down key

Track date in URL hash

The problem

  • The user enters a date in the past
  • The user refreshes the page for some reason
    • Observation: The date filter is reset to today's date

Make the date more prominent

Currently the date is only visible in the filter form, which is very tiny. We need it to be more prominent - example, show it in the table hading.

Handle quotation marks in comments correctly

The problem

If you enter a message like Feature #16130 Save installation title as "organization name" in address on Toggl, Redmine gets the comment Feature #16130 Save installation title as, which is incomplete.

Create a link to the Toggl 2 Redmine page

Currently the user has to remember and type in the URL to the toggl 2 redmine page. In the long-run it will be better to have a link to the page somewhere so that the users can click on it and land on the plugin page.

Make plugin compatible with Redmine 4.x

When installing in the redmine:latest docker container, I get the following error:

=> Booting WEBrick
=> Rails 5.2.2 application starting in production on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
W, [2019-03-18T15:15:03.766360 #1]  WARN -- : Creating scope :system. Overwriting existing method Enumeration.system.
/usr/local/bundle/gems/activerecord-5.2.2/lib/active_record/dynamic_matchers.rb:22:in `method_missing': undefined method `attr_protected' for #<Class:0x000055c3cade1320> (NoMethodError)
Did you mean?  attr_reader
        from /usr/src/redmine/plugins/toggl2redmine/app/models/toggl_mapping.rb:14:in `<class:TogglMapping>'
        from /usr/src/redmine/plugins/toggl2redmine/app/models/toggl_mapping.rb:1:in `<top (required)>'
        from /usr/local/bundle/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:291:in `require'
        from /usr/local/bundle/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:291:in `block in require'
        from /usr/local/bundle/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:257:in `load_dependency'
        from /usr/local/bundle/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:291:in `require'
        from /usr/local/bundle/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:378:in `block in require_or_load'
        from /usr/local/bundle/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:37:in `block in load_interlock'
        from /usr/local/bundle/gems/activesupport-5.2.2/lib/active_support/dependencies/interlock.rb:14:in `block in loading'
        from /usr/local/bundle/gems/activesupport-5.2.2/lib/active_support/concurrency/share_lock.rb:151:in `exclusive'
        from /usr/local/bundle/gems/activesupport-5.2.2/lib/active_support/dependencies/interlock.rb:13:in `loading'
        from /usr/local/bundle/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:37:in `load_interlock'
        from /usr/local/bundle/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:356:in `require_or_load'
        from /usr/local/bundle/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:334:in `depend_on'
        from /usr/local/bundle/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:246:in `require_dependency'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/engine.rb:478:in `block (2 levels) in eager_load!'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/engine.rb:477:in `each'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/engine.rb:477:in `block in eager_load!'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/engine.rb:475:in `each'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/engine.rb:475:in `eager_load!'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/engine.rb:356:in `eager_load!'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/application/finisher.rb:69:in `each'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/application/finisher.rb:69:in `block in <module:Finisher>'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/initializable.rb:32:in `instance_exec'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/initializable.rb:32:in `run'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/initializable.rb:61:in `block in run_initializers'
        from /usr/local/lib/ruby/2.6.0/tsort.rb:228:in `block in tsort_each'
        from /usr/local/lib/ruby/2.6.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
        from /usr/local/lib/ruby/2.6.0/tsort.rb:431:in `each_strongly_connected_component_from'
        from /usr/local/lib/ruby/2.6.0/tsort.rb:349:in `block in each_strongly_connected_component'
        from /usr/local/lib/ruby/2.6.0/tsort.rb:347:in `each'
        from /usr/local/lib/ruby/2.6.0/tsort.rb:347:in `call'
        from /usr/local/lib/ruby/2.6.0/tsort.rb:347:in `each_strongly_connected_component'
        from /usr/local/lib/ruby/2.6.0/tsort.rb:226:in `tsort_each'
        from /usr/local/lib/ruby/2.6.0/tsort.rb:205:in `tsort_each'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/initializable.rb:60:in `run_initializers'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/application.rb:361:in `initialize!'
        from /usr/src/redmine/config/environment.rb:14:in `<top (required)>'
        from config.ru:3:in `require'
        from config.ru:3:in `block in <main>'
        from /usr/local/bundle/gems/rack-2.0.6/lib/rack/builder.rb:55:in `instance_eval'
        from /usr/local/bundle/gems/rack-2.0.6/lib/rack/builder.rb:55:in `initialize'
        from config.ru:in `new'
        from config.ru:in `<main>'
        from /usr/local/bundle/gems/rack-2.0.6/lib/rack/builder.rb:49:in `eval'
        from /usr/local/bundle/gems/rack-2.0.6/lib/rack/builder.rb:49:in `new_from_string'
        from /usr/local/bundle/gems/rack-2.0.6/lib/rack/builder.rb:40:in `parse_file'
        from /usr/local/bundle/gems/rack-2.0.6/lib/rack/server.rb:319:in `build_app_and_options_from_config'
        from /usr/local/bundle/gems/rack-2.0.6/lib/rack/server.rb:219:in `app'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/commands/server/server_command.rb:27:in `app'
        from /usr/local/bundle/gems/rack-2.0.6/lib/rack/server.rb:354:in `wrapped_app'
        from /usr/local/bundle/gems/rack-2.0.6/lib/rack/server.rb:283:in `start'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/commands/server/server_command.rb:53:in `start'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/commands/server/server_command.rb:147:in `block in perform'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/commands/server/server_command.rb:142:in `tap'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/commands/server/server_command.rb:142:in `perform'
        from /usr/local/bundle/gems/thor-0.20.3/lib/thor/command.rb:27:in `run'
        from /usr/local/bundle/gems/thor-0.20.3/lib/thor/invocation.rb:126:in `invoke_command'
        from /usr/local/bundle/gems/thor-0.20.3/lib/thor.rb:387:in `dispatch'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/command/base.rb:65:in `perform'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/command.rb:46:in `invoke'
        from /usr/local/bundle/gems/railties-5.2.2/lib/rails/commands.rb:18:in `<top (required)>'
        from bin/rails:4:in `require'
        from bin/rails:4:in `<main>'
W, [2019-03-18T15:15:03.929886 #1]  WARN -- : Creating scope :sorted. Overwriting existing method Group.sorted.
W, [2019-03-18T15:15:03.934397 #1]  WARN -- : Creating scope :sorted. Overwriting existing method User.sorted.
Exiting

My Dockerfile looks like this:

FROM redmine:latest

RUN cd /usr/src/redmine/plugins \
    && git clone https://github.com/jigarius/toggl2redmine.git

/admin/info shows the following (without this plugin installed):

Environment:
  Redmine version                4.0.2.stable
  Ruby version                   2.6.1-p33 (2019-01-30) [x86_64-linux]
  Rails version                  5.2.2
  Environment                    production
  Database adapter               Mysql2
  Mailer queue                   ActiveJob::QueueAdapters::AsyncAdapter
  Mailer delivery                smtp
SCM:
  Subversion                     1.9.5
  Mercurial                      4.0
  Bazaar                         2.8.0
  Git                            2.11.0
  Filesystem                     
Redmine plugins:
  no plugin installed

Any ideas?

Improve Toggl description detection

  • Handle multiple spaces after ticket ID Issue #123 Whatever the comment
  • Trim comments
  • When issue ID is detected but not found in Redmine, show the issue ID in the error message or the row so the user knows which issue is being talked about

Allow users to edit hours

Alex's suggestion: Allow users to edit the duration of Toggl entries before importing them to Redmine.

Bulk import by date range

Are there any plans for bulk import support? I would like to have possibility to import one month or 2 weeks in one batch.

Put all invalid records at the bottom

Time entries from Toggle for which a relevant Redmine issue cannot be found should also appear after all valid entries. Currently, only the Toggl entries which don't have an issue ID sink to the bottom.

Improve time input and handling

The problem

  • There is a difference between the total time displayed as to be imported and the actual time imported to Redmine.

Sometimes the Toggl entries appear twice in the Toggl table

Bug summary

Opened Toggl2Redmine, changed date and applied filter. Saw the Toggl table to have all entries twice.

To Reproduce
Steps to reproduce the behavior: Unknown. It happened only once so far.

Expected behavior
All entries in the Toggl table should only appear once.

Screenshots
Don't have one.

Device information

  • Type: Desktop
  • OS: Mac OS
  • Browser Chrome
  • Version 73.0

Additional context
None.

Use AJAX requests instead of SJAX

Use asynchronous requests with jQuery.ajax() instead of always doing async: false. That way, Redmine will automatically show a loader on the page when a request is in progress.

Hide issue information from non-members of a project

To Reproduce
Steps to reproduce the behavior:

  • Say a person doesn't have access to see a project.
  • The ticket ID 123 belongs to that project.
  • If the person creates a time entry on Toggl with ticket ID 123, then they can see the title of the ticket on the Toggl 2 Redmine page in Redmine

Expected behavior
Since the person doesn't belong to the project, they shouldn't see any information about the issue 123.

Device information
All

Keep a log of what is imported

  • Maintain a map of Toggl time entry IDs with Redmine time entry IDs.
  • If a Toggl time entry is already imported, do not let the user import it again.

Make all requests to Toggl from server-side, not client-side

Making requests to Toggl from the client side requires the Toggl token to be available at the JavaScript level. To remove this necessity, simply make all requests to Toggl from Ruby and not using AJAX. Only some such requests are left now. This might even eliminate the need for domain whitelisting for CORS.

Create Toggl workspace filter

To keep time entries organized, Toggl offers the option to manage separate workspaces. We need to implement a filter which would allow users to see only time entries from a specific Toggl workspace.

Closed issues turn up as unidentified issues

  • When an issue is closed, it shows up without an issue ID.
  • Use status_id=* in the request to get all issues
  • Show a proper error message saying "the issue is closed so you cannot log time".

Incorrect last imported date when someone logs time in the future

Bug summary
If you've logged time in the future for some reason, the "last imported date" shows the future date.

To Reproduce
Steps to reproduce the behavior:

  1. Log time in the future
  2. Visit the Toggl 2 Redmine page
  3. The "last imported date" next to the date filter should be in the future now.

Expected behavior
The last imported date should show the last date on which time was imported / logged.

Device information

  • Type: All
  • OS: All

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.