organize - The file management automation tool

Overview

organize logo

tests Documentation Status License PyPI Version


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

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).

Getting started

Installation

Python 3.6+ 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:

pip3 install -U organize-tool

If you want the text extraction capabilities, install with textract like this:

pip3 install -U "organize-tool[textract]"

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.

Creating your first rule

In your shell, run organize config to edit the configuration:

rules:
    - folders: ~/Downloads
      subfolders: true
      filters:
          - extension: pdf
      actions:
          - echo: "Found PDF!"

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

Alternatively you can run organize config --path to see the full path to your config.yaml)

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 config again and add a copy-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 filenames 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:
    # sort my invoices and receipts
    - folders: ~/Downloads
      subfolders: true
      filters:
          - extension: pdf
          - filename:
                contains:
                    - Invoice
                    - Order
                    - Purchase
                case_sensitive: false
      actions:
          - move: ~/Documents/Shopping/

Move incomplete downloads older than 30 days into the trash:

rules:
    # move incomplete downloads older > 30 days into the trash
    - folders: ~/Downloads
      filters:
          - extension:
                - download
                - crdownload
                - part
          - lastmodified:
                days: 30
                mode: older
      actions:
          - trash

Delete empty files from downloads and desktop:

rules:
    # delete empty files from downloads and desktop
    - folders:
          - ~/Downloads
          - ~/Desktop
      filters:
          - filesize: 0
      actions:
          - trash

Move screenshots into a "Screenshots" folder on your desktop:

rules:
    # move screenshots into "Screenshots" folder
    - folders: ~/Desktop
      filters:
          - filename:
                startswith: "Screen Shot"
      actions:
          - move: ~/Desktop/Screenshots/

Organize your font downloads:

rules:
    # organize your font files but keep the folder structure:
    #   "~/Downloads/favourites/helvetica/helvetica-bold.ttf"
    #     is moved to
    #   "~/Documents/FONTS/favourites/helvetica/helvetica-bold.ttf"
    - folders: ~/Downloads/**/*.ttf
      actions:
          - Move: "~/Documents/FONTS/{relative_path}"

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

Advanced usage

This example shows some advanced features like placeholder variables, pluggable actions, recursion through subfolders and glob syntax:

rules:
    - folders: ~/Documents/**/*
      filters:
          - extension:
                - pdf
                - docx
          - created
      actions:
          - move: "~/Documents/{extension.upper}/{created.year}{created.month:02}/"
          - shell: 'open "{path}"'

Given we have two files in our ~/Documents folder (or any of its subfolders) named script.docx from january 2018 and demo.pdf from december 2016 this will happen:

  • script.docx will be moved to ~/Documents/DOCX/2018-01/script.docx
  • demo.pdf will be moved to ~/Documents/PDF/2016-12/demo.pdf
  • The files will be opened (open command in macOS) from their new location.
  • Note the format syntax for {created.month} to make sure the month is prepended with a zero.

Command line interface

The file management automation tool.

Usage:
    organize sim [--config-file=]
    organize run [--config-file=]
    organize config [--open-folder | --path | --debug] [--config-file=]
    organize list
    organize --help
    organize --version

Arguments:
    sim             Simulate a run. Does not touch your files.
    run             Organizes your files according to your rules.
    config          Open the configuration file in $EDITOR.
    list            List available filters and actions.
    --version       Show program version and exit.
    -h, --help      Show this screen and exit.

Options:
    -o, --open-folder  Open the folder containing the configuration files.
    -p, --path         Show the path to the configuration file.
    -d, --debug        Debug your configuration file.

