Code Monkey home page Code Monkey logo

organize's Introduction

organize v3 is out


organize - The file management automation tool
Full documentation at Read the docs

v3 is now available

The new version should be much faster and fix a lot of bugs. It also comes with a some new actions, filters and options.

If you encounter any other bugs or problems during the migration, please reach out!

About

Your desktop is a mess? You cannot find anything in your downloads and documents? Sorting and renaming all these files by hand is too tedious? Time to automate it once and benefit from it forever.

organize is a command line, open-source alternative to apps like Hazel (macOS) or File Juggler (Windows).

People use this for:

  • Sorting and tagging pictures into various folder structures based on EXIF data
  • Sorting and renaming PDF invoices based on file content
  • Removing incomplete downloads from their ~/Downloads
  • Cleaning up their ~/Desktop from unused files
  • Freeing up disk space by removing duplicates
  • Automating various business processes
  • and many more

Features

Some highlights include:

  • Safe moving, renaming, copying of files and folders with conflict resolution options.
  • Fast duplicate file detection.
  • Exif tags extraction.
  • Categorization via text extracted from PDF, DOCX and many more.
  • Powerful template engine.
  • Inline python and shell commands as filters and actions for maximum flexibility.
  • Everything can be simulated before touching your files.
  • Works on macOS, Windows and Linux.
  • Free and open source software.

Getting started

Installation

Only python 3.9+ is needed. Install it via your package manager or from python.org.

Installation is done via pip. Note that the package name is organize-tool:

pip install -U organize-tool

This command can also be used to update to the newest version. Now you can run organize --help to check if the installation was successful.

Create your first rule

In your shell, run organize new and then organize edit to edit the configuration:

rules:
  - name: "Find PDFs"
    locations:
      - ~/Downloads
    subfolders: true
    filters:
      - extension: pdf
    actions:
      - echo: "Found PDF!"

If you have problems editing the configuration you can run organize show --reveal to reveal the configuration folder in your file manager. You can then edit the config.yaml in your favourite editor.

save your config file and run:

organize run

You will see a list of all .pdf files you have in your downloads folder (+ subfolders). For now we only show the text Found PDF! for each file, but this will change soon... (If it shows Nothing to do you simply don't have any pdfs in your downloads folder).

Run organize edit again and add a move-action to your rule:

actions:
  - echo: "Found PDF!"
  - move: ~/Documents/PDFs/

Now run organize sim to see what would happen without touching your files.

You will see that your pdf-files would be moved over to your Documents/PDFs folder.

Congratulations, you just automated your first task. You can now run organize run whenever you like and all your pdfs are a bit more organized. It's that easy.

There is so much more. You want to rename / copy files, run custom shell- or python scripts, match names with regular expressions or use placeholder variables? organize has you covered. Have a look at the advanced usage example below!

Example rules

Here are some examples of simple organization and cleanup rules. Modify to your needs!

Move all invoices, orders or purchase documents into your documents folder:

rules:
  - name: "Sort my invoices and receipts"
    locations: ~/Downloads
    subfolders: true
    filters:
      - extension: pdf
      - name:
          contains:
            - Invoice
            - Order
            - Purchase
          case_sensitive: false
    actions:
      - move: ~/Documents/Shopping/

Recursively delete all empty directories:

rules:
  - name: "Recursively delete all empty directories"
    locations:
      - path: ~/Downloads
    targets: dirs
    subfolders: true
    filters:
      - empty
    actions:
      - delete

You'll find many more examples in the full documentation.

Command line interface

organize - The file management automation tool.

Usage:
  organize run   [options] [<config>]
  organize sim   [options] [<config>]
  organize new   [<config>]
  organize edit  [<config>]
  organize check [<config>]
  organize debug [<config>]
  organize show  [--path|--reveal] [<config>]
  organize list
  organize docs
  organize --version
  organize --help

Commands:
  run        Organize your files.
  sim        Simulate organizing your files.
  new        Creates a new config.
  edit       Edit the config file with $EDITOR.
  check      Check whether the config file is valid.
  debug      Shows the raw config parsing steps.
  show       Print the config to stdout.
               Use --reveal to reveal the file in your file manager
               Use --path to show the path to the file
  list       Lists config files found in the default locations.
  docs       Open the documentation.

Options:
  <config>                        A config name or path to a config file
  -W --working-dir <dir>          The working directory
  -F --format (default|jsonl)     The output format [Default: default]
  -T --tags <tags>                Tags to run (eg. "initial,release")
  -S --skip-tags <tags>           Tags to skip
  -h --help                       Show this help page.

Other donation options:

ETH:

0x8924a060CD533699E230C5694EC95b26BC4168E7

BTC:

39vpniiZk8qqGB2xEqcDjtWxngFCCdWGjY

organize's People

Contributors

chenrui333 avatar dependabot[bot] avatar elsatch avatar fgma avatar florianfritz avatar gaby avatar germanfab avatar hernandor avatar nicoandmee avatar pre-commit-ci[bot] avatar revpc18 avatar tfeldmann avatar white-gecko avatar win0err avatar xdhmoore avatar zor-el 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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

organize's Issues

Change missing folder from hard error to warning

Could Organize be updated to handle folders in the config file that are missing from disk without hard crashing the execution? Today, it throws a hard error as such:

ERROR: Path not found: /source/abc/def)
Full traceback at: /root/.cache/organize/log/organize.log

I use a service called FileThis to automatically download statements from several companies I do business with. The folder names sometimes change so I have to keep updating the rules file to even get it to run.

