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) martinklimke@MBP-von-Martin ~ % /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)
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
Extract an archive file (zip file or tar file) stored on AWS S3

S3 Extract Extract an archive file (zip file or tar file) stored on AWS S3. Details Downloads archive from S3 into memory, then extract and re-upload

Evan 1 Dec 14, 2021
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 7, 2022
gitfs is a FUSE file system that fully integrates with git - Version controlled file system

gitfs is a FUSE file system that fully integrates with git. You can mount a remote repository's branch locally, and any subsequent changes made to the files will be automatically committed to the remote.

Presslabs 2.3k Jan 8, 2023
A python script to convert an ucompressed Gnucash XML file to a text file for Ledger and hledger.

README 1 gnucash2ledger gnucash2ledger is a Python script based on the Github Gist by nonducor (nonducor/gcash2ledger.py). This Python script will tak

Thomas Freeman 0 Jan 28, 2022
This is a file deletion program that asks you for an extension of a file (.mp3, .pdf, .docx, etc.) to delete all of the files in a dir that have that extension.

FileBulk This is a file deletion program that asks you for an extension of a file (.mp3, .pdf, .docx, etc.) to delete all of the files in a dir that h

Enoc Mena 1 Jun 26, 2022
Python package to read and display segregated file names present in a directory based on type of the file

tpyfilestructure Python package to read and display segregated file names present in a directory based on type of the file. Installation You can insta

Tharun Kumar T 2 Nov 28, 2021
Search for files under the specified directory. Extract the file name and file path and import them as data.

Search for files under the specified directory. Extract the file name and file path and import them as data. Based on that, search for the file, select it and open it.

G-jon FujiYama 2 Jan 10, 2022
Small-File-Explorer - I coded a small file explorer with several options

Petit explorateur de fichier / Small file explorer Pour la première option (création de répertoire) / For the first option (creation of a directory) e

Xerox 1 Jan 3, 2022
Pti-file-format - Reverse engineering the Polyend Tracker instrument file format

pti-file-format Reverse engineering the Polyend Tracker instrument file format.

Jaap Roes 14 Dec 30, 2022
Generates a clean .txt file of contents of a 3 lined csv file

Generates a clean .txt file of contents of a 3 lined csv file. File contents is the .gml file of some function which stores the contents of the csv as a map.

Alex Eckardt 1 Jan 9, 2022
Extract longest transcript or longest CDS transcript from GTF annotation file or gencode transcripts fasta file.

Extract longest transcript or longest CDS transcript from GTF annotation file or gencode transcripts fasta file.

laojunjun 13 Nov 23, 2022
File-manager - A basic file manager, written in Python

File Manager A basic file manager, written in Python. Installation Install Pytho

Samuel Ko 1 Feb 5, 2022
Two scripts help you to convert csv file to md file by template

Two scripts help you to convert csv file to md file by template. One help you generate multiple md files with different filenames from the first colume of csv file. Another can generate one md file with several blocks.

null 2 Oct 15, 2022
A simple Python code that takes input from a csv file and makes it into a vcf file.

Contacts-Maker A simple Python code that takes input from a csv file and makes it into a vcf file. Imagine a college or a large community where each y

null 1 Feb 13, 2022
This is a junk file creator tool which creates junk files in Internal Storage

This is a junk file creator tool which creates junk files in Internal Storage

KiLL3R_xRO 3 Jun 20, 2021
A simple file sharing tool written in python

Share it A simple file sharing tool written in python Installation If you are using Windows os you can directly Run .exe file --> download If you are

Sachit Yadav 7 Dec 16, 2022
An universal file format tool kit. At present will handle the ico format problem.

An universal file format tool kit. At present will handle the ico format problem.

Sadam·Sadik 1 Dec 26, 2021
Object-oriented file system path manipulation

path (aka path pie, formerly path.py) implements path objects as first-class entities, allowing common operations on files to be invoked on those path

Jason R. Coombs 1k Dec 28, 2022