-
Notifications
You must be signed in to change notification settings - Fork 97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add batch move with formatting of folder names #826
base: main
Are you sure you want to change the base?
Conversation
I've never used |
Good question. From what I understood, the purpose of each is a bit different: you want In other words, having this document:
The functionality overlaps a lot, I mean, it's just |
Yeah, I don't really know why they were separate to begin with either. I'm mostly inclined to just have either For similar semantics, there is a
|
I agree. I would prefer just having a single For instance, with the "git-like" semantics, ie. a single command to move and rename, imagine you have two docs, so "alex-2004-foo" and "alex-2004-bar", I guess if you do But if you do Just thinking out loud. How would you expect this all to work? I think we should invite other to the conversation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs a bit more work and I left a few comments and suggestions. Probably worth leaving them alone until we decide what to do with rename
vs mv
though.
The ``mv`` command is used to organise a library moving their documents inside | ||
subfolders, which can be optionally created from a format adapted to each | ||
document. | ||
|
||
It will (except when run with ``--all``) bring up the picker with a list of | ||
documents that match the query. In the picker, you can select one or more | ||
documents and then initiate renaming by pressing enter. Folder names are cleaned, | ||
so that various characters (white spaces, punctuation, capital letters, and some | ||
others) are automatically converted. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ``mv`` command is used to organise a library moving their documents inside | |
subfolders, which can be optionally created from a format adapted to each | |
document. | |
It will (except when run with ``--all``) bring up the picker with a list of | |
documents that match the query. In the picker, you can select one or more | |
documents and then initiate renaming by pressing enter. Folder names are cleaned, | |
so that various characters (white spaces, punctuation, capital letters, and some | |
others) are automatically converted. | |
The ``mv`` command is used to organise a library by moving the documents. | |
Unlike the ``rename`` command, this command can move documents to subfolders | |
and has richer formatting options. When used, it will (except when run with | |
``--all``) bring up the picker with a list of documents that match the query. | |
In the picker, you can select one or more documents and then initiate the move | |
by pressing enter. Folder names are cleaned, so that various characters (white | |
spaces, punctuation, capital letters, and some others) are automatically converted | |
(see :confval:`doc-paths-extra-chars`). |
- You can use formatting rules to generate the folder name too. For instance, to | ||
organise all documents by author "Rick Astley" by year, you can use (Python and | ||
Jinja2 formatting, respectively): | ||
|
||
.. code:: sh | ||
|
||
# Python format | ||
papis mv --folder-name "{doc[year]}" --all author:"Rick Astley" | ||
|
||
# Jinja2 format | ||
papis mv --folder-name "{{doc.year}}" --all author:"Rick Astley" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- You can use formatting rules to generate the folder name too. For instance, to | |
organise all documents by author "Rick Astley" by year, you can use (Python and | |
Jinja2 formatting, respectively): | |
.. code:: sh | |
# Python format | |
papis mv --folder-name "{doc[year]}" --all author:"Rick Astley" | |
# Jinja2 format | |
papis mv --folder-name "{{doc.year}}" --all author:"Rick Astley" | |
- You can use formatting rules to generate the folder name too. For instance, to | |
organise all documents by author "Rick Astley" by year, you can use: | |
.. code:: sh | |
papis mv --folder-name "{doc[year]}" --all author:"Rick Astley" |
All the examples in the documentation use the Python formatter, because that's the default one. Since the formatter is essentially a plugin, it feels weird to just single out the python and jinja2 ones to give examples of here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough! I'll use this change then!
raise DocumentFolderNotFound(papis.document.describe(document)) | ||
|
||
if git: | ||
papis.git.mv(folder, new_folder_path) | ||
papis.git.mv(doc_main_folder, dest_path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should also commit? What do you think?
My thought is that if it just moves, then you'd need to call a separate papis git -p <QUERY> commit "Moved ?? from ?? to ??"
or something like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if the file is tracked it should commit, yes.
if dest_path is None: | ||
logger.error("Failed to construct a folder path for the document.") | ||
return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if dest_path is None: | |
logger.error("Failed to construct a folder path for the document.") | |
return | |
if not dest_path: | |
logger.error("Failed to construct a folder path for the document: %s", | |
papis.document.describe(document)) | |
continue |
? To just skip the document?
I'm never sure how to handle this, e.g. the update command will exit right away if it fails to update some key in some document, but most other commands will skip it and continue.
if folder_name is not None: | ||
subfolder = papis.format.format(folder_name, document) | ||
dest_path = os.path.join(lib_dir, subfolder) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If folder_name
is None, shouldn't we just skip the whole thing? i.e. this check should be moved outside the loop and return.
Then here it should check if subfolder
, i.e. if the formatting didn't end up making an empty string for whatever reason and warn about that.
for document in documents: | ||
# If the user has passed ``--folder-name``, use that to generate the subfolder | ||
if folder_name is not None: | ||
subfolder = papis.format.format(folder_name, document) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This not correct, since the document keys can contain additional path separators. There's similar logic in papis add
that we should refactor and reuse here.
For example, if the title is Face/Off
and the folder format is {doc[year]}/{doc[title]}
we'll get unexpected results.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes! And also special characters. I'll see what add
does and adapt for here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tried to do some refactoring in #829 to extract that logic. Probably needs a bit more work, but it should be doable to use it here as well.
if not os.path.exists(dest_path): | ||
logger.info("Creating path '%s'.", dest_path) | ||
os.makedirs(dest_path, mode=papis.config.getint("dir-umask") or 0o666) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this correctly check that we're not overwritting existing documents? I didn't see any checks in run()
either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it does! It should check and skip the move, like rename
(IIRC) does.
I think
Maybe this can work similarly to how Would that make sense? Not sure if the behavior of |
Thanks you so much for the review!
Yes, it looks like it doesn't handle file renames, additions or deletions, as per the TODO comment there. You can change the files list but it won't do anything to the files. Plus you have to pass Python-style syntax, but with all the shell quoting needed.
To be honest, I don't know what would be better for a user. I would be perfectly comfortable having just |
I'm not sure I follow, what does "Python-style syntax" mean here? (I haven't actually used
Hm, I don't think I was clear here, since it sounds to me like I agree with what you're saying. What I was suggesting was a way to resolve some ambiguity that you mentioned (?) when doing a In that case, I would suggest adding a
I think that's similar to how the |
I was just rambling about
Yep! Having |
Ah, yeah, I didn't quite like that either: the command-line string is quite clunky (like in your example) and then you also need to do an Suggestions are very welcome!
Yeah, that sounds good, we can definitely merge this with whatever fixes you have time to make from my comments above. It's already a pretty big improvement on the previous state of |
I finally managed to have a look at the conversation here!
In general I think there is sense in having a distinction between
Yeah, I don't think we need Btw, you don't need to use |
That's a good point! I'm also ok with just keeping both as long as the code is heavily shared. (Don't need to do it as part of this PR, but something to keep in mind)
Ha! I didn't realize that you can just use
I would definitely appreciate making that clearer in the docs 😁 |
The other day I was adding changes to
mv
, and I realised I never used it since I didn't understand how it was supposed to work. Also, compared to a file manager, maybe a command that can only handle one document at a time is very underpowered.So, today I wanted to change it more or less in line with what I did with
rename
.This PR implements:
--all
like in other documents--batch
to skip confirmation (what do you think: better to prompt at all? prompt before each move? or before executing the batch?)Let me show you what it does:
papis -l test mv -a --folder-name "{{doc.year}}"
:As for the code, I tried to make it a bit more verbose, since these operations are quite delicate. So there are some more checks such as
Document.get_main_folder()
and the like.I am reusing the
--folder-name
flag, although it's not defaulting toadd-folder-name
. Do you find it confusing?I am trying a new small feature: if the library is under version control, I think it might be a good idea to prompt the user if they haven't supplied the
--git
option. If you think this might be a good idea, maybe it would be cool to also have an option in the config file to always add the changes or to never ask.Tell me what you think about this PR! 😄