One thought would be to have it output a "WARNING" style message in the output and in the logs with the missing folder info, but continue processing other folders.

For instance, in the following config, if /source/abc/def is missing, we'd get a WARNING message but it would continue to process /source/abc/xyz.

utility_folders: &utility
- '/source/abc/def'
- '/source/abc/xyz'

Folder instructions in filename?

I would like to include path/folder-instructions into the filename because I have a lot of different files (and there are always new categories added) I don't want create rules for. For example my filename is '2019_Jobs_CategoryA_TagB_A-Media_content-name_V01_draft_eng.docx' which means: Move the file to the folder '2019/Jobs/CategoryA/TagB/Media/drafts/eng' whereby 'A-' is an additional instruction and should be removed from the filename afterwards ('2019_Jobs_CategoryA_TagB_content-name_V01_draft_eng.docx').

I have a rough idea to figure it out with python but I'm new to it (see below a sketch). Is there a possibility to use such variables, conditions etc. with organizer natively? If no, is it possible to do it with Python in Organizer at all?

  1. Transform file-string into array
  2. Search for 'A-...', 'V...' and 'content-name' and get index of values
  3. remove value 'A-... and 'content-name' of array
  4. build new filename string
  5. remove value 'V...' and 'A-' of array
  6. build folder-path string (convert _ to /) etc.

Filter: Expose Creation/Modification Year

Hi Thomas,

Organize version: 1.7.0

first of all: Thank you for creating organize! I really like the simple and readable configuration syntax!
I was trying to organize my expenses folder, which looks like this:

├── 2018
├── 2019
├── 2020
├── Eigenbeleg-Vorlage.docx
└── unsorted

What I want to do is compress the folders for the previous year(s). I tried with a rule like this:

  - folders:
      - '~/orga/expenses'
    filters:
      - created:
          years: 1
    actions:
      - echo: 'Folder is ready to be archived {path}'

Unfortunately created and lastmodified do not have a years parameter:

ERROR: __init__() got an unexpected keyword argument 'years'
Full traceback at: /home/er4z0r/.cache/organize/log/organize.log

This struck me as inconsistent given that there are params for the smaller time units.

Could you please add the years parameter to lastmodified and created filters

Multiple problems with the PY code ...

on Linux KDE Kubuntu 18.04

When I try to run a configuration with some simple example like the ones you provide on your web page, the program returns errors, among others, for example:

rules:

  • folders:'˜/Desktop'
    filters:
    • LastModified:
      • days: 10
        actions:
      • Echo: 'Was modified at least 10 days ago'

Return me...

ERROR! unsupported type for timedelta days component: dict

into last_modified.py file code appear (lines 82 and 83 on my file)...

self.timedelta = timedelta(
days=days, hours=hours, minutes=minutes, seconds=seconds)

with others examples others error too. Thanks.

Way to operate on folders instead of files?

Hi! First of all, I'm very happy organize exists and and eager to contribute some features that I personally need. if you prefer to keep this project sweet and simple that is fine too of course!

Anyways, here I go with another feature request that I will attempt to build.

Consider the following use case:

# move stale projects to archive
  - subfolders: true
    folders:
      - '~/projects/'
    filters:
      - LastModified:
          days: 90
          mode: older
    actions: 
      - Move: /mnt/raid/archive-projects/{relative_path}
    subfolders: true

This technically works but has the following downsides:

  • Files trickle over depending on their individual LastModified date. The level of annoyance from this depends on the nature of your projects.
  • Empty folder structure gets left behind. I feel like removing empty folders should definitely be a something organize can do.

Hazel has a feature to match folders specifically (see screenshot below). Another valid value for the Kind field is Music, which switches the list of available filters to ones applicable to music files or any kind as well as generic ones.
screenshot_2018-11-20_12-55-43

Now in the case of organize, I would suggest adding one relatively simple flag to the rule that will make the rule target folders only instead of files only:

# move stale projects to archive
rules:
  - target_folders: true
    folders:
      - '~/projects/'
    filters:
    - LastModified:
        days: 90
        mode: newer
    actions: 
      - Move: /mnt/raid/archive-projects/{relative_path}

By adding 3 lines of code https://github.com/mope1/organize/commit/8bcfe3502e984f11d6799da475072d0098d3d9bd the flag can be implemented and it already works with LastModified filter and Move action without further changes.

Now obviously some filters and actions might be more tricky, but imho if you defaulted the filter class to never match folders (or maybe error out if not implemented) when applied to folders, this feature would be quite useful. I could imagine e.g. a filter called Empty that works on both files and folders as well.

UPDATE Turns out i'm less clever than i tought, as modifiying a file does not update the parent directory's DateModified, only the creation of new files. Therefore, more extensive changes would have to be made to the filters.

Installation issue on Mac

Hello,

I am having an issue installing on Mac: 10.11.6 OS X Ei Capitan

$ pip3 install organize-tool
Collecting organize-tool
  Using cached organize_tool-1.2-py3-none-any.whl
Collecting pyyaml (from organize-tool)
Collecting colorama (from organize-tool)
  Using cached colorama-0.3.9-py2.py3-none-any.whl
Collecting appdirs (from organize-tool)
  Using cached appdirs-1.4.3-py2.py3-none-any.whl
Collecting docopt (from organize-tool)
Collecting clint (from organize-tool)
Collecting Send2Trash (from organize-tool)
  Using cached Send2Trash-1.5.0-py3-none-any.whl
