The floatformat filter sometimes returns "-0" rather than "0"

2019-09-12 Thread Sky Christensen

Hi,

I'd like to discuss reopening this wontfix'ed ticket: 
https://code.djangoproject.com/ticket/30761


The issue is that for values between 0 and -0.5, the floatformat filter 
returns "-0", whereas I think that most people would expect it to return 
"0".


The ticket was wontfix'ed because "this behavior is consistent with 
builtin round() and -0 exists in floating-point arithmetic".


Can I propose that in this case the better way to decide the result that 
it should produce is to ask what an ordinary person would expect to see, 
rather than what's consistent with a particular Python function?


When humans round -0.3 to the nearest whole number, we express the 
result as 0, not -0. Given that the point of template tags and filters 
is to make data more human-readable, I think returning "0" is preferable 
in this case than returning "-0".


If this ticket is reopened, I'll be happy to submit a patch for it.

Cheers,

Sky

--
You received this message because you are subscribed to the Google Groups "Django 
developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/aec9450abcb511a0bb4a020c770a0483%40skychristensen.com.


Re: The floatformat filter sometimes returns "-0" rather than "0"

2019-09-12 Thread Kye Russell
Sounds reasonable. As you said, the template tags (well, this one) seem to be 
to prepare things for human consumption, not to expose computer science 
intricacies.

If I saw this presented on a website, as a ‘developer-user’, I’d probably 
consider it a bug. I’m unsure of other uses of this filter though.

Kye Russell
Sent from my iPhone

> On 12 Sep 2019, at 12:54 pm, Sky Christensen  wrote:
> 
> Hi,
> 
> I'd like to discuss reopening this wontfix'ed ticket: 
> https://code.djangoproject.com/ticket/30761
> 
> The issue is that for values between 0 and -0.5, the floatformat filter 
> returns "-0", whereas I think that most people would expect it to return "0".
> 
> The ticket was wontfix'ed because "this behavior is consistent with builtin 
> round() and -0 exists in floating-point arithmetic".
> 
> Can I propose that in this case the better way to decide the result that it 
> should produce is to ask what an ordinary person would expect to see, rather 
> than what's consistent with a particular Python function?
> 
> When humans round -0.3 to the nearest whole number, we express the result as 
> 0, not -0. Given that the point of template tags and filters is to make data 
> more human-readable, I think returning "0" is preferable in this case than 
> returning "-0".
> 
> If this ticket is reopened, I'll be happy to submit a patch for it.
> 
> Cheers,
> 
> Sky
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "Django developers  (Contributions to Django itself)" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to django-developers+unsubscr...@googlegroups.com.
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/django-developers/aec9450abcb511a0bb4a020c770a0483%40skychristensen.com.

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/1F8DEBF9-FBEB-4A1C-BE3B-CA9D4507E702%40kye.id.au.


Re: The floatformat filter sometimes returns "-0" rather than "0"

2019-09-12 Thread Adam Johnson
+1 to swapping -0 for 0

On Thu, 12 Sep 2019 at 12:44, Kye Russell  wrote:

> Sounds reasonable. As you said, the template tags (well, this one) seem to
> be to prepare things for human consumption, not to expose computer science
> intricacies.
>
> If I saw this presented on a website, as a ‘developer-user’, I’d probably
> consider it a bug. I’m unsure of other uses of this filter though.
>
> Kye Russell
> Sent from my iPhone
>
> > On 12 Sep 2019, at 12:54 pm, Sky Christensen 
> wrote:
> >
> > Hi,
> >
> > I'd like to discuss reopening this wontfix'ed ticket:
> https://code.djangoproject.com/ticket/30761
> >
> > The issue is that for values between 0 and -0.5, the floatformat filter
> returns "-0", whereas I think that most people would expect it to return
> "0".
> >
> > The ticket was wontfix'ed because "this behavior is consistent with
> builtin round() and -0 exists in floating-point arithmetic".
> >
> > Can I propose that in this case the better way to decide the result that
> it should produce is to ask what an ordinary person would expect to see,
> rather than what's consistent with a particular Python function?
> >
> > When humans round -0.3 to the nearest whole number, we express the
> result as 0, not -0. Given that the point of template tags and filters is
> to make data more human-readable, I think returning "0" is preferable in
> this case than returning "-0".
> >
> > If this ticket is reopened, I'll be happy to submit a patch for it.
> >
> > Cheers,
> >
> > Sky
> >
> > --
> > You received this message because you are subscribed to the Google
> Groups "Django developers  (Contributions to Django itself)" group.
> > To unsubscribe from this group and stop receiving emails from it, send
> an email to django-developers+unsubscr...@googlegroups.com.
> > To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-developers/aec9450abcb511a0bb4a020c770a0483%40skychristensen.com
> .
>
> --
> You received this message because you are subscribed to the Google Groups
> "Django developers  (Contributions to Django itself)" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to django-developers+unsubscr...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-developers/1F8DEBF9-FBEB-4A1C-BE3B-CA9D4507E702%40kye.id.au
> .
>


