bookwyrm
bookwyrm copied to clipboard
Allow dates to be only year
Is your feature request related to a problem? Please describe. Currently dates need to be a full date with day, month and year.
Describe the solution you'd like To be able to to set only year, since a lot of older or not so known books don't have that specific data available.
Describe alternatives you've considered N/A
Additional context N/A
#863 doesn't do this, but it's adjacent
i got a dev environment set up and looked around the codebase a bit; i'd love to work on this! how would you want it implemented in terms of ui/database though? a date input can't be used for just a year (and likewise the DateTimeField
uses datetime
which doesn't support just a year). i was thinking maybe using a toggle switch to change the form to use a number input (sorry for the quick terrible mockup)
and then have the form's clean
method check the toggle box, and if set, set the date to datetime(year, 1, 1)
along with an additional year_only
flag set to true
does that sound alright? if there are other ideas please lmk :)
alternatively, could drop the toggle switch and just display both inputs. might be a little confusing in the ui, but would be less code on the frontend and backend
I think the toggle is a good idea, if it isn't too much effort to get the frontend stuff working for it. I wish there was a better database solution to this (since year-only dates will be indistinguishable from books actually published on January 1st), but I'm not thinking of one! Which is all to say, go for it @void-witch
cool! yeah, the only "better" solution i can think of would be a custom field that saves to a string, but that's super complex and also you lose db optimizations because of not using a date type i'll look into it and report back when i have something! :3
This issue is still open but the associated PR is closed (rather than merged) and the user working on it is no longer registered. @arkhi @mouse-reeve do you know the current status? This kinda annoys me every time I add a book because I only ever have the year of publication (which usually is all we want), so I'd be happy to assist if help is needed.
@hughrun: As far as I know (from a few months back), this issue is stalled and could definitely use a hand. Thanks for bringing that up!
The change to the date picker widget that I have a PR for above should make the UI aspect of this much more straightforward. The form validation still requires a full date at this point, however.
Instead of using the flag … would it be an option to split the field up into three parts? Akin https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date#html
Implementation wise that would be a custom input instead of Django's native ones.
Yep! We had the exact same idea, and a couple months ago I switched to a custom three part Django form widget for publication dates. The fields are still all required, but from a UI perspective it's ready for that, and the flag is no longer necessary.
Current UI:
Any news on this? Just ran into the same issue when I tried to manually add a book and it wouldn't accept 2021 as publishing date, but I don't know the exact day it was published on.
Unfortunately this is still stalled (it bugs me all the time, too). I had a vague recollection of there being a way to store dates like this in Postgres but I can't find it and now I wonder if it was just wishful thinking. Barring that, perhaps the best solution is to store the date precision as an enum (down to the year, month, or day) in addition to the date, and default to January 1st for absent information. From the user's perspective, you would leave the day and/or month field blank, in the database it would be stored as a regular date, and then in the display of the book, only the relevant part of the date would be shown.
If we're storing a date
object in Postgres it has to have a day and month:
https://www.postgresql.org/docs/current/datatype-datetime.html
I was working on this a couple of months ago but gave up because I couldn't work out how to set a default value if the input is empty when the user saves the form. As far as I can tell that's the only solution: set any empty values to 01
before storing in the database.
If the data is not necessarily a date (just YYYY, a YYYY-MM or a full date) and it’s annoying for everyone, then maybe the data type is not the right one?
Could this be three fields made of numbers (0-31, 1-12, -infity to current year) and that’s it? That would allow to not add erroneous data (January 1st) in many cases. In case of a full date, the switch mentioned in a previous comment could work too but that would make the DB more difficult to deal with, I guess.
Drawback are invalid dates such as 31st February …
Could this be three fields made of numbers (0-31, 1-12, -infity to current year) and that’s it? That would allow to not add erroneous data (January 1st) in many cases. In case of a full date, the switch mentioned in a previous comment could work too but that would make the DB more difficult to deal with, I guess.
Drawback are invalid dates such as 31st February …
Since data should be checked before being stored anyway, what would be the roadblock with checking for validity before the date or its components are stored?
Dates can be tricky: https://github.com/kdeldycke/awesome-falsehood#dates-and-time
Just saying. I see a lot of maintenance burden here.
Dates are tricky overall, indeed, but we’re not aiming for precision here, quite the opposite. :)
I’m no python developer but wouldn’t a dateutil.parser.isoparse be enough?
I defer to @mouse-reeve for a decision. Keep in mind that migrations are likely necessary. They has a better connection to fellow instances to make a judgement call, whether it is worth it.
The reality is that the vast majority of bibliographic metadata in the world only lists a publication year, or at most a month as well. The only reason to care about a specific day is that the Bookwyrm database field was originally a datetime.
As I see it, both database-level solutions (either using a datetime field and storing the precision, or storing the year and month and day as separate date fields) have advantages and disadvantages, and it's not super obvious which is better in the application as it is. I'm favoring using a datetime field and precision enum because it would mean that it's not super hard to do date math. If you're manipulating the dates in python, @arkhi is quite right that parsing the date after loading the object would be a simple solution. However, if you wanted to query, for example, every book published between 3 and 5 years ago, or sort a queryset by publication date, you'd have a very challenging query ahead of you.
Since querying based on publication time seems like a pretty expected use case to me (it may also be how the author page is sorted, I don't recall off the top of my head), I'm inclined to keep the database field as a datetime field. Does that make sense to yall? Am I missing something in my reasoning? Extremely grateful for all your input ❤️
I think there may possibly be a solution that pleases everyone here.
@mouse-reeve's explanation makes sense. Generally dates should always be Date
s as @Ryuno-Ki points out above.
However the original point of this issue is that it's a crappy user experience to have to enter full dates, given that most of the time the publication month and day are unknown to the user and almost everyone else. If seems unlikely that there would be a pressing use-case for searching publication dates with more specificity than a year, so can we set the value of a "blank" day or month to 1
such that if a user enters only a year, Bookwyrm set the date to 1 January {year}?
I did some testing and it seems this might be as easy as swapping out two lines of code in SelectDateWidget
within forms/widgets.py
:
# old
if not self.is_required:
month_choices.insert(0, self.month_none_value) # self.month_none_value equates to "(0, '---')"
# new
if not self.is_required:
month_choices.insert(0, (1, '---'))
I find Django widgets a little bamboozling, however, so it's possible this might cause problems I haven't anticipated.
Agreed! I don't think there's anything up in the air about how the UI should work from the user's perspective; you should be able to leave the day and/or month blank as fitting. The reason for the database discussion is, once you've create a date that's like 2022-01-01, how do you know if that's a book that was published in 2022, or a book that was published in January of 2022. If the year really only ever mattered, it would be a trivial problem, but I do think there can be publication dates that are meaningful down to the day (when I'm waiting for the next book in a series, for example, I care very much when specifically it will get released).
I'm glad you dug into that widget; I think I copy/pasted it nearly wholesale from the django source code so that's a valuable insight into how it works even though git blame acts like I wrote it 😂
Once we get around to fixing this on a database level, #2660 needs attention as well given that any book coming in via ActivityPub's federation, or the connectors which transform the data into ActivityPub's format first, uses dateutil.parser.parse
which fills in the blanks with the current date and turns a string like "2022"
into datetime.datetime(2022, 2, 13, 0, 0)
(Feb 13 being the date today).
I've been low-key thinking about this a bit over the past days. What do you think about this proposal:
We create a new Django field type. Naming is hard, but let's say for now we'd call it DateWithNullablePartsField
, it would work like this:
- on a database level it's a plain old string storing the date as
YYYY-MM-DD
- in Python the field is a value object with data accessors for
.day
,.month
,.year
- Day and month parts would be allowed to be null, not year, in which case the whole field and column should be null
- when translating the field from Python to the value that Django will send to the database we:
- construct an ephemeral string with the format
YYYY-MM-DD
field, important here is that for nullable months and days we use the value01
, because: - we parse this ephemeral string into a Python date with a strict parser (not
dateutil
) to validate the date - if valid, construct a similar string, but replace the nullable parts with
00
(i.e.2023-02-00
for February 2023) - send this string with the null parts zeroed out to Django to send to the database
- construct an ephemeral string with the format
- when translating the field from the database to Python we:
- parse the string with a regex (since
2023-02-00
is an invalid date) - fill in the values into the value object
- parse the string with a regex (since
I'm still not entirely sure how to support ordering based on the first-/published dates with those field types. Maybe the solution to that could be that we store two columns each, one as described above, and one as an actual date field where the nullable parts default to 01
. We could then use that date field for ordering.
I have two commits in this draft PR each showcasing two separate solutions to this. Both are proofs of concept and only deal with the Book edit form rendering and submitting. They don't include any changes for where we render the dates or federating them out to other instances.
I will humbly remove the "good first issue" label because this is anything but easy 😅
Just ran into this again. tbh it's baffling, I don't think I've ever seen any book that had a full YYYY MM DD date in it, only ever years.
I'm manually importing all the niche books from my Goodreads import that aren't in the database (speaking of which: Inventaire often imports complete word salad), and the inability to file books with only a year is currently messing it all up.
I’m mentioning my previous proposal again. :)
Just ran into this again. tbh it's baffling, I don't think I've ever seen any book that had a full YYYY MM DD date in it, only ever years.
@mxamber: I can give you a couple of example:
- https://bookwyrm.social/book/487779/s/skating-polly
- https://bookwyrm.social/book/860271/s/la-tristesse-de-lelephant
- https://bookwyrm.social/book/894692/s/la-societe-vue-du-don
- https://bookwyrm.social/book/361882/s/le-monde-sans-fin
- https://bookwyrm.social/book/846250/s/la-tyrannie-du-divertissement
- https://bookwyrm.social/book/567256/s/culottees-2
- https://bookwyrm.social/book/645428/s/dictionnaire-bilingue-lsffrancais
- https://bookwyrm.social/book/618692/s/parce-que-les-tatouages-sont-notre-histoire
- https://bookwyrm.social/book/303166/s/lorigine-du-monde
- https://bookwyrm.social/book/611457/s/le-traite-de-miamologie-les-legumes
Most of the books I added recently, in fact. The dates come from the Publisher’s website.