Collecting args (from clint->organize-tool)
Installing collected packages: pyyaml, colorama, appdirs, docopt, args, clint, Send2Trash, organize-tool
Exception:
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/pip/basecommand.py", line 215, in main
    status = self.run(options, args)
  File "/usr/local/lib/python3.6/site-packages/pip/commands/install.py", line 342, in run
    prefix=options.prefix_path,
  File "/usr/local/lib/python3.6/site-packages/pip/req/req_set.py", line 784, in install
    **kwargs
  File "/usr/local/lib/python3.6/site-packages/pip/req/req_install.py", line 851, in install
    self.move_wheel_files(self.source_dir, root=root, prefix=prefix)
  File "/usr/local/lib/python3.6/site-packages/pip/req/req_install.py", line 1064, in move_wheel_files
    isolated=self.isolated,
  File "/usr/local/lib/python3.6/site-packages/pip/wheel.py", line 345, in move_wheel_files
    clobber(source, lib_dir, True)
  File "/usr/local/lib/python3.6/site-packages/pip/wheel.py", line 323, in clobber
    shutil.copyfile(srcfile, destfile)
  File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/shutil.py", line 121, in copyfile
    with open(dst, 'wb') as fdst:
PermissionError: [Errno 13] Permission denied: '/lib/python3.6/site-packages/_yaml.cpython-36m-darwin.so'

Watch folder?

Hello, is there a way to watch folders for changes and then trigger organize?

Maintaining subfolder tree on move action?

Can't seem to figure this one out.

Given a folder structure:
~/Downloads/SomeFontPackage/SomeFolder/font.ttf

I can't figure out how to do a Move with a ttf extension that will let me move to say:
~/Downloads/FONTS/SomeFontPackage/SomeFolder/font.ttf
(the goal being maintaining the folder structure)