-- 
Adam

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/CAMyDDM3FhTEUmWmpHaAqHuRxCbqHJcH6riS9vQ%2BNjhx3LcGhPA%40mail.gmail.com.


Work in progress (WIP), ticket #28935, Template error raised in an {% extends %} child template shows incorrect source location on debug page

2019-09-12 Thread Min ho Kim
Hi,
I am trying to contribute to code.

I picked my first issue to solve.
https://code.djangoproject.com/ticket/28935
And wanted to solve for the last few weeks, and finally kinda fixed it, but I 
don't know whether it's good enough.
Feels like it's just monkey patched of something.
Please have a look.

To summarise the issue #28935:
When "extends" and "include" template tags are used together in same html page, 
if "include" tries to include nonexistent html file, 
error page gives incorrect information (filename and line number) under the 
heading "Error during template rendering".

For example,

[base.html]
{% block content %}
{% endblock %}


[home.html]

{% extends 'base.html' %}
{% block content %}
{% include "./nonexistent.html" %}
{% endblock %}

This gives 

Error during template rendering
In template /.../base.html, error at line 0 
nonexistent.html

But the expected error should be

In template /.../home.html, error at line 3
nonexistent.html

So here's my code change:

[django > template > context.py ]
# made a new global variable at the top in context.py
template_stack = []

[django > template > base.py > Node > render_annotated]
def render_annotated(self, context):
try:
return self.render(context)
except Exception as e:
if context.template.engine.debug and not hasattr(e, 'template_debug'):
from django.template.context import template_stack

# template_stack[-1].name ==> "base.html"
# template_stack[-2].nodelist[0].token.contents ==> "extends 
'base.html"
try:
if  len(template_stack) >= 2 and "extends" in 
template_stack[-2].nodelist[0].token.contents and  template_stack[-1].name in 
template_stack[-2].nodelist[0].token.contents:
e.template_debug = context.template.get_exception_info(e, 
self.token)
else:
e.template_debug = 
context.render_context.template.get_exception_info(e, self.token)
except (AttributeError, IndexError, KeyError):
e.template_debug = 
context.render_context.template.get_exception_info(e, self.token)
raise



First of all, there is 1 test failure. 
But if I manually test the failed one by making exact same files being tested, 
error page seems to be Okay.

Second, is this ways of code change acceptable?

==
FAIL: test_compile_tag_error_27956 (template_tests.tests.TemplateTests)
Errors in a child of {% extends %} are displayed correctly.
--
Traceback (most recent call last):
  File 
"/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py",
 line 59, in testPartExecutor
yield
  File 
"/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py",
 line 628, in run
testMethod()
  File "/Users/minhokim/Code/Repo/django/tests/template_tests/tests.py", line 
135, in test_compile_tag_error_27956
self.assertEqual(e.exception.template_debug['during'], '{% badtag %}')
  File 
"/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py",
 line 852, in assertEqual
assertion_func(first, second, msg=msg)
  File 
"/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py",
 line 845, in _baseAssertEqual
raise self.failureException(msg)
AssertionError: 'nt.html" %}\n' != '{% badtag %}'

--

Thank you.

- Min ho Kim

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/e4b1c45a-8750-4ae4-8985-dd09421d06a8%40googlegroups.com.


Re: Work in progress (WIP), ticket #28935, Template error raised in an {% extends %} child template shows incorrect source location on debug page

