Repository: wimglenn/advent-of-code-data

Issue:
Add typecheck (int, float, str) to aocd.models.puzzle.answer_a/b?
An answer was rejected when send through aocd, when using a **float** (w/o siginificant digits behind comma) as an input,  while it was accepted afterwards from the website, with an **int** as an input, with the same value.  AFAIK, AoC never had any non-whole numbers as the answer in the past?
Expected behaviour: 
An int is sent to the website, rather than the float

Potential solution:
Idk how to best catch this unexpected behaviour.
Should the library always send an int?  Or do input typecheck on top of valuecheck and give a warning?Maybe change a float answer with mod(X,1)==0 (no significant digits behind comma)  to INT? 

Example for 6/'22, part A:
![image](https://user-images.githubusercontent.com/6223082/206266259-f7987463-71f4-4c24-a5c5-f67357c1139d.png)
![image](https://user-images.githubusercontent.com/6223082/206266177-0bdd38fd-5135-46a6-b5fb-b2d6a3894669.png)

Same behaviour for other days 15/'21, for example part B: 
![image](https://user-images.githubusercontent.com/6223082/206268662-d58c7fa0-8adc-4765-b9df-07755669e694.png)
![image](https://user-images.githubusercontent.com/6223082/206268586-5db65729-5d54-44e9-9ac7-e17e36192051.png) was accepted
![image](https://user-images.githubusercontent.com/6223082/206269015-e703b962-d72a-4619-9267-51147dc71cda.png)

if you need more info, let me know.
(& kudos for the amazing library!)


Discussion:

Patch:
diff --git a/aocd/models.py b/aocd/models.py
index 8b13adf..6b80c7c 100644
--- a/aocd/models.py
+++ b/aocd/models.py
@@ -261,6 +261,14 @@ class Puzzle(object):
             template = "<{0}({1.year}, {1.day}) at {2} - {1.title}>"
             p.text(template.format(type(self).__name__, self, hex(id(self))))
 
+    def _coerce_val(self, val):
+        if isinstance(val, float) and val.is_integer():
+            log.warning("coerced value %r for %d/%02d", val, self.year, self.day)
+            val = int(val)
+        if isinstance(val, int):
+            val = str(val)
+        return val
+
     @property
     def answer_a(self):
         try:
@@ -270,8 +278,7 @@ class Puzzle(object):
 
     @answer_a.setter
     def answer_a(self, val):
-        if isinstance(val, int):
-            val = str(val)
+        val = self._coerce_val(val)
         if getattr(self, "answer_a", None) == val:
             return
         self._submit(value=val, part="a")
@@ -289,8 +296,7 @@ class Puzzle(object):
 
     @answer_b.setter
     def answer_b(self, val):
-        if isinstance(val, int):
-            val = str(val)
+        val = self._coerce_val(val)
         if getattr(self, "answer_b", None) == val:
             return
         self._submit(value=val, part="b")
@@ -325,7 +331,8 @@ class Puzzle(object):
     def _submit(self, value, part, reopen=True, quiet=False):
         if value in {u"", b"", None, b"None", u"None"}:
             raise AocdError("cowardly refusing to submit non-answer: {!r}".format(value))
-        value = str(value)
+        if not isinstance(value, str):
+            value = self._coerce_val(value)
         part = str(part).replace("1", "a").replace("2", "b").lower()
         if part not in {"a", "b"}:
             raise AocdError('part must be "a" or "b"')
diff --git a/aocd/version.py b/aocd/version.py
index 9c73af2..f708a9b 100644
--- a/aocd/version.py
+++ b/aocd/version.py
@@ -1,1 +1,1 @@
-__version__ = "1.3.1"
+__version__ = "1.3.2"


Test patch:
diff --git a/tests/test_submit.py b/tests/test_submit.py
index dcf1d35..2a89271 100644
--- a/tests/test_submit.py
+++ b/tests/test_submit.py
@@ -1,4 +1,5 @@
 import errno
+import logging
 
 import pytest
 from termcolor import colored
@@ -252,11 +253,19 @@ def test_will_not_submit_null():
         submit(None, part="a")
 
 
-@pytest.mark.answer_not_cached(rv='value')
+@pytest.mark.answer_not_cached(rv="value")
 def test_submit_guess_against_saved(requests_mock, capsys):
-    post = requests_mock.post(
-        url="https://adventofcode.com/2018/day/1/answer",
-        text="<article>That's the right answer. Yeah!!</article>",
-    )
+    post = requests_mock.post(url="https://adventofcode.com/2018/day/1/answer")
     submit(1234, part="a", day=1, year=2018, session="whatever", reopen=False)
     assert post.call_count == 0
+
+
+def test_submit_float_warns(requests_mock, capsys, caplog):
+    post = requests_mock.post(
+        url="https://adventofcode.com/2022/day/8/answer",
+        text="<article>yeah</article>",
+    )
+    submit(1234.0, part="a", day=8, year=2022, session="whatever", reopen=False)
+    assert post.call_count == 1
+    log_record = ("aocd.models", logging.WARNING, "coerced value 1234.0 for 2022/08")
+    assert log_record in caplog.record_tuples