I tried messing with the paths, including trying the {path.relative_to('~/Downloads)}, following the PurePath documentation, but I get:

File lily_of_the_valley/Lily of the Valley.ttf:
    - [Move] ERROR! Missing template variable 'PosixPath' object has no attribute 'relative_to('/Users/MYUSER/Downloads')' for "~/Downloads/FONTS/{path.relative_to('/Users/MYUSER/Downloads')}/"

I know you can chain paths and parents, but it seems like a guessing game of "how many parents might an item have."

FWIW my Python(s) are installed via brew on macOS High Sierra:

12:00 $ which python
/usr/bin/python
✔ ~/Downloads
12:04 $ which python3
/Library/Frameworks/Python.framework/Versions/3.6/bin/python3

I'm sure I'm missing something simple. Thanks for your hard work on this! I want to get it going and then just cron it (or equivalent). 😄

You might find this useful

Hey, I made something similar quite a while ago. It supports a few additional filters ("conditions" for Filemaid): term conditions (collections of conditions with a junctor), mime conditions based on the file's detected mimetype, size conditions and something like your regex filter: path conditions (which matches on the whole path instead, if you have a more complicated folder hierarchy).

If you have no interest in this, feel free to close this issue and sorry for the noise. I thought some of the concepts might be interesting to you and easily adaptable to your project.

Shortcuts not registering?

This is an odd one because it seems to be inconsistent. I'm pointing the application to my desktop and asking it to move new .lnk files (shortcuts) somewhere else in my home directory... While it has worked before it often ignores certain shortcuts as if they don't even exist.

Edit
It managed to recognize the "ImageMagick Display" applications shortcut when updating it but I see no discernible difference between "it" and other shortcuts that sit on my desktop.

Feature request: Cancel move action if file exists

Hi!

I would like to request a feature on the move action. I would like to be able to specify the move action to cancel if the matching file already exists on the destination path.

As is now it can make a new file or overwite it, but it can't cancel the operation.

Many thanks for a great program!
Johnny

Encoding Windows

Hey,

I'm trying to use organize with Windows 10/Python 3.7.3 and have a problem with the encoding (ü/ö/ä).

Example:

rules:
  # move Übungen to 'Übungen' folder
  - folders: 'D:\test'
    subfolders: true
    filters:
      - Extension:
        - txt
      - Filename:
          contains : übung
          case_sensitive: false
    actions:
      - Move: 'D:\test\übungen\'

The tools is not finding the "übung.txt".

With the following config it finds the file but the move folder is wrong.

rules:
  # move Übungen to 'Übungen' folder
  - folders: 'D:\test'
    subfolders: true
    filters:
      - Extension:
        - txt
      - Filename:
          contains : bung
          case_sensitive: false
    actions:
      - Move: 'D:\test\übungen\'

organize run gives the following output:

PS C:\Users\jn> organize run
Folder D:\test\:
  File übungen.txt:
    - [Move] Move to "D:\test\übungen\übungen.txt"

The result is the file in a new folder named "übungen"

Maybe some ideas where to look? The file "übung.txt" is displayed correct. Thanks

Cheers,
Jan

File Size Filter

This is a feature request for filtering files by size like so:

rules:
  - folders: '~/Downloads'
    filters:
      - Filesize:
          bigger: 500m
    actions:
      - Trash

I built this already but need to remake the PR for reasons so I might as well get an issue number here and name my branch correctly.

Rename: Remove whitespace from counter

When renaming multiple files to the same filename, a counter is added.

However, the counter automatically consists of "[whitespace]". Could the whitespace be removed?

Filter: File Contents

Hi @tfeldmann,
I like your organize tool. I would however like to see matching for file contents like File Juggler does.

There you can match things like a date found in the document and use it as a pattern for renaming or match for specific Words appearing in the file. This would be extremely useful for automatically sorting OCRed scanned documents.

What do you think?

Filter: Class 'CreationDate'

Hi! Loving your project.

Would it be possible to add a class "Date" which stores the creation date? This could be used as a filter and a var for actions, e.g. when organizing files by date

Rename: {filename}-{date}.{extension}

Preserve path option

It would be very helpful to have an option to replicate the file path from root of the source folder to the destination folder on a move or copy action. I subscribe to a system that downloads statements from over a dozen companies that I do business with and files them into a sub-folder for each account. I'd like to have an automated process run periodically to file those into a folder of the same name, but under the current year so that, at year's end, all the documents for that particular year are filed together

For instance, given the following input file:
/source-folder/my-bank-name/monthly-statement-july.pdf

move to the following destination file:
/destination-folder/2018/my-bank-name/monthly-statement-july.pdf

One idea might be to have a rule similar to the following:

rules:
  - folders: '/source-folder'
    subfolders: true
    filters:
      - Extension:
          - pdf
          - docx
      - LastModified
    actions:
      - Move: '/destination-folder/{lastmodified.year}/'
          - preservepath: true

Another might be even simpler:

rules:
  - folders: '/source-folder'
    subfolders: true
    filters:
      - Extension:
          - pdf
          - docx
      - LastModified
    actions:
      - Move: '/destination-folder/{lastmodified.year}/{path}/'

Flatten filter and action lists for code reusability

YAML has a merge operator for combining dictionaries, but it does not have a native way of merging ordered lists.

I see that you already have "Flatten filter lists?" in your Roadmap, and I would suggest that you do the same for actions to allow for code reusability in the config file.

Often filters and actions have many parts in common between rules. Instead of repeating the same filters and actions everywhere, flattening filters and actions lists can allow one to do something like this:

folder_aliases:
  Downloads: &downloads ~/Downloads/
  Payables_due: &payables_due ~/PayablesDue/
  Payables_paid: &payables_paid ~/Accounting/Expenses/
  Receivables_due: &receivables_due ~/Receivables/
  Receivables_paid: &receivables_paid ~/Accounting/Income/

regex_patterns:
  long_date: &date_long '(?P<date>\d{1,2}\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\w{,8}\s20\d{2}))'
  any_date: &date_any "(?P<date>\
    (\\d{1,2}.{,3}(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\w{,8}.{,3}20\\d{2})|\
    ((([0-2]?\\d)|(3[01])).((0\\d)|(1[0-2])).((20)?\\d{2}))\
    )"

python_code:
  timestamp_and_quarter: &timestamp |
    from dateutil.parser import parse
    dat = parse(filecontent.date)
    ts = dat.strftime('%Y%m%d')
    q = dat.strftime('%Y') + 'Q' + str((2 + int(dat.month)) // 3)
    return {'timestamp': ts, 'quarter': q}

defaults:
  filters: &default_filters
    - extension: pdf
    - filecontent: *date_any
    - python: *timestamp
  actions: &default_actions
    - echo: 'Dated: {filecontent.date}'
    - echo: 'Stem of filename: {filecontent.stem}'
    - echo: 'Timestamp: {python.timestamp}'
    - echo: 'Quarter: {python.quarter}'
  post_actions: &default_sorting
    - rename: '{python.timestamp}-{filecontent.stem}.{extension.lower}'
    - move: '{path.parent}/{python.quarter}/'

Rule_in_development: &DEV
  enabled: false
  actions: *default_actions

Rule_stable: &PROD
  enabled: true

rules:
  - <<: *PROD
    folders: *downloads
    filters: 
      - *default_filters
      - filecontent: '' # regex to id as payable
      - filecontent: '(?P<stem>...)' # regex to extract supplier
    actions: 
      - *default_actions
      - move: *payables_due
      - *default_sorting

  - <<: *PROD
    folders: *downloads
    filters: 
      - *default_filters
      - filecontent: '' # regex to id as receivables due
      - filecontent: '(?P<stem>...)' # regex to extract customer
    actions: 
      - *default_actions
      - move: *receivables_due
      - *default_sorting

  - <<: *PROD
    folders: *downloads
    filters: 
      - *default_filters
      - filecontent: '' # regex to id as receivables paid
      - filecontent: '(?P<stem>...)' # regex to extract customer
      - filecontent: '(?P<paid>...)' # regex to extract date paid
    actions: 
      - *default_actions
      - move: *receivables_paid
      - *default_sorting
      - rename: '{filecontent.paid}_{filecontent.stem}.{extension}'

Having all the code at the top of the config file and reducing duplication makes it easier to debug.

Cheers!

PS: I have only just discovered your tool and am already finding it incredibly useful!

Cannot access Plug-n-Play Devices

Hi everyone,
Just a Bug I wanted to report, you cannot organize any Plug-n-Play devices.I believe this would also effect any secondary hard drives.

I have been attempting to use this tool to help organize my USB stick(s) and have discovered that the system cannot actually identify the folder the USB stick is:
(/media/[USERNAME]/[USB NAME]/filesAndFolders)

Classification:
Bug / Feature Request Combo

tl;dr : You cant organize USB sticks

Long term - GUI plans

This project seems to be something I have been waiting for for years. Its probably way too early in the life cycle to ask this but have you have plans for a GUI? Thank you for your work

How to act on a file only when it's finished downloading?

I have a rule in my ~/Downloads directory that seems to fire as soon as the file download starts and then repeatedly while it continues to download. This doesn't end well as I'm sure you can guess. How do I ensure that actions are only performed once the file has finished downloading?

I'm using Kubuntu 19 and using Firefox to download the file, in case any of that matters. Here's the rule in question:

rules:

  # Downloads: Extract "backstop_data" tar files, open the HTML report and send a notification.
  - folders:
      - ~/Downloads
    filters:
      - filename:
          startswith: backstop_data
    actions:
      - shell: 'tar xvf {path} -C {basedir}'
      - shell: "xdg-open /home/dan/Downloads/backstop_data/html_report/index.html"
      - shell: "notify-send 'organize rule ran' '{path}' -u low -i folder"

I'm using inotify-hookable to watch the directory for modifications:

# ~/.bash_profile
inotify-hookable -w ~/Downloads -c "organize run" --no-r &

Filter: Hashtags

I'm the author of another project (https://github.com/xeor/taggo), which aims to solve a problem where that ends up needing many of the same ideas as this one. In the middle of a little rewrite now, but you get the idea by looking at it.

The basics idea we are both solving:

  • Scan for files
  • Collect data about file
  • Only if a filter matches
  • Do something

organize are:

  • Using config-files
  • Being very generic what files you support
  • Having pluggable actions

taggo are:

  • Able to define everything via cli
  • Not being file-generic, but focusing on files/folders that are "tagged" (having the # in it)
  • Creating symlinks, not using actions

Would it be meaningful to try to join these two projects under the same umbrella? Maybe create a github org and make something that solves both problems..?

Feel free to say no, there are still a lot of ideas we can share..

A joint efford could:

  • Use configs or cli
  • Have python functions to act as an api for other tools

What do you think? Feel free to contact me at: lars {dot} solberg at gmail

Use Exif datetime tag for file renaming

Hi,

I tryed to add the date of creation to the filename of my images. With the following code I get the date of creation in a format like this: "2019:01:19 15:23:33".

rules:
  - folders: ~/Desktop/Bilder
    enabled: false
    subfolders: false
    filters:
      - exif:
          image.datetime
    actions:
      - echo: "{exif.image.datetime}"

How could I modify this tag, so I could use this for renaming and get image names like this: "190119_15-23-33_IMG"? And how do I invert the Exif filter to get all images without Exif informations.

Thanks a lot!
Christian

Undo option

Even though there is organize sim, an undo option would be great (in case sth bad happened and changed tons of files). Probably not so easy - just a thought :)

LastModified gives always an error

Hi there,

this is my rule:

- folders:
    - ~/Dropbox/neuesortierung/lecturenotes
    filters:
      - Extension:
        - pdf
        - PDF
      - LastModified:
        - days: 10
    actions:
      - Shell: shrinkpdf "{path}"

Running organize sim or run gives me this output:

ERROR! unsupported type for timedelta days component: dict
Full traceback at: /home/marcel/.cache/organize/log/organize.log

full traceback:

2019-06-05 08:51:51,976 - organize.main - ERROR - unsupported type for timedelta days component: dict
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/organize/main.py", line 102, in main
    execute_rules(config.rules, simulate=args['sim'])
  File "/usr/local/lib/python3.6/dist-packages/organize/config.py", line 134, in rules
    rule_filters = list(self.instantiate_filters(rule_item))
  File "/usr/local/lib/python3.6/dist-packages/organize/config.py", line 95, in instantiate_filters
    yield self._class_instance_with_args(filter_class, args)
  File "/usr/local/lib/python3.6/dist-packages/organize/config.py", line 71, in _class_instance_with_args
    return Cls(*args)
  File "/usr/local/lib/python3.6/dist-packages/organize/filters/last_modified.py", line 83, in __init__
    days=days, hours=hours, minutes=minutes, seconds=seconds)
TypeError: unsupported type for timedelta days component: dict

I have no idea what I am doing wrong, or what is wrong with my system or my config.

I would appreciate some help.

Marcel

File Content Filter Unusable With Multiline Text

Typical real-world use cases for a content filter involve multiple matches and regex captures spread over several lines of text. Alas, the current implementation only works if the entire filter expression is contained within a single line. 1,2

Tested with version 1.8.0 and HEAD.

I will be posting a PR that fixes this in a few minutes.

BTW, love this tool. :)


1) This is not so surprising. A look at the corresponding test revealed only the most trivial cases are covered. (The aside here being: Perhaps review the tests to cover more realistic scenarios. ;-))