2019-09-12 Thread Carlton Gibson
Hello! Super. 🙂

Can I ask you to put it in a PR on GitHub, so it’s easier to test and review? 

Thanks. 

Kind Regards,

Carlton


> On 12 Sep 2019, at 19:09, Min ho Kim  wrote:
> 
> Hi,
> I am trying to contribute to code.
> 
> I picked my first issue to solve.
> https://code.djangoproject.com/ticket/28935
> And wanted to solve for the last few weeks, and finally kinda fixed it, but I 
> don't know whether it's good enough.
> Feels like it's just monkey patched of something.
> Please have a look.
> 
> To summarise the issue #28935:
> When "extends" and "include" template tags are used together in same html 
> page, if "include" tries to include nonexistent html file, 
> error page gives incorrect information (filename and line number) under the 
> heading "Error during template rendering".
> 
> For example,
> 
> [base.html]
> {% block content %}
> {% endblock %}
> 
> 
> [home.html]
> 
> {% extends 'base.html' %}
> {% block content %}
> {% include "./nonexistent.html" %}
> {% endblock %}
> 
> This gives 
> 
> Error during template rendering
> In template /.../base.html, error at line 0 
> nonexistent.html
> 
> But the expected error should be
> 
> In template /.../home.html, error at line 3
> nonexistent.html
> 
> So here's my code change:
> 
> [django > template > context.py ]
> # made a new global variable at the top in context.py
> template_stack = []
> 
> [django > template > base.py > Node > render_annotated]
> def render_annotated(self, context):
>try:
>return self.render(context)
>except Exception as e:
>if context.template.engine.debug and not hasattr(e, 'template_debug'):
>from django.template.context import template_stack
> 
># template_stack[-1].name ==> "base.html"
># template_stack[-2].nodelist[0].token.contents ==> "extends 
> 'base.html"
>try:
>if  len(template_stack) >= 2 and "extends" in 
> template_stack[-2].nodelist[0].token.contents and  template_stack[-1].name in 
> template_stack[-2].nodelist[0].token.contents:
>e.template_debug = context.template.get_exception_info(e, 
> self.token)
>else:
>e.template_debug = 
> context.render_context.template.get_exception_info(e, self.token)
>except (AttributeError, IndexError, KeyError):
>e.template_debug = 
> context.render_context.template.get_exception_info(e, self.token)
>raise
> 
> 
> 
> First of all, there is 1 test failure. 
> But if I manually test the failed one by making exact same files being 
> tested, error page seems to be Okay.
> 
> Second, is this ways of code change acceptable?
> 
> ==
> FAIL: test_compile_tag_error_27956 (template_tests.tests.TemplateTests)
> Errors in a child of {% extends %} are displayed correctly.
> --
> Traceback (most recent call last):
>  File 
> "/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py",
>  line 59, in testPartExecutor
>yield
>  File 
> "/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py",
>  line 628, in run
>testMethod()
>  File "/Users/minhokim/Code/Repo/django/tests/template_tests/tests.py", line 
> 135, in test_compile_tag_error_27956
>self.assertEqual(e.exception.template_debug['during'], '{% badtag %}')
>  File 
> "/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py",
>  line 852, in assertEqual
>assertion_func(first, second, msg=msg)
>  File 
> "/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py",
>  line 845, in _baseAssertEqual
>raise self.failureException(msg)
> AssertionError: 'nt.html" %}\n' != '{% badtag %}'
> 
> --
> 
> Thank you.
> 
> - Min ho Kim
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "Django developers  (Contributions to Django itself)" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to django-developers+unsubscr...@googlegroups.com.
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/django-developers/e4b1c45a-8750-4ae4-8985-dd09421d06a8%40googlegroups.com.

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/60A9C223-4838-4CBA-8C7F-D0BFD8AFA1F7%40gmail.com.


NULLs taking up space in foreign key indexes

2019-09-12 Thread Ole Laursen
Hi!

I recently noticed that the default indexes that Django generates for 
foreign keys also index NULL values, at least for PostgreSQL. Is this on 
purpose?

>From my digging, it looks like PostgreSQL used to exclude NULL values from 
the index, but not since some years. It's relatively easy to skip them by 
appending "where ... is not null" to the index creation, like this:

  create index for_bar_id_index on foo (bar_id) where bar_id is not null;

