Skip to content

Commit 4a1c779

Browse files
committed
Fixed logging date format, added post_log shortcut
1 parent ddac5af commit 4a1c779

File tree

7 files changed

+98
-21
lines changed

7 files changed

+98
-21
lines changed

pycaching/cache.py

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from pycaching.geo import Point
99
from pycaching.trackable import Trackable
1010
from pycaching.log import Log
11-
from pycaching.util import parse_date, rot13, lazy_loaded
11+
from pycaching.util import parse_date, format_date, rot13, lazy_loaded
1212

1313
# prefix _type() function to avoid colisions with cache type
1414
_type = type
@@ -532,28 +532,34 @@ def load_trackables(self, limit=float("inf")):
532532

533533
def _load_log_page(self):
534534
log_page = self.geocaching._request(self.log_page_url)
535-
# Find all valid log types for the cache
536-
type_options = log_page.find_all("option")
537-
valid_types = {o.get_text().lower(): o["value"] for o in type_options}
538535

539-
# Find all static data fields needed for log
540-
hidden_elements = log_page.find_all("input", type=["hidden", "submit"])
541-
return (valid_types, hidden_elements)
536+
# find all valid log types for the cache (-1 kicks out "- select type of log -")
537+
valid_types = {o.get_text().lower(): o["value"]
538+
for o in log_page.find_all("option") if o["value"] != "-1"}
539+
540+
# find all static data fields needed for log
541+
hidden_inputs = log_page.find_all("input", type=["hidden", "submit"])
542+
hidden_inputs = {i["name"]: i.get("value", "") for i in hidden_inputs}
543+
544+
# get user date format
545+
date_format = log_page.find(
546+
id="ctl00_ContentBody_LogBookPanel1_uxDateFormatHint").text.strip("()")
547+
548+
return valid_types, hidden_inputs, date_format
542549

543550
def post_log(self, l):
544551
if not l.text:
545552
raise errors.ValueError("Log text is empty")
546553

547-
valid_types, hidden = self._load_log_page()
554+
valid_types, hidden_inputs, date_format = self._load_log_page()
548555
if l.type.value not in valid_types:
549556
raise errors.ValueError("The Cache does not accept this type of log")
550-
post = {field["name"]: (field["value"] if field.has_attr("value") else "")
551-
for field in hidden}
552-
post["ctl00$ContentBody$LogBookPanel1$btnSubmitLog"] = "Submit Log Entry"
553557

554-
# fill form from Log object
558+
# assemble post data
559+
post = hidden_inputs
560+
post["ctl00$ContentBody$LogBookPanel1$btnSubmitLog"] = "Submit Log Entry"
555561
post["ctl00$ContentBody$LogBookPanel1$ddLogType"] = valid_types[l.type.value]
556-
post["ctl00$ContentBody$LogBookPanel1$uxDateVisited"] = l.visited.strftime("%m/%d/%Y")
562+
post["ctl00$ContentBody$LogBookPanel1$uxDateVisited"] = format_date(l.visited, date_format)
557563
post["ctl00$ContentBody$LogBookPanel1$uxLogInfo"] = l.text
558564

559565
self.geocaching._request(self.log_page_url, method="POST", data=post)

pycaching/geocaching.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
import logging
44
import requests
5+
import datetime
56
from urllib.parse import urljoin
67
from mechanicalsoup import Browser
78
from bs4 import BeautifulSoup
89
from pycaching.cache import Cache, Type, Size
10+
from pycaching.log import Log, Type as LogType
911
from pycaching.geo import Point
1012
from pycaching.trackable import Trackable
1113
from pycaching.errors import Error, NotLoggedInException, LoginFailedException
@@ -220,3 +222,8 @@ def load_cache(self, wp):
220222
def load_trackable(self, tid):
221223
"""Return a cache by its TID."""
222224
return Trackable(self, tid)
225+
226+
def post_log(self, wp, text, type=LogType.found_it, date=datetime.date.today()):
227+
"""Post log for cache."""
228+
l = Log(type=type, text=text, visited=date)
229+
self.load_cache(wp).post_log(l)

pycaching/log.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def visited(self, visited):
5252
if _type(visited) is str:
5353
visited = parse_date(visited)
5454
elif _type(visited) is not datetime.date:
55-
raise ValueError(
55+
raise errors.ValueError(
5656
"Passed object is not datetime.date instance nor string containing a date.")
5757
self._visited = visited
5858

pycaching/util.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python3
22

33
import logging
4+
import re
45
from datetime import datetime
56
from pycaching import errors
67

@@ -48,6 +49,23 @@ def parse_date(raw):
4849
raise errors.ValueError("Unknown date format - '{}'.".format(raw))
4950

5051

52+
def format_date(date, user_date_format):
53+
# parse user format
54+
date_format = user_date_format.lower()
55+
date_format = re.split("(\W+)", date_format)
56+
formats = {
57+
"dd": r'%d',
58+
"d": r'%-d',
59+
"mmm": r'%b',
60+
"mm": r'%m',
61+
"m": r'%-m',
62+
"yyyy": r'%Y',
63+
"yy": r'%y',
64+
}
65+
date_format = "".join((formats[c] if c in formats else c for c in date_format))
66+
return date.strftime(date_format)
67+
68+
5169
def get_possible_attributes():
5270
"""Returns dict of all possible attributes parsed from Groundspeak's website."""
5371

test/test_cache.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,19 @@ def test_load_logbook(self):
194194
for expected_author in ["Dudny-1995", "Sopdet Reviewer", "donovanstangiano83"]:
195195
self.assertIn(expected_author, log_authors)
196196

197+
def test_load_log_page(self):
198+
expected_types = set((t.value for t in (LogType.found_it, LogType.didnt_find_it, LogType.note)))
199+
expected_inputs = "__EVENTTARGET", "__VIEWSTATE" # and more ...
200+
expected_date_format = "d.M.yyyy"
201+
202+
# make request
203+
valid_types, hidden_inputs, user_date_format = self.c._load_log_page()
204+
205+
self.assertSequenceEqual(expected_types, set(valid_types.keys()))
206+
for i in expected_inputs:
207+
self.assertIn(i, hidden_inputs.keys())
208+
self.assertEqual(expected_date_format, user_date_format)
209+
197210
@mock.patch.object(Cache, "_load_log_page")
198211
@mock.patch.object(Geocaching, "_request")
199212
def test_post_log(self, mock_request, mock_load_log_page):
@@ -204,7 +217,7 @@ def test_post_log(self, mock_request, mock_load_log_page):
204217
"write note": "4",
205218
"didn't find it": "3",
206219
}
207-
mock_load_log_page.return_value = (valid_log_types, {})
220+
mock_load_log_page.return_value = (valid_log_types, {}, "mm/dd/YYYY")
208221
test_log_text = "Test log."
209222