2) Note that using more than one file content filter per rule is currently not an acceptable workaround, b/c the way the FileContent filter is implemented, it involves redundant textract processing and severe performance penalties when used multiple times per rule, particularly for formats other than plain text, ex. PDF. (This is worth rethinking in its own right, but not the subject of this issue.)

Looking into Text-Files

I wondering about integrating the function to look into textfiles to filter like

if 'blabla' in open('example.txt').read():
    print("true")

This could be very useful for i.e. letters like machine-readable pdfs or so.

"requires string as left operand, not list"

OS: Arch
organize v1.4.2
organize config --debug -> no errors

organize sim               
                                                                                                                                                                              
/usr/lib/python3.7/site-packages/organize/config.py:28: YAMLLoadWarning: calling yaml.load() without Loader=... is deprecated, as the default Loader is unsafe. 
Full Log
2019-08-01 15:48:38,434 - organize.main - ERROR - 'in <string>' requires string as left operand, not list
Traceback (most recent call last):
  File "/usr/lib/python3.7/site-packages/organize/main.py", line 102, in main
    execute_rules(config.rules, simulate=args['sim'])
  File "/usr/lib/python3.7/site-packages/organize/core.py", line 80, in execute_rules
    jobs = list(find_jobs(rules))
  File "/usr/lib/python3.7/site-packages/organize/core.py", line 47, in find_jobs
    if all(f.matches(path) for f in rule.filters):
  File "/usr/lib/python3.7/site-packages/organize/core.py", line 47, in <genexpr>
    if all(f.matches(path) for f in rule.filters):
  File "/usr/lib/python3.7/site-packages/organize/filters/filename.py", line 70, in matches
    self.contains in filename)