This comes up because nullable foreign keys are for optional relations, and 
I sometimes have big tables with a bunch of mostly NULL foreign keys (NULLs 
are cheap in PostgreSQL), and the indexes can then dwarf the table.

I have always assumed that the default indexes where for reverse joining 
and deleting purposes only so keeping track of NULLs doesn't actually seem 
necessary.


Ole


-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/d26f0ed4-94f3-48a5-8671-a3dfa3d5440b%40googlegroups.com.


Re: Cross-DB JSONField ready for review

2019-09-12 Thread Ole Laursen
fredag den 2. august 2019 kl. 13.46.46 UTC+2 skrev Sage M.A.:
>
> As a follow-up to this message 
> 
>  and this ticket , I have 
> completed the implementation of a cross-DB JSONField.
>

As someone who has been using the Postgres-specific JSONField extensively 
for dynamic, custom fields for the past couple of years, can I humbly 
suggest that the some more thought goes into the field lookup before the 
current approach is enshrined?

The simple .filter(field__foo="hello world") is actually fine, but it gets 
really weird if you let users define foo. What if they call foo something 
like "contains"? What if "foo" is actually "Foo the bar?". The JSON makes 
sense:

{
  "Foo the bar?": true
}

but the Django filter does not.

The current documentation says you can use __contains, but as far as I can 
tell, __contains, besides being difficult to understand, cannot be used for 
queries like __icontains. And it overwrites a built-in, otherwise useful 
operator.

My current wish list is:

- kill special contains and contained_by (use Q(field__foo="xxx") | 
Q(field__bar="yyy") instead)

- kill has_keys and has_any_keys (use Q(field__has_key="xxx") | 
Q(field__has_key="yyy") instead)

- add possibility of using something more robust than __ for path 
traversal, perhaps just a JSON extract string like 
JSONExtract("owner.other_pets[0].name")

This could perhaps also allow us to use the JSONField content in places 
where you currently can't (e.g. annotate). I'm not sure how exactly to 
combine it with field lookup and operators, but I'm personally okay with 
something like

   .filter(**{ "myfield__" + JSONExtract("owner.other_pets[0].name") + 
"__icontains": "baz" })

That's way better than what we have now.

The neat thing about JSONField is that it can store some of the things that 
are otherwise difficult to handle with traditional schemas. I think with 
some love and a set of more orthogonal primitives, we could query it easily 
from Django, too.


Ole

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/92277b8e-4453-496e-b0aa-e15b79a9d0a2%40googlegroups.com.


Re: PEP 484 type hinting in Django

2019-09-12 Thread Lukas Meier
I'm writing here with the risk of you guys obviously already knowing about 
it

there is a project called monkeytype written by instagram that does 
analysis similar to what  @Maxim said

they actually have specific answers to django related questions here
https://monkeytype.readthedocs.io/en/stable/faq.html