Full documentation: https://organize.readthedocs.io
Comments
  • Version 2.x filecontent not working anymore

    Version 2.x filecontent not working anymore

    The filecontent regex is not compatible. Error is happening with pdf files. All rules previously working containing the "filecontent" command result in error. Tested with Version 2.09 and 2.1.

    - (filecontent) ERROR! 'NoneType' object has no attribute 'groupdict'
    

    Examples not working anymore:

        filters:
          - regex: '(?P<year>20\d{2})-[01]\d-[0-3]\d.*\.pdf'  
          - filecontent:
            - '(HUK-COBURG)|(ADAC)|(Meine HUK24)|(Sozialversicherung)'
          - filecontent:
            - '(?P<strasse>Brückl)'
    
       filters:
          - regex: '(?P<year>20\d{2})-[01]\d-[0-3]\d.*\.pdf'
          - filecontent:
            - '(Versorgung)|(Abwasser)|(Schmutzwasser)|(Abfall)'
          - filecontent:
            - 'Brückl'
    

    Even examples taken straight from documentation do not work anymore

        filters:
          - filecontent:
            - '(?P<inhalt>.*)'
    
    bug awaiting feedback 
    opened by Marty56 28
  • How to run the program

    How to run the program

    Hi,

    I am a newbie in all things technical. I have installed the package doing pip3 install -U organize-tool. How do I run it? I have written "organize" on the Terminal, but this does not work. Thanks in advance!

    opened by AtomicNess123 16
  • Error after update to V 2.0

    Error after update to V 2.0

    I have changed my rule file according migration documentation. Getting the following error:

    /usr/local/bin/python3 -m  organize sim --config-file='/Users/martinklimke/Library/Mobile Documents/com~apple~CloudDocs/Documents/Organize/Rules/config.yaml'
    (base) [email protected] ~ % /usr/local/bin/python3 -m  organize sim --config-file='/Users/martinklimke/Library/Mobile Documents/com~apple~CloudDocs/Documents/Organize/Rules/config.yaml'
    Deprecated: The --config-file option can now be omitted. See organize --help.
    organize 2.0.0
    Config: "/Users/martinklimke/Library/Mobile 
    Documents/com~apple~CloudDocs/Documents/Organize/Rules/config.yaml"
    ╭───────────────────── Traceback (most recent call last) ──────────────────────╮
    │                                                                              │
    │ /Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-package │
    │ s/organize/cli.py:86 in run_local                                            │
    │                                                                              │
    │    83 │   │   config_dir, config_name = split(config_path)                   │
    │    84 │   │   config = open_fs(config_dir).readtext(config_name)             │
    │    85 │   │   os.chdir(working_dir)                                          │
    │ ❱  86 │   │   core.run(rules=config, simulate=simulate)                      │
    │    87 │   except NeedsMigrationError as e:                                   │
    │    88 │   │   console.error(e, title="Config needs migration")               │
    │    89 │   │   console.warn(                                                  │
    │ /Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-package │
    │ s/organize/core.py:294 in run                                                │
    │                                                                              │
    │   291 │                                                                      │
    │   292 │   rules = config.cleanup(rules)                                      │
    │   293 │                                                                      │
    │ ❱ 294 │   migrate_v1(rules)                                                  │
    │   295 │                                                                      │
    │   296 │   if validate:                                                       │
    │   297 │   │   config.validate(rules)                                         │
    │                                                                              │
    │ /Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-package │
    │ s/organize/migration.py:26 in migrate_v1                                     │
    │                                                                              │
    │   23 │   │   if "folders" in rule:                                           │
    │   24 │   │   │   raise NeedsMigrationError("`folders` are now `locations`")  │
    │   25 │   │   for fil in rule.get("filters", []):                             │
    │ ❱ 26 │   │   │   name, _ = entry_name_args(fil)                              │
    │   27 │   │   │   if name == "filename":                                      │
    │   28 │   │   │   │   raise NeedsMigrationError("`filename` is now `name`")   │
    │   29 │   │   │   if name == "filesize":                                      │
    ╰──────────────────────────────────────────────────────────────────────────────╯
    TypeError: cannot unpack non-iterable NoneType object
    
    opened by Marty56 15
  • macOS tags

    macOS tags

    I love this project and use it quite a lot.

    I would be great if organize could also support tagging under MacOS. Library for setting tags seems to be available https://pypi.org/project/macos-tags/

    feature request 
    opened by Marty56 15
  • {created} filter is not populated  version 2.0.x

    {created} filter is not populated version 2.0.x

    Hi Great work on the 2.x stuff..

    With the Created filter set and modified config for 2.x it seems that the created variable is not being populated

    config:

      - name: "Match Images"
        locations: "~/Downloads"
        filters:
          - extension:
              - png
              - jpg
              - jpeg
              - gif
              - tiff
              - eps
          - created
        actions:
          - echo: "Found Image File CREATED: {created} {created.strftime('%Y-%m-%d')}"
          - move: "~/Nextcloud/Pictures/2020 - 2029/{created.year}/{created.month}/Daily/{created.day}/"
    

    Output is as follows on SIM

    ⚙ Match Images ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
    /home/marty/Downloads
      Picture 2022-02-09 10-05-19.png
        - (echo) ERROR! 'None' has no attribute 'strftime'
    

    Debug info:

    │                                                                                                  │
    │ /home/marty/.local/lib/python3.10/site-packages/organize/cli.py:87 in run_local                  │
    │                                                                                                  │
    │    84 │   │   config_dir, config_name = split(config_path)                                       │
    │    85 │   │   config = open_fs(config_dir).readtext(config_name)                                 │
    │    86 │   │   os.chdir(working_dir)                                                              │
    │ ❱  87 │   │   core.run(rules=config, simulate=simulate)                                          │
    │    88 │   except NeedsMigrationError as e:                                                       │
    │    89 │   │   console.error(e, title="Config needs migration")                                   │
    │    90 │   │   console.warn(                                                                      │
    │ /home/marty/.local/lib/python3.10/site-packages/organize/core.py:328 in run                      │
    │                                                                                                  │
    │   325 │   console.summary(count)                                                                 │
    │   326 │                                                                                          │
    │   327 │   if count["fail"]:                                                                      │
    │ ❱ 328 │   │   raise RuntimeWarning("Some actions failed.")                                       │
    │   329  
    
    awaiting feedback 
    opened by snuffop 14
  • Filter

    Filter "created" does not output date and time

    Hello all, In my config I try to rename the files with the filter "created" and move them to a certain folder.

    Unfortunately, no values are output in the variables used.

    VID-{created.year}{created.month}{created.day}.{created.hour}-{created.minutes}.{extension.lower}

    VID-.-.<built-in method lower of str object at 0xb65aad00>

    The file name should then look like this: Example: VID-20220210-06-20.mp4

    here my config:

      - locations:
          - *downloads
        filters:
          - extension:
              - mp4
              - avi
              - mpeg
              - mpg
              - mov
              - mkv
              - flv
              - m4v
              - 3gp
              - wmv
          - name:
              case_sensitive: false
          - created
        actions:
          - move: '/srv/mergerfs/Pool/Videos/VID-{created.year}{created.month}{created.day}.{created.hour}-{created.minutes}.{extension.lower}'  
        subfolders: false
    
    opened by wintuxx 12
  • Migration to 2.0 - InvalidCharsInPath: contains invalid characters

    Migration to 2.0 - InvalidCharsInPath: contains invalid characters

    I am trying to move from 1.0 to 2.0 on my Windows 10 laptop. However, I cannot simulate or run any rules.

    The error message reads : InvalidCharsInPath: path 'C:\Users\%USERPFOILE%\AppData\Local\organize\organize\config.yaml' contains invalid characters

    Errors are occurring at :

    Python\Python310\lib\site-packages\organize\cli.py:86 in │ │ run_local

    86 │ │ config = open_fs(config_dir).readtext(config_name)

    \Python\Python310\lib\site-packages\fs\base.py:691 in ││ readtext │

    691 │ │ │ self.open( │ │ 692 │ │ │ │ path, mode="rt", encoding=encoding, errors=errors, newline=newline │ │ 693 │ │ │ ) │ │ 694 │ │ ) as read_file:

    Python\Python310\lib\site-packages\fs\osfs.py:640 in open

    │ > 640 │ │ _path = self.validatepath(path)

    Python\Python310\lib\site-packages\fs\osfs.py:687 in │ │ validatepath │ │ > 687 │ │ return super(OSFS, self).validatepath(path)

    │ > 1577 │ │ │ │ raise errors.InvalidCharsInPath(path)

    How can I resolve this ? I'm I missing some dependencies ?

    bug awaiting feedback 
    opened by ktreharrison 11
  • Rule ignoring «contains: screenshot»

    Rule ignoring «contains: screenshot»

    This rule ignores any screenshot files in the pertinent folder. Maybe somebody can see where the error is? I need the rule to target any file whose filename contains screenshot.

      - name: "move screenshots to temp folder"
        locations: '~/OneDrive/Pictures/Camera Roll/'
        subfolders: false
        enabled: true
        filters:
          - extension:
            - png
            - jpg
            - jpeg
          - name:
              contains: screenshot
              case_sensitive: false
        actions:
          - move:
              dest: "~/Pictures/album/TEMP/"
              on_conflict: rename_new
              rename_template: "{name}_{counter}{extension}"
    
    bug awaiting feedback 
    opened by iburunat 11
  • Shortcuts not registering?

    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.

    opened by daephx 10
  • Duplicate filter can offer unpredictable results in folders with multiples copies of a single file

    Duplicate filter can offer unpredictable results in folders with multiples copies of a single file

    I have used organize to sort and rename files. After several runs, I created a few duplicates. As I tried to remove the dupes with the built-in filter, the simulations worried me a bit. I created a simplified example to demonstrate this behavior.

    Initial configuration

    You have a folder with several copies of the same file. There is an original file and several copies of the same file with numbers appended at the end. For this example, I created a testfile.txt under testdupes folder. Content of the file is "Hello World!". Then I copied the file 5 times using the Finder (on Mac).

    Organize rule

    This is the organize rule I used to match the duplicate files:

    - folders: ~/testdupes/**/*
        filters:
          - duplicate
        actions:
          - echo: "{path} is a duplicate of {duplicate}"
    

    Initial test

     ~ % vi testdupes/tesfile.txt
    (Files copied on Finder)
     ~ % cd testdupes 
    testdupes % ls -la
    total 48
    drwxr-xr-x   8 user  staff   256 27 ene 11:11 .
    drwxr-xr-x+ 53 user  staff  1696 27 ene 11:10 ..
    -rw-r--r--   1 user  staff    13 27 ene 11:10 tesfile_copy_1.txt
    -rw-r--r--   1 user  staff    13 27 ene 11:10 tesfile_copy_2.txt
    -rw-r--r--   1 user  staff    13 27 ene 11:10 tesfile_copy_3.txt
    -rw-r--r--   1 user  staff    13 27 ene 11:10 tesfile_copy_4.txt
    -rw-r--r--@  1 user  staff    13 27 ene 11:10 tesfile_copy_5.txt
    -rw-r--r--   1 user  staff    13 27 ene 11:10 tesfile_orig.txt
    

    Then I run the organize sim command:

    testdupes % organize sim            
    False
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SIMULATION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Folder /Users/user/testdupes:
      File tesfile_copy_2.txt:
        - [Echo] /Users/user/testdupes/tesfile_copy_2.txt is a duplicate of /Users/user/testdupes/tesfile_copy_1.txt
      File tesfile_copy_3.txt:
        - [Echo] /Users/user/testdupes/tesfile_copy_3.txt is a duplicate of /Users/user/testdupes/tesfile_copy_2.txt
      File tesfile_copy_4.txt:
        - [Echo] /Users/user/testdupes/tesfile_copy_4.txt is a duplicate of /Users/user/testdupes/tesfile_copy_3.txt
      File tesfile_copy_5.txt:
        - [Echo] /Users/user/testdupes/tesfile_copy_5.txt is a duplicate of /Users/user/testdupes/tesfile_copy_4.txt
      File tesfile_orig.txt:
        - [Echo] /Users/cesargarciasaez/testdupes/tesfile_orig.txt is a duplicate of /Users/cesargarciasaez/testdupes/tesfile_copy_5.txt
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SIMULATION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    

    Issues detected

    • Organize only looks for the first duplicate. If you have several duplicates of each other, they won't be detected as dupes of the original.
    • The order of the sort appear to be on last modified, not on creation date. This can create issues if you ask Organize to delete the duplicates. Instead of ending up with the original file, that file could be deleted and you could end up with one of the copies (but it is not easy to determine the ending name/number).
    • In this particular example, if I deleted instead of echoing the name, I would have keep just: tesfile_copy_1.txt
    • Note: I assume you will always retain one of the copies, but I am not sure this could not lead to a circular deletion of all duplicates, including the original file too.

    Additional tests

    To verify that last modified date is the default sort order, I rename all files but the original one.

    testdupes % mv tesfile_copy_1.txt 'testfile_orig 1.txt'
    testdupes % mv tesfile_copy_2.txt 'testfile_orig 2.txt'
    testdupes % mv tesfile_copy_3.txt 'testfile_orig 3.txt'
    testdupes % mv tesfile_copy_4.txt 'testfile_orig 4.txt'
    testdupes % mv tesfile_copy_5.txt 'testfile_orig 5.txt'
    

    Then running organize sim again, would leave testfile_orig.txt untouched.

    testdupes % organize sim                               
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SIMULATION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Folder /Users/user/testdupes:
      File testfile_orig 1.txt:
        - [Echo] /Users/user/testdupes/testfile_orig 1.txt is a duplicate of /Users/user/testdupes/tesfile_orig.txt
      File testfile_orig 2.txt:
        - [Echo] /Users/user/testdupes/testfile_orig 2.txt is a duplicate of /Users/user/testdupes/testfile_orig 1.txt
      File testfile_orig 3.txt:
        - [Echo] /Users/user/testdupes/testfile_orig 3.txt is a duplicate of /Users/user/testdupes/testfile_orig 2.txt
      File testfile_orig 4.txt:
        - [Echo] /Users/user/testdupes/testfile_orig 4.txt is a duplicate of /Users/user/testdupes/testfile_orig 3.txt
      File testfile_orig 5.txt:
        - [Echo] /Users/user/testdupes/testfile_orig 5.txt is a duplicate of /Users/user/testdupes/testfile_orig 4.txt
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SIMULATION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    

    This test might suggest that last modified file is not tested for duplication. Is this expected behavior?

    Additional run, renaming the original file. This would leave us again with testfile_orig 1.txt as the only remaining copy.

    testdupes% mv tesfile_orig.txt testfile_true_orig.txt
    testdupes % organize sim                              
    False
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SIMULATION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Folder /Users/user/testdupes:
      File testfile_orig 2.txt:
        - [Echo] /Users/user/testdupes/testfile_orig 2.txt is a duplicate of /Users/user/testdupes/testfile_orig 1.txt
      File testfile_orig 3.txt:
        - [Echo] /Users/user/testdupes/testfile_orig 3.txt is a duplicate of /Users/user/testdupes/testfile_orig 2.txt
      File testfile_orig 4.txt:
        - [Echo] /Users/user/testdupes/testfile_orig 4.txt is a duplicate of /Users/user/testdupes/testfile_orig 3.txt
      File testfile_orig 5.txt:
        - [Echo] /Users/user/testdupes/testfile_orig 5.txt is a duplicate of /Users/user/testdupes/testfile_orig 4.txt
      File testfile_true_orig.txt:
        - [Echo] /Users/user/testdupes/testfile_true_orig.txt is a duplicate of /Users/user/testdupes/testfile_orig 5.txt
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SIMULATION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    

    Final notes

    I am aware that I can use the regex filter (and maybe even the filename filter) to deal with duplicates with similar names, but I am not quite sure that the duplicate filter won't cause any data loss without any extra attention in folders with several copies of the same file.

    opened by elsatch 9
  • "macostags" causes error on M1 Mac

    The "macostags" causes the following error on M1 Mac

    Traceback (most recent call last): File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/organize/cli.py", line 77, in main execute_rules(config.rules, simulate=args["sim"]) File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/organize/config.py", line 161, in rules rule_actions = list(self.instantiate_actions(rule_item)) File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/organize/config.py", line 139, in instantiate_actions action_class = self._get_action_class_by_name(name) File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/organize/config.py", line 87, in _get_action_class_by_name return self.action_by_name[self.sanitize_key(name)] KeyError: 'macostags' 2021-04-27 20:18:11,822 - organize - ERROR - 'macostags' Traceback (most recent call last): File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/organize/cli.py", line 77, in main execute_rules(config.rules, simulate=args["sim"]) File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/organize/config.py", line 161, in rules rule_actions = list(self.instantiate_actions(rule_item)) File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/organize/config.py", line 139, in instantiate_actions action_class = self._get_action_class_by_name(name) File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/organize/config.py", line 87, in _get_action_class_by_name return self.action_by_name[self.sanitize_key(name)] KeyError: 'macostags'

    opened by Marty56 9
  • Add option to preserve mtime on move action

    Add option to preserve mtime on move action

    Is your feature request related to a problem? Please describe. When I move files, I want the file creation and modified time to be preserved. Currently there is no option to do that.

    Describe the solution you'd like I want to be able to specify that a move action should preserve the modified time on files

    Describe alternatives you've considered There are none

    Additional context The code is already in place to preserve the mtime on file move, but it is off by default and there is no way to enable it via config.yaml. (See organize/actions/move.py)

    # this is taken from my PR
    def move_file_optimized(
        src_fs,
        src_path,
        dst_fs,
        dst_path,
        preserve_time=False,
        cleanup_dst_on_error=True,
    ):
        # type: (...) -> None
        """Move a file from one filesystem to another.
        Arguments:
            src_fs (FS or str): Source filesystem (instance or URL).
            src_path (str): Path to a file on ``src_fs``.
            dst_fs (FS or str): Destination filesystem (instance or URL).
            dst_path (str): Path to a file on ``dst_fs``.
            preserve_time (bool): If `True`, try to preserve mtime of the
                resources (defaults to `False`).
            cleanup_dst_on_error (bool): If `True`, tries to delete the file copied to
                ``dst_fs`` if deleting the file from ``src_fs`` fails (defaults to `True`).
    
    feature request 
    opened by FlorianMittag 0
  • Error with rename

    Error with rename

    Describe the bug Trying to run the script in sim mode.

    PS C:\Users\Paul\Downloads> organize sim
    organize 2.4.0
    Config: "C:\Users\Paul\AppData\Local\organize\config.yaml"
    Working dir: ""
    ╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
    │ C:\Users\Paul\AppData\Local\Programs\Python\Python311\Lib\site-packages\organize\core.py:181 in  │
    │ replace_with_instances                                                                           │
    │                                                                                                  │
    │   178 │   │   _actions = []                                                                      │
    │   179 │   │   for x in ensure_list(rule["actions"]):                                             │
    │   180 │   │   │   try:                                                                           │
    │ ❱ 181 │   │   │   │   _actions.append(instantiate_action(x))                                     │
    │   182 │   │   │   except Exception as e:                                                         │
    │   183 │   │   │   │   raise ValueError("Invalid action %s (%s)" % (x, e)) from e                 │
    │   184                                                                                            │
    │                                                                                                  │
    │ C:\Users\Paul\AppData\Local\Programs\Python\Python311\Lib\site-packages\organize\core.py:138 in  │
    │ instantiate_action                                                                               │
    │                                                                                                  │
    │   135 │   spec = ensure_dict(action_config)                                                      │
    │   136 │   name, value = next(iter(spec.items()))                                                 │
    │   137 │   args, kwargs = to_args(value)                                                          │
    │ ❱ 138 │   return ACTIONS[name](*args, **kwargs)                                                  │
    │   139                                                                                            │
    │   140                                                                                            │
    │   141 def syspath_or_exception(fs, path):                                                        │
    │                                                                                                  │
    │ C:\Users\Paul\AppData\Local\Programs\Python\Python311\Lib\site-packages\organize\actions\rename. │
    │ py:59 in __init__                                                                                │
    │                                                                                                  │
    │    56 │   │   │   │   "on_conflict must be one of %s" % ", ".join(CONFLICT_OPTIONS)              │
    │    57 │   │   │   )                                                                              │
    │    58 │   │                                                                                      │
    │ ❱  59 │   │   self.new_name = Template.from_string(name)                                         │
    │    60 │   │   self.conflict_mode = on_conflict                                                   │
    │    61 │   │   self.rename_template = Template.from_string(rename_template)                       │
    │    62                                                                                            │
    │                                                                                                  │
    │ C:\Users\Paul\AppData\Local\Programs\Python\Python311\Lib\site-packages\jinja2\environment.py:10 │
    │ 92 in from_string                                                                                │
    │                                                                                                  │
    │   1089 │   │   """                                                                               │
    │   1090 │   │   gs = self.make_globals(globals)                                                   │
    │   1091 │   │   cls = template_class or self.template_class                                       │
    │ ❱ 1092 │   │   return cls.from_code(self, self.compile(source), gs, None)                        │
    │   1093 │                                                                                         │
    │   1094 │   def make_globals(                                                                     │
    │   1095 │   │   self, d: t.Optional[t.Mapping[str, t.Any]]                                        │
    │                                                                                                  │
    │ C:\Users\Paul\AppData\Local\Programs\Python\Python311\Lib\site-packages\jinja2\environment.py:75 │
    │ 7 in compile                                                                                     │
    │                                                                                                  │
    │    754 │   │   │   │   filename = "<template>"                                                   │
    │    755 │   │   │   return self._compile(source, filename)                                        │
    │    756 │   │   except TemplateSyntaxError:                                                       │
    │ ❱  757 │   │   │   self.handle_exception(source=source_hint)                                     │
    │    758 │                                                                                         │
    │    759 │   def compile_expression(                                                               │
    │    760 │   │   self, source: str, undefined_to_none: bool = True                                 │
    │                                                                                                  │
    │ C:\Users\Paul\AppData\Local\Programs\Python\Python311\Lib\site-packages\jinja2\environment.py:92 │
    │ 5 in handle_exception                                                                            │
    │                                                                                                  │
    │    922 │   │   """                                                                               │
    │    923 │   │   from .debug import rewrite_traceback_stack                                        │
    │    924 │   │                                                                                     │
    │ ❱  925 │   │   raise rewrite_traceback_stack(source=source)                                      │
    │    926 │                                                                                         │
    │    927 │   def join_path(self, template: str, parent: str) -> str:                               │
    │    928 │   │   """Join a template with the parent.  By default all the lookups are               │
    │ <unknown>:1 in template                                                                          │
    ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
    TemplateSyntaxError: expected token 'end of print statement', got ':'
    
    The above exception was the direct cause of the following exception:
    
    ╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
    │ C:\Users\Paul\AppData\Local\Programs\Python\Python311\Lib\site-packages\organize\cli.py:151 in   │
    │ execute                                                                                          │
    │                                                                                                  │
    │   148 │                                                                                          │
    │   149 │   try:                                                                                   │
    │   150 │   │   console.info(config=config_path, working_dir=working_dir)                          │
    │ ❱ 151 │   │   core.run(                                                                          │
    │   152 │   │   │   rules=config_text,                                                             │
    │   153 │   │   │   simulate=simulate,                                                             │
    │   154 │   │   │   working_dir=working_dir,                                                       │
    │                                                                                                  │
    │ C:\Users\Paul\AppData\Local\Programs\Python\Python311\Lib\site-packages\organize\core.py:380 in  │
    │ run                                                                                              │
    │                                                                                                  │
    │   377 │   │   config.validate(rules)                                                             │
    │   378 │                                                                                          │
    │   379 │   # instantiate                                                                          │
    │ ❱ 380 │   warnings = replace_with_instances(rules, default_filesystem=working_dir)               │
    │   381 │   for msg in warnings:                                                                   │
    │   382 │   │   console.warn(msg)                                                                  │
    │   383                                                                                            │
    │                                                                                                  │
    │ C:\Users\Paul\AppData\Local\Programs\Python\Python311\Lib\site-packages\organize\core.py:183 in  │
    │ replace_with_instances                                                                           │
    │                                                                                                  │
    │   180 │   │   │   try:                                                                           │
    │   181 │   │   │   │   _actions.append(instantiate_action(x))                                     │
    │   182 │   │   │   except Exception as e:                                                         │
    │ ❱ 183 │   │   │   │   raise ValueError("Invalid action %s (%s)" % (x, e)) from e                 │
    │   184 │   │                                                                                      │
    │   185 │   │   rule["locations"] = _locations                                                     │
    │   186 │   │   rule["filters"] = _filters                                                         │
    ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
    ValueError: Invalid action {'rename': '{created.year}-{created.month:02}-{created.day:02}_{name}'} (expected token 'end
    of print statement', got ':')
    PS C:\Users\Paul\Downloads>
    

    Environment (please complete the following information):

    • OS: win11
    • Output of organize --version: organize, version 2.4.0
    # organize configuration file
    # https://organize.readthedocs.io
    
    rules:
      - name: "Delete installers"
        locations:
          - "~/Downloads"
        filters:
          - extension: exe
        actions:
          - trash
    rules:
      - name: "Move and rename images"
        locations:
          - "~/Downloads"
        filters:
          - extension:
            - jpg
            - jpeg
            - png
            - webp
        actions:
          - rename: "{created.year}-{created.month:02}-{created.day:02}_{name}"
          - move: "H:/local/images/"
    
    bug 
    opened by DeutscheGabanna 1
  • Bump types-pyyaml from 6.0.11 to 6.0.12.2

    Bump types-pyyaml from 6.0.11 to 6.0.12.2

    Bumps types-pyyaml from 6.0.11 to 6.0.12.2.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies python 
    opened by dependabot[bot] 0
  • Possibility to use vars instead of absolute paths when using certain location features

    Possibility to use vars instead of absolute paths when using certain location features

    Is your feature request related to a problem? Please describe. It doesn't seem to be possible to use variables along with certain options, like exclude_dirs.

    E.g. this works:

    folderpath: &mypath
      - "/run/media/myuser/samsung/"
    
    excl_dirs: &excldirs
      - "github"
      - "backups"
    
    rules:
      - name: "Finding audio"
        locations: *mypath
        subfolders: true
        filters:
            - extension
                - *aud
        actions:
            - echo: "File: {relative_path}"
    

    This doesn't (it keeps asking for a [str]):

    folderpath: &mypath
      - "/run/media/myuser/samsung/"
    
    excl_dirs: &excldirs
      - "github"
      - "backups"
    
    rules:
      - name: "Finding audio"
        locations:
          - path: *mypath
            exclude_dirs: *excldirs
        subfolders: true
        filters:
            - extension
                - *aud
        actions:
            - echo: "File: {relative_path}"
    

    In order for that to work, you have to provide an absolute path:

    rules:
      - name: "Finding audio"
        locations:
          - path: "/run/media/myuser/samsung/"
            exclude_dirs:
              - "github"
              - "backups"
        subfolders: true
        filters:
            - extension
                - *aud
        actions:
            - echo: "File: {relative_path}"
    

    Describe the solution you'd like Just the possibility to use variables instead of having to use absolute paths 😊

    Describe alternatives you've considered I'm just making do with the available options

    Additional context It could very well be that it is I that's doing something wrong.

    feature request 
    opened by telometto 1
  • Python filter: regex.mon triggers error 'dict' object has no attribute 'mon'

    Python filter: regex.mon triggers error 'dict' object has no attribute 'mon'

    Describe the bug I tried to write a python filter that takes a short month name and returns the corresponding month number. I took the python filter example from the docs that changes the last name into an email, and changed it as needed (see below)

    Issue

    The preceding regex filter creates a mon attribute. Python should be able to fetch that attribute as

    regex.mon
    

    but I get an error instead: (python) ERROR! 'dict' object has no attribute 'mon' When I change the code as follows:

    regex["mon"]
    

    the code works.

    • OS: macOS 12.6
    • Output of organize --version: organize, version 2.2.0
    • Python 3.10.8

    Your config file

      - name: "Monthly Hours"
        tags:
          - finances
        locations:
          - ~/Downloads
        filters:
          - extension: csv
          - regex: 'Monthly_Hours_(?P<year>\d\d\d\d)-(?P<mon>...)'
          - python: |
              print(regex)
              print(regex["year"])
              print(regex.mon)  # <- This syntax errors out
              months = {
                "Jan": "01",
                "Feb": "02",
                "Mar": "03",
                "Apr": "04",
                "May": "05",
                "Jun": "06",
                "Jul": "07",
                "Aug": "08",
                "Sep": "09",
                "Oct": "10",
                "Nov": "11",
                "Dec": "12",
              }
              return {"month": months[regex["mon"]]}
        actions:
          - move: "target_dir/Monthly_Hours_{regex.year}-{python.month}.csv"
    

    Output of organize sim:

      Monthly_Hours_2022-Jan-01-2022-Jan-31.csv
        - (python) {'year': '2022', 'mon': 'Jan'}
        - (python) 2022
        - (python) ERROR! 'dict' object has no attribute 'mon'
    
    bug 
    opened by christophberger 1
  • Bump mkdocs-include-markdown-plugin from 3.6.1 to 3.9.1

    Bump mkdocs-include-markdown-plugin from 3.6.1 to 3.9.1

    Bumps mkdocs-include-markdown-plugin from 3.6.1 to 3.9.1.

    Release notes

    Sourced from mkdocs-include-markdown-plugin's releases.

    v3.9.1

    Bug fixes:

    • Fixed event order on Mkdocs >= 1.4.0

    v3.9.0

    New features:

    • Included files are watched when using mkdocs serve. Only supported from Mkdocs v1.4.0 onwards.

    Deprecated:

    • The support for Python 3.6 has been oficially deprecated and the plugin will not be installable on that version from the next v4.0.0 release.

    v3.8.1

    Bug fixes:

    • Fixed two errors relative to includer indentation for both directives.

    v3.8.0

    New features:

    v3.7.1

    Bug fixes:

    • HTML-escape file paths in comments generated by include-markdown directive to prevent broken content when including some edge cases.

    v3.7.0

    New features:

    • Added new argument encoding for both directives.
    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies python 
    opened by dependabot[bot] 0
Releases(v2.4.0)
Python's Filesystem abstraction layer

PyFilesystem2 Python's Filesystem abstraction layer. Documentation Wiki API Documentation GitHub Repository Blog Introduction Think of PyFilesystem's

pyFilesystem 1.8k Jan 02, 2023
CSV-Handler written in Python3

CSVHandler This code allows you to work intelligently with CSV files. A file in CSV syntax is converted into several lists, which are combined in a to

Max Tischberger 1 Jan 13, 2022
pytiff is a lightweight library for reading chunks from a tiff file

pytiff is a lightweight library for reading chunks from a tiff file. While it supports other formats to some extend, it is focused on reading tiled greyscale/rgb images, that can also be bigtiffs. Wr

Big Data Analytics group 9 Mar 21, 2022
Powerful Python library for atomic file writes.

Powerful Python library for atomic file writes.

Markus Unterwaditzer 313 Oct 19, 2022
Media file renamer and organizion tool

mnamer mnamer (media renamer) is an intelligent and highly configurable media organization utility. It parses media filenames for metadata, searches t

Jessy Williams 533 Dec 29, 2022
Read and write TIFF files

Read and write TIFF files Tifffile is a Python library to store numpy arrays in TIFF (Tagged Image File Format) files, and read image and metadata fro

Christoph Gohlke 346 Dec 18, 2022
BOOTH宛先印刷用CSVから色々な便利なリストを作成してCSVで出力するプログラムです。

BOOTH注文リスト作成スクリプト このPythonスクリプトは、BOOTHの「宛名印刷用CSV」から、 未発送の注文 今月の注文 特定期間の注文 を抽出した上で、各注文を商品毎に一覧化したCSVとして出力するスクリプトです。 簡単な使い方 ダウンロード 通常は、Relaseから、booth_ord

hinananoha 1 Nov 28, 2021
This is just a GUI that detects your file's real extension using the filetype module.

Real-file.extnsn This is just a GUI that detects your file's real extension using the filetype module. Requirements Python 3.4 and above filetype modu

1 Aug 08, 2021
useful files for the Freenove Big Hexapod

FreenoveBigHexapod useful files for the Freenove Big Hexapod HexaDogPos is a utility for converting the Freenove xyz co-ordinate system to servo angle

Alex 2 May 28, 2022
Import Python modules from any file system path

pathimp Import Python modules from any file system path. Installation pip3 install pathimp Usage import pathimp

Danijar Hafner 2 Nov 29, 2021
Python function to stream unzip all the files in a ZIP archive: without loading the entire ZIP file or any of its files into memory at once

Python function to stream unzip all the files in a ZIP archive: without loading the entire ZIP file or any of its files into memory at once

Department for International Trade 206 Jan 02, 2023
Automatically generates a TypeQL script for doing entity and relationship insertions from a .csv file, so you don't have to mess with writing TypeQL.

Automatically generates a TypeQL script for doing entity and relationship insertions from a .csv file, so you don't have to mess with writing TypeQL.

3 Feb 09, 2022
A tiny Configuration File Parser for Python Projects

A tiny Configuration File Parser for Python Projects. Currently working on JSON Config Files only.

Tanmoy Sen Gupta 1 Feb 12, 2022
A Python script to organize your files in a given directory.

File-Organizer A Python script to organize your files in a given directory. It organizes your files based on the file extension and moves them into sp

Imira Randeniya 1 Sep 11, 2022
Remove [x]_ from StudIP zip Archives and archive_filelist.csv completely

This tool removes the "[x]_" at the beginning of StudIP zip Archives. It also deletes the "archive_filelist.csv" file

Kelke vl 1 Jan 19, 2022
PaddingZip - a tool that you can craft a zip file that contains the padding characters between the file content.

PaddingZip - a tool that you can craft a zip file that contains the padding characters between the file content.

phithon 53 Nov 07, 2022
ValveVMF - A python library to parse Valve's VMF files

ValveVMF ValveVMF is a Python library for parsing .vmf files for the Source Engi

pySourceSDK 2 Jan 02, 2022
File storage with API access. Used as a part of the Swipio project

API File storage File storage with API access. Used as a part of the Swipio project 📝 About The Project File storage allows you to upload and downloa

25 Sep 17, 2022
Singer is an open source standard for moving data between databases, web APIs, files, queues, and just about anything else you can think of.

Singer is an open source standard for moving data between databases, web APIs, files, queues, and just about anything else you can think of. Th

Singer 1.1k Jan 05, 2023
A simple tool to find and replace all the matches of a regular expression in file(s).

FindREp A simple tool to find and replace all the matches of a regular expression in file(s). You can either select the file(s) directly or select a f

Biraj 5 Oct 18, 2022