210223
with self.subTest("empty log text"):

test/test_geocaching.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ def setUpClass(cls):
116116
def test_login(self):
117117
pycaching.login(_username, _password)
118118

119+
def test_geocode(self):
120+
ref_point = Point(49.74774, 13.37752)
121+
self.assertLess(great_circle(self.g.geocode("Pilsen"), ref_point).miles, 10)
122+
119123
def test_load_cache(self):
120124
c = self.g.load_cache("GC4808G")
121125
self.assertEqual("Nekonecne ticho", c.name)
@@ -124,6 +128,6 @@ def test_load_trackable(self):
124128
t = self.g.load_trackable("TB1KEZ9")
125129
self.assertEqual("Lilagul #2: SwedenHawk Geocoin", t.name)
126130

127-
def test_geocode(self):
128-
ref_point = Point(49.74774, 13.37752)
129-
self.assertLess(great_circle(self.g.geocode("Pilsen"), ref_point).miles, 10)
131+
def test_post_log(self):
132+
# I refuse to write 30 lines of tests (mocking etc.) because of simple 2 lines of code
133+
pass

test/test_util.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import unittest
44
import datetime
55
import itertools
6-
from pycaching.util import rot13, parse_date, get_possible_attributes
6+
from pycaching.util import rot13, parse_date, format_date, get_possible_attributes
77

88

99
class TestModule(unittest.TestCase):
@@ -12,8 +12,8 @@ def test_rot13(self):
1212
self.assertEqual(rot13("Text"), "Grkg")
1313
self.assertEqual(rot13("abc'ř"), "nop'ř")
1414

15-
def test_date_parsing(self):
16-
dates = (datetime.date(2014, 1, 30), datetime.date(2000, 1, 1), datetime.date(2020, 12, 13))
15+
def test_parse_date(self):
16+
dates = datetime.date(2014, 1, 30), datetime.date(2000, 1, 1), datetime.date(2020, 12, 13)
1717
patterns = ("%Y-%m-%d", "%Y/%m/%d", "%m/%d/%Y", "%d/%m/%Y",
1818
"%d.%m.%Y", "%d/%b/%Y", "%d.%b.%Y", "%b/%d/%Y", "%d %b %y")
1919

@@ -22,6 +22,35 @@ def test_date_parsing(self):
2222
formatted_date = datetime.datetime.strftime(date, pattern)
2323
self.assertEqual(date, parse_date(formatted_date))
2424

25+
def test_format_date(self):
26+
date = datetime.date(2015, 1, 30)
27+
cases = {
28+
"d. M. yyyy": "30. 1. 2015",
29+
"d.M.yyyy": "30.1.2015",
30+
"d.MM.yyyy": "30.01.2015",
31+
"d/M/yy": "30/1/15",
32+
"d/M/yyyy": "30/1/2015",
33+
"d/MM/yyyy": "30/01/2015",
34+
"dd MMM yy": "30 Jan 15",
35+
"dd.MM.yyyy": "30.01.2015",
36+
"dd.MM.yyyy.": "30.01.2015.",
37+
"dd.MMM.yyyy": "30.Jan.2015",
38+
"dd/MM/yy": "30/01/15",
39+
"dd/MM/yyyy": "30/01/2015",
40+
"dd/MMM/yyyy": "30/Jan/2015",
41+
"dd-MM-yy": "30-01-15",
42+
"dd-MM-yyyy": "30-01-2015",
43+
"d-M-yyyy": "30-1-2015",
44+
"M/d/yyyy": "1/30/2015",
45+
"MM/dd/yyyy": "01/30/2015",
46+
"MMM/dd/yyyy": "Jan/30/2015",
47+
"yyyy.MM.dd.": "2015.01.30.",
48+
"yyyy/MM/dd": "2015/01/30",
49+
"yyyy-MM-dd": "2015-01-30",
50+
}
51+
for user_format, ref_result in cases.items():
52+
self.assertEqual(format_date(date, user_format), ref_result)
53+
2554
def test_get_possible_attributes(self):
2655
attributes = get_possible_attributes()
2756

0 commit comments

Comments
 (0)