TypeError: 'in <string>' requires string as left operand, not list
2019-08-01 15:48:55,504 - organize.main - ERROR - 'in <string>' requires string as left operand, not list
Traceback (most recent call last):
  File "/usr/lib/python3.7/site-packages/organize/main.py", line 102, in main
    execute_rules(config.rules, simulate=args['sim'])
  File "/usr/lib/python3.7/site-packages/organize/core.py", line 80, in execute_rules
    jobs = list(find_jobs(rules))
  File "/usr/lib/python3.7/site-packages/organize/core.py", line 47, in find_jobs
    if all(f.matches(path) for f in rule.filters):
  File "/usr/lib/python3.7/site-packages/organize/core.py", line 47, in <genexpr>
    if all(f.matches(path) for f in rule.filters):
  File "/usr/lib/python3.7/site-packages/organize/filters/filename.py", line 70, in matches
    self.contains in filename)
TypeError: 'in <string>' requires string as left operand, not list

ERROR! 'PosixPath' object has no attribute 'items'

I get this error:

ERROR! 'PosixPath' object has no attribute 'items'

Why do I get it and how to solve this? This is my config.yaml:

rules:
- folders: ~/Downloads
filters:
- filename:
startswith: "19"
contains:
- "_WF_"
actions:
- rename: "{path.stem}_unread{path.suffix}"
- copy:
dest: '~/Documents/'
overwrite: false
counter_separator: '_'

Thank you for helping me!

Add glob-compatible syntax support to Folder section

Also, it'll be very nice to have globstar support, which is, I quote

If set, the pattern ‘**’ used in a filename expansion context will match all files and zero or more directories and subdirectories. If the pattern is followed by a ‘/’, only directories and subdirectories match.

TypeError: 'int' object is not iterable

Installed poetry and Pendulum manually according to this to bypass initial install issue.
Then, I think I've managed to setup a valid config file, but I'm getting this output from 'organize config --debug':

Traceback (most recent call last):
  File "C:\Python38\lib\runpy.py", line 193, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Python38\lib\runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "C:\Python38\Scripts\organize.exe\__main__.py", line 7, in <module>
  File "C:\Python38\lib\site-packages\organize\cli.py", line 65, in main
    config_debug(config_path)
  File "C:\Python38\lib\site-packages\organize\cli.py", line 123, in config_debug
    rules = config.rules
  File "C:\Python38\lib\site-packages\organize\config.py", line 160, in rules
    rule_filters = list(self.instantiate_filters(rule_item))
  File "C:\Python38\lib\site-packages\organize\config.py", line 121, in instantiate_filters
    yield self._class_instance_with_args(filter_class, args)
  File "C:\Python38\lib\site-packages\organize\config.py", line 98, in _class_instance_with_args
    return Cls(**args)
  File "C:\Python38\lib\site-packages\organize\filters\filename.py", line 81, in __init__
    self.startswith = self.create_list(startswith, case_sensitive)
  File "C:\Python38\lib\site-packages\organize\filters\filename.py", line 103, in create_list
    x = [str(x) for x in x]
TypeError: 'int' object is not iterable

slightly anonymized config:

rules:
  - folders:
        - H:\X\Finished\X
    filters:
        - lastmodified:
            minutes: 40
            mode: older
        - extension:
            - mkv
            - mp4
    actions:
      - copy: G:\
      
    # optional settings:
    enabled: true
    subfolders: true

  - folders:
        - H:\X\Finished\X_h265ize
    filters: 
      - lastmodified:
          minutes: 40
          mode: older
      - extension:
        - mkv
        - mp4
        - avi
        - mpg
        - mpeg
        - wmv
        - m4v
        - flv
    actions:
      - copy: E:\h265ize
      
    # optional settings:
    enabled: true
    subfolders: true

  - folders:
      - E:\h265ize
    filters: 
      - lastmodified:
          minutes: 5
          mode: older
      - extension:
        - mkv
    actions:
      - Move: G:\
      
    # optional settings:
    enabled: true
    subfolders: false
    
  - folders:
      - \\Srv-omv01\data00\h265ize
    filters: 
      - lastmodified:
          minutes: 5
          mode: older
      - extension:
        - mkv
    actions:
      - Move: G:\
      
    # optional settings:
    enabled: true
    subfolders: false
    
  - folders:
      - E:\Podcasts\Downloads\X
    filters: 
      - filename:
          startswith: 2020
      - lastmodified:
          minutes: 40
          mode: newer
      - extension:
        - mp3        
    actions:
      - copy: F:\Podcast_Archive\X\2020\
      
    # optional settings:
    enabled: true
    subfolders: true
  - folders:
      - E:\Podcasts\Downloads\X
    filters: 
      - filename:
          startswith: 2020
      - lastmodified:
          minutes: 40
          mode: newer
      - extension:
        - mp3        
    actions:
      - copy: F:\Podcast_Archive\X\2020\
      
    # optional settings:
    enabled: true
    subfolders: true
  - folders:
      - E:\Podcasts\Downloads\X
    filters: 
      - lastmodified:
          minutes: 40
          mode: newer
      - extension:
        - mp3        
    actions:
      - copy: F:\Podcast_Archive\X
      
    # optional settings:
    enabled: true
    subfolders: true

Filter: Zip files