On Monday, 26 August 2019 07:05:19 UTC-5, Maxim Kurnikov wrote:
>
> Hi, I'm the original creator of 
> https://github.com/typeddjango/django-stubs.
>
> I guess it's my duty to start writing a DEP, so here is some preliminary 
> thoughts/questions to gather early feedback: 
>
> Main discussion points: 
> 1. There's two ways to make Django compatible with type hinting and 
> PEP484: 
> - Separate repo with `.pyi` files and mypy plugin (how it's done 
> currently with django-stubs). 
>
> Pros: 
> - no backwards-incompatible changes. Only required change from 
> Django is adding the `__class_getitem__` method to `QuerySet` 
> https://docs.python.org/3/reference/datamodel.html#object.__class_getitem__ 
> to support generic parameters in 3.7+
> - could be released separately
> - no type hints code clutter
> Cons: 
> - (as of now) there's no way to use type hints to typecheck Django 
> codebase itself. I think the official answer to that is to use retype tool 
> https://github.com/ambv/retype (
> https://github.com/python/mypy/issues/5028#issuecomment-495942769), but 
> so far I didn't have any positive experience with that
>
> - Inline type hints in the codebase. 
> 
> Pros/Cons are exact reverse. 
>
> 2. Whether to use Django own machinery to extract info for the 
> models/fields, or not. 
>
> Current implementation of django-stubs utilises `Apps` registry of Django 
> itself to extract some data for models/fields.
>  So, basically it executes codes first, then performs static analysis of 
> it. 
>
> Pros: 
> - lots of models/fields introspection could be extracted from the 
> `_meta` API => lower maintenance burden, more accurate types. In the 
> pre-1.0.0 versions of the django-stubs it was all done statically, and it 
> was very painful to implement. 
>
> Cons: 
> - mypy daemon mode (dmypy) is broken. It could be fixed, but it 
> requires some changes to mypy itself, and as `django.setup()` could not run 
> incrementally, it's not clear whether dmypy would provide any real benefits 
> over plain incremental mode. 
> - there could be some side effects in `django.setup()` invocation
> - cannot typecheck invalid code
> - slower to execute
>
> Carlton, is it possible to move discussion to Github somehow?
>
> On Wednesday, December 12, 2018 at 7:06:16 PM UTC+3, Carlton Gibson wrote:
>>
>> Hi all. 
>>
>>
>> Where are we with this Type Hinting work? 
>>
>>
>> I just closed https://code.djangoproject.com/ticket/30019 as needsinfo 
>> pointing back to this thread. 
>>
>>
>> As far as I can see: 
>>
>> * There's keenness for this. 
>> * There's a number of people who are prepared to put in the effort. 
>> * But we just need a strategy. 
>>
>> It looks then like a coordination problem. Do we just need to get said 
>> people in a (virtual) room...? 
>>
>> Maybe one of you — anyone — opening a DEP to begin the conversation would 
>> be enough.. 
>> (That's just an idea.) 
>>
>> Looking here: 
>> https://github.com/django/deps/blob/master/final/0001-dep-process.rst#dep-submission-workflow
>> I'd guess "Forming the Team" is relevant. 
>>
>> Happy to help if I can. 
>> (I recall Frank mentioned it no too long ago...) 
>>
>> Kind Regards,
>>
>> Carlton
>>
>>

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/2273ec96-8ad8-4ae3-9802-1e0663bdd9b6%40googlegroups.com.


cache_control() "max_age" overrides cache_page() "timeout"

2019-09-12 Thread Danny Flack
It is currently impossible to specify a different client-side cache value 
than a server-side cache value when using both decorators *cache_control* 
and *cache_page* on a view.

For example:

@cache_control(max_age=60)
> @cache_page(timeout=3600)
> def my_view():
>   return HttpResponse()


This would correctly return the header *Cache-Control: max-age=60 *but 
would cache the result, server-side, for 60 seconds instead of the 
requested 3600 seconds. 

The reason this happens is because *UpdateCacheMiddleware.process_response() 
*chooses the "*max-age" *header over any explicitly set *self.cache_timeout* 
value 
(i.e set from *CacheMiddleware* which *cache_page* uses).

My immediate thought would be to have UpdateCacheMiddleware not initially 
set *self.cache_timeout* to *settings.CACHE_MIDDLEWARE_SECONDS* and then, 
in *process_response()*, only use "*max-age*" if *self.cache_timeout* was 
indeed 
*None*.

I created the ticket https://code.djangoproject.com/ticket/30765.

Any ideas if this would work? It seems too simple.

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/009cce02-0d55-4a7c-b498-0490f04ba6a9%40googlegroups.com.


Re: Cross-DB JSONField ready for review

2019-09-12 Thread schinckel
Hi Ole,

I'm interested in what you are trying to do with JSONExtract. I have a 
subclass of Func called JSONBExtractPathText that I use with great success 
to extract parts of a JSONB object.

Also, as of Django 3.0, you can filter directly on an expression (that has 
an output_field of BooleanField).

Thus, you could write your first example as:

MyModel.objects.filter(JSONBExtractPathText('field', Value('Foo the bar?'), 
output_field=models.BooleanField())

I think you could possibly do the other stuff using either an 
ExpressionWrapper, or at worst a Case(When()).

(I hang out on #django on IRC if you want to discuss this in a more 
interactive manner).

Matt.

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/45fb5bf5-c1d8-44eb-973b-56914264fcc3%40googlegroups.com.