Filter based on if a Zip file contains a certain filename. Perhaps all filters could apply?
Other types of archived files could be filtered as well.

ERROR! 'lastmodified' & ERROR! 'created'

Hi, I'm testing a config to move some files in subdirectories having name of the year, month, date, ecc ecc of creation or last modification.
The config seems to be ok but everytime I run sim I get these errors both trying with lastmodified and created properties.

Here my simple config

`rules:

  • folders: '/media/scheduled'
    subfolders: true
    filters:

    • extension: jpg

    actions:

    • move: '~/media/foto/{lastmodified.year}/{lastmodified.month}/{lastmodified.day}/{lastmodified.year}-{lastmodified.month}-{lastmodified.day} {lastmodified.hour}:{lastmodified.minute}/'`

It doesn't work either if I use a simpler version with only one subfolder attribute.

File Schedule_20180802-052137.jpg: - [Move] ERROR! 'created'
File Schedule_20170128-124732.jpg: - [Move] ERROR! 'lastmodified'

EDIT: Still not working with same error if I only echo lastmodified.year or created.year

ERROR! 'ascii' codec can't encode character '\u2019' in position 44: ordinal not in range(128)

Hello,

Thanks for this nifty script!

I'm getting the error above when running it on my MacBookPro with Python 3.6.2, and was wondering if anyone might be able to help me troubleshoot.

I've attached the last few lines of the log below.

Thank you!

Cheers,
Ivan

+++

2018-08-29 03:22:36,950 - organize.actions.shell - INFO - Executing command "open "/Volumes/Transcend/Downloads/2. JACINTA_Personal Brand_30min n 15min QnA.pptx"" in shell.
2018-08-29 03:22:36,966 - organize.main - ERROR - 'ascii' codec can't encode character '\u2019' in position 44: ordinal not in range(128)
Traceback (most recent call last):
File "/Users/ivanwong/anaconda3/lib/python3.6/site-packages/organize/main.py", line 103, in main
execute_rules(config.rules, simulate=args['sim'])
File "/Users/ivanwong/anaconda3/lib/python3.6/site-packages/organize/core.py", line 92, in execute_rules
puts('File %s:' % bold(relative_path))
File "/Users/ivanwong/anaconda3/lib/python3.6/site-packages/clint/textui/core.py", line 68, in puts
stream(_str)
UnicodeEncodeError: 'ascii' codec can't encode character '\u2019' in position 44: ordinal not in range(128)
2018-08-29 03:23:29,009 - organize.main - ERROR - 'ascii' codec can't encode character '\u2019' in position 44: ordinal not in range(128)
Traceback (most recent call last):
File "/Users/ivanwong/anaconda3/lib/python3.6/site-packages/organize/main.py", line 103, in main
execute_rules(config.rules, simulate=args['sim'])
File "/Users/ivanwong/anaconda3/lib/python3.6/site-packages/organize/core.py", line 92, in execute_rules
puts('File %s:' % bold(relative_path))
File "/Users/ivanwong/anaconda3/lib/python3.6/site-packages/clint/textui/core.py", line 68, in puts
stream(_str)
UnicodeEncodeError: 'ascii' codec can't encode character '\u2019' in position 44: ordinal not in range(128)
2018-08-29 03:24:56,215 - organize.main - ERROR - 'ascii' codec can't encode character '\u2019' in position 44: ordinal not in range(128)
Traceback (most recent call last):
File "/Users/ivanwong/anaconda3/lib/python3.6/site-packages/organize/main.py", line 103, in main
execute_rules(config.rules, simulate=args['sim'])
File "/Users/ivanwong/anaconda3/lib/python3.6/site-packages/organize/core.py", line 92, in execute_rules
puts('File %s:' % bold(relative_path))
File "/Users/ivanwong/anaconda3/lib/python3.6/site-packages/clint/textui/core.py", line 68, in puts
stream(_str)
UnicodeEncodeError: 'ascii' codec can't encode character '\u2019' in position 44: ordinal not in range(128)
2018-08-29 03:26:21,312 - organize.main - ERROR - 'ascii' codec can't encode character '\u2019' in position 44: ordinal not in range(128)
Traceback (most recent call last):
File "/Users/ivanwong/anaconda3/lib/python3.6/site-packages/organize/main.py", line 103, in main
execute_rules(config.rules, simulate=args['sim'])
File "/Users/ivanwong/anaconda3/lib/python3.6/site-packages/organize/core.py", line 92, in execute_rules
puts('File %s:' % bold(relative_path))
File "/Users/ivanwong/anaconda3/lib/python3.6/site-packages/clint/textui/core.py", line 68, in puts
stream(_str)
UnicodeEncodeError: 'ascii' codec can't encode character '\u2019' in position 44: ordinal not in range(128)
2018-08-29 03:26:47,525 - organize.main - ERROR - 'ascii' codec can't encode character '\u2019' in position 44: ordinal not in range(128)
Traceback (most recent call last):
File "/Users/ivanwong/anaconda3/lib/python3.6/site-packages/organize/main.py", line 103, in main
execute_rules(config.rules, simulate=args['sim'])
File "/Users/ivanwong/anaconda3/lib/python3.6/site-packages/organize/core.py", line 92, in execute_rules
puts('File %s:' % bold(relative_path))
File "/Users/ivanwong/anaconda3/lib/python3.6/site-packages/clint/textui/core.py", line 68, in puts
stream(_str)
UnicodeEncodeError: 'ascii' codec can't encode character '\u2019' in position 44: ordinal not in range(128)
2018-08-29 03:26:53,406 - organize.main - ERROR - 'ascii' codec can't encode character '\u2019' in position 44: ordinal not in range(128)
Traceback (most recent call last):
File "/Users/ivanwong/anaconda3/lib/python3.6/site-packages/organize/main.py", line 103, in main
execute_rules(config.rules, simulate=args['sim'])
File "/Users/ivanwong/anaconda3/lib/python3.6/site-packages/organize/core.py", line 92, in execute_rules
puts('File %s:' % bold(relative_path))
File "/Users/ivanwong/anaconda3/lib/python3.6/site-packages/clint/textui/core.py", line 68, in puts
stream(_str)
UnicodeEncodeError: 'ascii' codec can't encode character '\u2019' in position 44: ordinal not in range(128)

Globstrings on folders don't work (anymore)

I updated to version 1.5 and I'm getting the following error:
ERROR! Path does not exist: ~/Downloads/*.pdf

Config is:

  • folders: '~/Downloads/*.pdf'

Seems like globstrings don't work anymore in folders.

Copy multiple files to separate folders

Hello

I am trying to make your Program work but it gives me errors. I use The docker container from dskaggs to let it run on my server. There is an Folder when files with specific filename appears depending on the filename it moves it to its on folder. My Configfile looks like that but i don't know whats wrong.

Ruben

rules:
  - folders: /source
    subfolders: true
    filters:
      - filename:
            contains: Example1
            case_sensitive: false
    actions:
      - echo: 'File Found'
      - copy:
          dest: '/destination/Example1/'
          overwrite: true


- folders: /source
    subfolders: true
    filters:
      - filename:
            contains: Example2
            case_sensitive: false
    actions:
      - echo: 'File Found'
      - copy:
          dest: '/destination/Example2'
          overwrite: true

Filter by content of PDF file

I've just found this nice little tool which would almost perfectly fit what I need except for filtering based on content of a file. In my particular use case I'd like to move PDF files to different folders based on its text content.

I've found #10 and the related entry in https://github.com/tfeldmann/organize/projects/4 concerning the use of textract.

If there is any interest I might implement this and submit a PR. To prevent unnecessary work I'd like to discuss the implementation before starting. Would integrating textract as a generic solution be OK or are there other ideas?

Command not added to cli

I have installed the tool using pip3
but it is not added to cli commands,
i have re-run the cli (terminate and run new session) same issue.

organize: command not found

System: Ubuntu 17

Rename files with date

hey, is there a possibility to rename files like this, i've a file e.g.:
File_abc_datYYYYMMDD_xyz.pdf -> to -> File_DDMMYYYY.pdf

maybe with regex but i don't know how to use it

{path} resolves to source path instead of destination path

Hi,

First of all thank you very much for this free and open source project! I know it takes a lot of time to manage and maintain such projects and it is highly appreciated!

I'm using the currently latest version (1.4.2) on Mac and tried to move a file and open it afterwards like in the Advanced Usage Example. Unfortunately the {path} placeholder resolves to the source path instead of the destination path and therefore the file can't be opened after it has been moved.

[Trash] ERROR! File not found

Hi,
thanks for this software. I run it at every login, for cleaning the Downloads folder, but when a file is moved to the trash i get this error:

[Trash] ERROR! File not found

file_ext, doesnotcontain

Hi @tfeldmann,

Many thanks again for this fantastic utility.

Might there be a file_ext feature (similar to img_ext), that allow the matching of multiple file names?
e.g. I'd like the "Filename" rule below to match the filenames "love" or "patience".

Secondly is there any way to create a doesnotcontain match on the filename?

Thank you!

Ivan

@themacmarketer

rules:

  - folders: '~/Downloads'
    filters:
      - Filename:          
          contains: love          
          case_sensitive: false
      - LastModified          
    actions:
      - Move: '~/Downloads/FAITH/'

Exception raised when "VSCode" is used as $EDITOR.

Hi,

Title says it all, my editor is VCode, i.e. code --wait --new-window and an exception is raised when issuing organize config:

(python-3.6) Kali:organize kelsolaar$ organize config
Traceback (most recent call last):
  File "/usr/local/bin/organize", line 11, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.6/site-packages/organize/main.py", line 95, in main
    config_edit()
  File "/usr/local/lib/python3.6/site-packages/organize/main.py", line 121, in config_edit
    subprocess.call([editor, str(config_path)])
  File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/subprocess.py", line 267, in call
    with Popen(*popenargs, **kwargs) as p:
  File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/subprocess.py", line 709, in __init__
    restore_signals, start_new_session)
  File "/usr/local/Cellar/python/3.6.4_4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/subprocess.py", line 1344, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'code --wait --new-window ': 'code --wait --new-window '

Cheers,

Thomas

Umlauts and Regular Expressions

I wish I wouldn't have to organize files with umlauts or any other special characters in it, but unfortunately my bank is sending me PDF files with umlauts in it.

Example file name:
Erträgnisaufstellung_2018.pdf

I tried the following regular expressions with no success:

  • Regex: '^Erträgnisaufstellung.pdf$'
  • Regex: '^Ertr.gnisaufstellung.pdf$'
  • Regex: '^Ertr(.)gnisaufstellung.pdf$'

The only workaround I found (not very accurate) is one of these:

  • Regex: '^Ertr(.{2})gnisaufstellung.pdf$'
  • Regex: '^Ertr(.*)gnisaufstellung.pdf$'
  • Regex: '^Ertr(.+)gnisaufstellung.pdf$'

OS: Mac
organize version: 1.5

Maybe it has sth. to do with unicode encoding, but I'm not sure.

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.