@@ -44,7 +44,7 @@ class AccountAnalyticLine(models.Model):
|
||||
# Compute based on pricetype
|
||||
amount_unit = self.product_id.price_compute('standard_price', uom=unit)[self.product_id.id]
|
||||
amount = amount_unit * self.unit_amount or 0.0
|
||||
result = round(amount, self.currency_id.decimal_places) * -1
|
||||
result = self.currency_id.round(amount) * -1
|
||||
self.amount = result
|
||||
self.general_account_id = account
|
||||
self.product_uom_id = unit
|
||||
|
||||
@@ -1551,14 +1551,13 @@ class AccountPaymentTerm(models.Model):
|
||||
currency = self.env['res.currency'].browse(self.env.context['currency_id'])
|
||||
else:
|
||||
currency = self.env.user.company_id.currency_id
|
||||
prec = currency.decimal_places
|
||||
for line in self.line_ids:
|
||||
if line.value == 'fixed':
|
||||
amt = round(line.value_amount, prec)
|
||||
amt = currency.round(line.value_amount)
|
||||
elif line.value == 'percent':
|
||||
amt = round(value * (line.value_amount / 100.0), prec)
|
||||
amt = currency.round(value * (line.value_amount / 100.0))
|
||||
elif line.value == 'balance':
|
||||
amt = round(amount, prec)
|
||||
amt = currency.round(amount)
|
||||
if amt:
|
||||
next_date = fields.Date.from_string(date_ref)
|
||||
if line.option == 'day_after_invoice_date':
|
||||
@@ -1573,7 +1572,7 @@ class AccountPaymentTerm(models.Model):
|
||||
result.append((fields.Date.to_string(next_date), amt))
|
||||
amount -= amt
|
||||
amount = sum(amt for _, amt in result)
|
||||
dist = round(value - amount, prec)
|
||||
dist = currency.round(value - amount)
|
||||
if dist:
|
||||
last_date = result and result[-1][0] or fields.Date.today()
|
||||
result.append((last_date, dist))
|
||||
|
||||
@@ -192,10 +192,10 @@ class AccountMove(models.Model):
|
||||
@api.multi
|
||||
def _check_lock_date(self):
|
||||
for move in self:
|
||||
lock_date = max(move.company_id.period_lock_date, move.company_id.fiscalyear_lock_date)
|
||||
lock_date = max(move.company_id.period_lock_date or '0000-00-00', move.company_id.fiscalyear_lock_date or '0000-00-00')
|
||||
if self.user_has_groups('account.group_account_manager'):
|
||||
lock_date = move.company_id.fiscalyear_lock_date
|
||||
if move.date <= lock_date:
|
||||
if move.date <= (lock_date or '0000-00-00'):
|
||||
if self.user_has_groups('account.group_account_manager'):
|
||||
message = _("You cannot add/modify entries prior to and inclusive of the lock date %s") % (lock_date)
|
||||
else:
|
||||
@@ -1029,7 +1029,7 @@ class AccountMoveLine(models.Model):
|
||||
partial_rec_set = self.env['account.partial.reconcile']
|
||||
aml_id = False
|
||||
partial_rec_id = False
|
||||
maxdate = None
|
||||
maxdate = '0000-00-00'
|
||||
for aml in self:
|
||||
total_debit += aml.debit
|
||||
total_credit += aml.credit
|
||||
@@ -1491,7 +1491,7 @@ class AccountPartialReconcile(models.Model):
|
||||
# The move date should be the maximum date between payment and invoice
|
||||
# (in case of payment in advance). However, we should make sure the
|
||||
# move date is not recorded after the end of year closing.
|
||||
if move_date > self.company_id.fiscalyear_lock_date:
|
||||
if (move_date or '0000-00-00' > self.company_id.fiscalyear_lock_date or '0000-00-00'):
|
||||
res['date'] = move_date
|
||||
return res
|
||||
|
||||
@@ -1689,7 +1689,7 @@ class AccountPartialReconcile(models.Model):
|
||||
#make sure that all partial reconciliations share the same secondary currency otherwise it's not
|
||||
#possible to compute the exchange difference entry and it has to be done manually.
|
||||
currency = list(partial_rec_set)[0].currency_id
|
||||
maxdate = None
|
||||
maxdate = '0000-00-00'
|
||||
aml_to_balance = None
|
||||
for partial_rec in partial_rec_set:
|
||||
if partial_rec.currency_id != currency:
|
||||
|
||||
@@ -109,7 +109,7 @@ class ReportPartnerLedger(models.AbstractModel):
|
||||
self.env.cr.execute(query, tuple(params))
|
||||
partner_ids = [res['partner_id'] for res in self.env.cr.dictfetchall()]
|
||||
partners = obj_partner.browse(partner_ids)
|
||||
partners = sorted(partners, key=lambda x: (x.ref, x.name))
|
||||
partners = sorted(partners, key=lambda x: (x.ref or '', x.name or ''))
|
||||
|
||||
return {
|
||||
'doc_ids': partner_ids,
|
||||
|
||||
@@ -298,9 +298,9 @@ class Lead(models.Model):
|
||||
# stage change with new stage: update probability and date_closed
|
||||
if vals.get('stage_id') and 'probability' not in vals:
|
||||
vals.update(self._onchange_stage_id_values(vals.get('stage_id')))
|
||||
if vals.get('probability') >= 100 or not vals.get('active', True):
|
||||
if vals.get('probability', 0) >= 100 or not vals.get('active', True):
|
||||
vals['date_closed'] = fields.Datetime.now()
|
||||
elif 'probability' in vals and vals['probability'] < 100:
|
||||
elif 'probability' in vals:
|
||||
vals['date_closed'] = False
|
||||
return super(Lead, self).write(vals)
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import logging
|
||||
import psycopg2
|
||||
import werkzeug
|
||||
|
||||
from operator import itemgetter
|
||||
from werkzeug import url_encode
|
||||
|
||||
from odoo import api, http, registry, SUPERUSER_ID, _
|
||||
@@ -154,10 +153,10 @@ class MailController(http.Controller):
|
||||
'default': subtype.default,
|
||||
'internal': subtype.internal,
|
||||
'followed': subtype.id in followers.mapped('subtype_ids').ids,
|
||||
'parent_model': subtype.parent_id and subtype.parent_id.res_model or False,
|
||||
'parent_model': subtype.parent_id.res_model,
|
||||
'id': subtype.id
|
||||
} for subtype in subtypes]
|
||||
subtypes_list = sorted(subtypes_list, key=itemgetter('parent_model', 'res_model', 'internal', 'sequence'))
|
||||
subtypes_list = sorted(subtypes_list, key=lambda it: (it['parent_model'] or '', it['res_model'] or '', it['internal'], it['sequence']))
|
||||
return subtypes_list
|
||||
|
||||
@http.route('/mail/view', type='http', auth='none')
|
||||
|
||||
@@ -60,9 +60,9 @@ class AccountInvoiceLine(models.Model):
|
||||
invoice_line.product_id.membership and \
|
||||
not MemberLine.search([('account_invoice_line', '=', invoice_line.id)]):
|
||||
# Product line is a membership product
|
||||
date_from = invoice_line.product_id.membership_date_from
|
||||
date_to = invoice_line.product_id.membership_date_to
|
||||
if invoice_line.invoice_id.date_invoice > date_from and invoice_line.invoice_id.date_invoice < date_to:
|
||||
date_from = invoice_line.product_id.membership_date_from or '0000-00-00'
|
||||
date_to = invoice_line.product_id.membership_date_to or '0000-00-00'
|
||||
if date_from < (invoice_line.invoice_id.date_invoice or '0000-00-00') < date_to:
|
||||
date_from = invoice_line.invoice_id.date_invoice
|
||||
MemberLine.create({
|
||||
'partner': invoice_line.invoice_id.partner_id.id,
|
||||
|
||||
@@ -146,7 +146,6 @@ class AccountInvoice(models.Model):
|
||||
# reference_account_id is the stock input account
|
||||
reference_account_id = i_line.product_id.product_tmpl_id.get_product_accounts(fiscal_pos=fpos)['stock_input'].id
|
||||
diff_res = []
|
||||
account_prec = inv.company_id.currency_id.decimal_places
|
||||
# calculate and write down the possible price difference between invoice price and product price
|
||||
for line in res:
|
||||
if line.get('invl_id', 0) == i_line.id and reference_account_id == line['account_id']:
|
||||
@@ -180,13 +179,13 @@ class AccountInvoice(models.Model):
|
||||
if child.type_tax_use != 'none':
|
||||
tax_ids.append((4, child.id, None))
|
||||
price_before = line.get('price', 0.0)
|
||||
line.update({'price': round(valuation_price_unit * line['quantity'], account_prec)})
|
||||
line.update({'price': company_currency.round(valuation_price_unit * line['quantity'])})
|
||||
diff_res.append({
|
||||
'type': 'src',
|
||||
'name': i_line.name[:64],
|
||||
'price_unit': round(price_unit - valuation_price_unit, account_prec),
|
||||
'price_unit': company_currency.round(price_unit - valuation_price_unit),
|
||||
'quantity': line['quantity'],
|
||||
'price': round(price_before - line.get('price', 0.0), account_prec),
|
||||
'price': company_currency.round(price_before - line.get('price', 0.0)),
|
||||
'account_id': acc,
|
||||
'product_id': line['product_id'],
|
||||
'uom_id': line['uom_id'],
|
||||
|
||||
@@ -420,8 +420,12 @@ class ResourceCalendar(models.Model):
|
||||
dtstart=start_dt,
|
||||
until=end_dt,
|
||||
byweekday=self._get_weekdays()):
|
||||
start_time = day.date() == start_dt.date() and start_dt.time() or datetime.time.min
|
||||
end_time = day.date() == end_dt.date() and end_dt.time() or datetime.time.max
|
||||
start_time = datetime.time.min
|
||||
if day.date() == start_dt.date():
|
||||
start_time = start_dt.time()
|
||||
end_time = datetime.time.max
|
||||
if day.date() == end_dt.date() and end_dt.time() != datetime.time():
|
||||
end_time = end_dt.time()
|
||||
|
||||
intervals = self._get_day_work_intervals(
|
||||
day.date(),
|
||||
@@ -445,8 +449,12 @@ class ResourceCalendar(models.Model):
|
||||
dtstart=start_dt,
|
||||
until=end_dt,
|
||||
byweekday=self._get_weekdays()):
|
||||
start_time = day.date() == start_dt.date() and start_dt.time() or datetime.time.min
|
||||
end_time = day.date() == end_dt.date() and end_dt.time() or datetime.time.max
|
||||
start_time = datetime.time.min
|
||||
if day.date() == start_dt.date():
|
||||
start_time = start_dt.time()
|
||||
end_time = datetime.time.max
|
||||
if day.date() == end_dt.date() and end_dt.time() != datetime.time():
|
||||
end_time = end_dt.time()
|
||||
|
||||
intervals = self._get_day_leave_intervals(
|
||||
day.date(),
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
from datetime import timedelta
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
from odoo.tools import pycompat
|
||||
|
||||
class ResourceMixin(models.AbstractModel):
|
||||
_name = "resource.mixin"
|
||||
@@ -47,7 +47,7 @@ class ResourceMixin(models.AbstractModel):
|
||||
theoric_hours = self.get_day_work_hours_count(day_intervals[0][0].date(), calendar=calendar)
|
||||
work_time = sum((interval[1] - interval[0] for interval in day_intervals), timedelta())
|
||||
total_work_time += work_time
|
||||
days_count += round((work_time.total_seconds() / 3600 / theoric_hours) * 4) / 4
|
||||
days_count += pycompat.round((work_time.total_seconds() / 3600 / theoric_hours) * 4) / 4
|
||||
return {
|
||||
'days': days_count,
|
||||
'hours': total_work_time.total_seconds() / 3600,
|
||||
@@ -70,7 +70,7 @@ class ResourceMixin(models.AbstractModel):
|
||||
for day_intervals in calendar._iter_leave_intervals(from_datetime, to_datetime, self.resource_id.id):
|
||||
theoric_hours = self.get_day_work_hours_count(day_intervals[0][0].date(), calendar=calendar)
|
||||
leave_time = sum((interval[1] - interval[0] for interval in day_intervals), timedelta())
|
||||
days_count += round((leave_time.total_seconds()/3600 / theoric_hours) * 4) / 4
|
||||
days_count += pycompat.round((leave_time.total_seconds() / 3600 / theoric_hours) * 4) / 4
|
||||
return days_count
|
||||
|
||||
def iter_leaves(self, from_datetime, to_datetime, calendar=None):
|
||||
|
||||
@@ -94,7 +94,7 @@ class AccountInvoiceLine(models.Model):
|
||||
price = company_currency.with_context(date=self.invoice_id.date_invoice).compute(price_unit * self.quantity, self.invoice_id.currency_id)
|
||||
else:
|
||||
price = price_unit * self.quantity
|
||||
return round(price, self.invoice_id.currency_id.decimal_places)
|
||||
return self.invoice_id.currency_id.round(price)
|
||||
|
||||
def get_invoice_line_account(self, type, product, fpos, company):
|
||||
if company.anglo_saxon_accounting and type in ('in_invoice', 'in_refund') and product and product.type == 'product':
|
||||
|
||||
@@ -226,9 +226,7 @@ class TestSurvey(TransactionCase):
|
||||
lines = [line.value_suggested.id for line in question.user_input_line_ids]
|
||||
answers = [{'text': label.value, 'count': lines.count(label.id), 'answer_id': label.id} for label in question.labels_ids]
|
||||
prp_result = self.env['survey.survey'].prepare_result(question)['answers']
|
||||
answers.sort()
|
||||
prp_result.sort()
|
||||
self.assertEqual(prp_result, answers, msg="Statistics of simple, multiple choice questions are different from expectation")
|
||||
self.assertItemsEqual(prp_result, answers, msg="Statistics of simple, multiple choice questions are different from expectation")
|
||||
|
||||
def test_11_survey_result_matrix(self):
|
||||
question = self.env['survey.question'].sudo(self.survey_manager).create({
|
||||
|
||||
@@ -510,7 +510,7 @@ def _realize_padding(it):
|
||||
requests for at least n newlines of padding. Runs thereof can be collapsed
|
||||
into the largest requests and converted to newlines.
|
||||
"""
|
||||
padding = None
|
||||
padding = 0
|
||||
for item in it:
|
||||
if isinstance(item, int):
|
||||
padding = max(padding, item)
|
||||
@@ -518,7 +518,7 @@ def _realize_padding(it):
|
||||
|
||||
if padding:
|
||||
yield '\n' * padding
|
||||
padding = None
|
||||
padding = 0
|
||||
|
||||
yield item
|
||||
# leftover padding irrelevant as the output will be stripped
|
||||
|
||||
@@ -209,7 +209,7 @@ class BlogPost(models.Model):
|
||||
def write(self, vals):
|
||||
self.ensure_one()
|
||||
if 'website_published' in vals and 'published_date' not in vals:
|
||||
if self.published_date <= fields.Datetime.now():
|
||||
if (self.published_date or '') <= fields.Datetime.now():
|
||||
vals['published_date'] = vals['website_published'] and fields.Datetime.now()
|
||||
result = super(BlogPost, self).write(vals)
|
||||
self._check_for_publication(vals)
|
||||
|
||||
@@ -73,7 +73,7 @@ class WebsiteCustomer(http.Controller):
|
||||
'country_id_count': 0,
|
||||
'country_id': (country.id, country.name)
|
||||
})
|
||||
countries.sort(key=lambda d: d['country_id'] and d['country_id'][1])
|
||||
countries.sort(key=lambda d: (d['country_id'] or (0, ""))[1])
|
||||
|
||||
countries.insert(0, {
|
||||
'country_id_count': country_count,
|
||||
|
||||
@@ -68,7 +68,7 @@ class WebsiteEventTrackController(http.Controller):
|
||||
@http.route(['''/event/<model("event.event", "[('website_track','=',1)]"):event>/agenda'''], type='http', auth="public", website=True)
|
||||
def event_agenda(self, event, tag=None, **post):
|
||||
days_tracks = collections.defaultdict(lambda: [])
|
||||
for track in event.track_ids.sorted(lambda track: (track.date, bool(track.location_id))):
|
||||
for track in event.track_ids.sorted(lambda track: (track.date or '', bool(track.location_id))):
|
||||
if not track.date:
|
||||
continue
|
||||
days_tracks[track.date[:10]].append(track)
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<h2 class="text-center mb32">Our Sponsors</h2>
|
||||
</section>
|
||||
<div class="row">
|
||||
<div t-attf-class="col-md-#{(len(event.sponsor_ids) > 6) and 2 or (12/ len(event.sponsor_ids))} text-center mb16 oe_sponsor" t-foreach="event.sponsor_ids" t-as="sponsor">
|
||||
<div t-attf-class="col-md-#{(len(event.sponsor_ids) > 6) and 2 or (12 // len(event.sponsor_ids))} text-center mb16 oe_sponsor" t-foreach="event.sponsor_ids" t-as="sponsor">
|
||||
<t t-if="sponsor.url">
|
||||
<a t-att-href="sponsor.url" style="position: relative; display: inline-block;">
|
||||
<span t-field="sponsor.image_medium"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import base64
|
||||
import json
|
||||
import lxml
|
||||
@@ -547,9 +546,9 @@ class WebsiteForum(http.Controller):
|
||||
pager = request.website.pager(url="/forum/%s/users" % slug(forum), total=tag_count, page=page, step=step, scope=30)
|
||||
user_obj = User.sudo().search([('karma', '>', 1), ('website_published', '=', True)], limit=step, offset=pager['offset'], order='karma DESC')
|
||||
# put the users in block of 3 to display them as a table
|
||||
users = [[] for i in range(len(user_obj) / 3 + 1)]
|
||||
users = [[] for i in range(len(user_obj) // 3 + 1)]
|
||||
for index, user in enumerate(user_obj):
|
||||
users[index / 3].append(user)
|
||||
users[index // 3].append(user)
|
||||
searches['users'] = 'True'
|
||||
|
||||
values = self._prepare_forum_values(forum=forum, searches=searches)
|
||||
|
||||
@@ -53,28 +53,28 @@ class TableCompute(object):
|
||||
x = y = 1
|
||||
|
||||
pos = minpos
|
||||
while not self._check_place(pos % PPR, pos / PPR, x, y):
|
||||
while not self._check_place(pos % PPR, pos // PPR, x, y):
|
||||
pos += 1
|
||||
# if 21st products (index 20) and the last line is full (PPR products in it), break
|
||||
# (pos + 1.0) / PPR is the line where the product would be inserted
|
||||
# maxy is the number of existing lines
|
||||
# + 1.0 is because pos begins at 0, thus pos 20 is actually the 21st block
|
||||
# and to force python to not round the division operation
|
||||
if index >= ppg and ((pos + 1.0) / PPR) > maxy:
|
||||
if index >= ppg and ((pos + 1.0) // PPR) > maxy:
|
||||
break
|
||||
|
||||
if x == 1 and y == 1: # simple heuristic for CPU optimization
|
||||
minpos = pos / PPR
|
||||
minpos = pos // PPR
|
||||
|
||||
for y2 in range(y):
|
||||
for x2 in range(x):
|
||||
self.table[(pos / PPR) + y2][(pos % PPR) + x2] = False
|
||||
self.table[pos / PPR][pos % PPR] = {
|
||||
self.table[(pos // PPR) + y2][(pos % PPR) + x2] = False
|
||||
self.table[pos // PPR][pos % PPR] = {
|
||||
'product': p, 'x': x, 'y': y,
|
||||
'class': " ".join(x.html_class for x in p.website_style_ids if x.html_class)
|
||||
}
|
||||
if index <= ppg:
|
||||
maxy = max(maxy, y + (pos / PPR))
|
||||
maxy = max(maxy, y + (pos // PPR))
|
||||
index += 1
|
||||
|
||||
# Format table according to HTML needs
|
||||
@@ -909,7 +909,7 @@ class WebsiteSale(http.Controller):
|
||||
@http.route(['/shop/get_unit_price'], type='json', auth="public", methods=['POST'], website=True)
|
||||
def get_unit_price(self, product_ids, add_qty, **kw):
|
||||
products = request.env['product.product'].with_context({'quantity': add_qty}).browse(product_ids)
|
||||
return {product.id: product.website_price / add_qty for product in products}
|
||||
return {product.id: product.website_price // add_qty for product in products}
|
||||
|
||||
# ------------------------------------------------------
|
||||
# Edit
|
||||
|
||||
@@ -323,7 +323,7 @@ class IrHttp(models.AbstractModel):
|
||||
headers += [('Content-Type', mimetype), ('X-Content-Type-Options', 'nosniff')]
|
||||
|
||||
# cache
|
||||
etag = hasattr(request, 'httprequest') and request.httprequest.headers.get('If-None-Match')
|
||||
etag = bool(request) and request.httprequest.headers.get('If-None-Match')
|
||||
retag = '"%s"' % hashlib.md5(last_update).hexdigest()
|
||||
status = status or (304 if etag == retag else 200)
|
||||
headers.append(('ETag', retag))
|
||||
|
||||
@@ -482,7 +482,7 @@ class TestAPI(common.TransactionCase):
|
||||
ps = self.env['res.partner'].search([])
|
||||
|
||||
# sort by model order
|
||||
qs = ps[:len(ps) / 2] + ps[len(ps) / 2:]
|
||||
qs = ps[:len(ps) // 2] + ps[len(ps) // 2:]
|
||||
self.assertEqual(qs.sorted().ids, ps.ids)
|
||||
|
||||
# sort by name, with a function or a field name
|
||||
|
||||
@@ -46,7 +46,7 @@ class TestEmptyDate(common.TransactionCase):
|
||||
|
||||
gb = self.Model.read_group([], ['date', 'value'], ['date'], lazy=False)
|
||||
|
||||
self.assertSequenceEqual(sorted(gb, key=lambda r: r['date']), [{
|
||||
self.assertSequenceEqual(sorted(gb, key=lambda r: r['date'] or ''), [{
|
||||
'__count': 2,
|
||||
'__domain': [('date', '=', False)],
|
||||
'date': False,
|
||||
|
||||
@@ -149,8 +149,8 @@ def initialize_sys_path():
|
||||
ad_paths.append(ad)
|
||||
|
||||
if not hooked:
|
||||
sys.meta_path.append(AddonsHook())
|
||||
sys.meta_path.append(OdooHook())
|
||||
sys.meta_path.insert(0, OdooHook())
|
||||
sys.meta_path.insert(0, AddonsHook())
|
||||
hooked = True
|
||||
|
||||
def get_module_path(module, downloaded=False, display_warning=True):
|
||||
|
||||
@@ -58,7 +58,7 @@ def float_round(value, precision_digits=None, precision_rounding=None, rounding_
|
||||
epsilon = 2**(epsilon_magnitude-53)
|
||||
if rounding_method == 'HALF-UP':
|
||||
normalized_value += math.copysign(epsilon, normalized_value)
|
||||
rounded_value = round(normalized_value) # round to integer
|
||||
rounded_value = pycompat.round(normalized_value) # round to integer
|
||||
|
||||
# TIE-BREAKING: UP (for ceiling operations)
|
||||
# When rounding the value up, we instead subtract the epsilon value
|
||||
|
||||
+5
-6
@@ -1,6 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
try:
|
||||
import cStringIO as StringIO
|
||||
except ImportError:
|
||||
@@ -102,7 +101,7 @@ def image_resize_and_sharpen(image, size, preserve_aspect_ratio=False, factor=2.
|
||||
resized_image = sharpener.enhance(factor)
|
||||
# create a transparent image for background and paste the image on it
|
||||
image = Image.new('RGBA', size, (255, 255, 255, 0))
|
||||
image.paste(resized_image, ((size[0] - resized_image.size[0]) / 2, (size[1] - resized_image.size[1]) / 2))
|
||||
image.paste(resized_image, ((size[0] - resized_image.size[0]) // 2, (size[1] - resized_image.size[1]) // 2))
|
||||
if image.mode != origin_mode:
|
||||
image = image.convert(origin_mode)
|
||||
return image
|
||||
@@ -181,17 +180,17 @@ def crop_image(data, type='top', ratio=False, thumbnail_ratio=None, image_format
|
||||
|
||||
if ratio:
|
||||
w_ratio, h_ratio = ratio
|
||||
new_h = (w * h_ratio) / w_ratio
|
||||
new_h = (w * h_ratio) // w_ratio
|
||||
new_w = w
|
||||
if new_h > h:
|
||||
new_h = h
|
||||
new_w = (h * w_ratio) / h_ratio
|
||||
new_w = (h * w_ratio) // h_ratio
|
||||
|
||||
if type == "top":
|
||||
cropped_image = image_stream.crop((0, 0, new_w, new_h))
|
||||
cropped_image.save(output_stream, format=image_format)
|
||||
elif type == "center":
|
||||
cropped_image = image_stream.crop(((w - new_w) / 2, (h - new_h) / 2, (w + new_w) / 2, (h + new_h) / 2))
|
||||
cropped_image = image_stream.crop(((w - new_w) // 2, (h - new_h) // 2, (w + new_w) // 2, (h + new_h) // 2))
|
||||
cropped_image.save(output_stream, format=image_format)
|
||||
elif type == "bottom":
|
||||
cropped_image = image_stream.crop((0, h - new_h, new_w, h))
|
||||
@@ -201,7 +200,7 @@ def crop_image(data, type='top', ratio=False, thumbnail_ratio=None, image_format
|
||||
# TDE FIXME: should not have a ratio, makes no sense -> should have maximum width (std: 64; 256 px)
|
||||
if thumbnail_ratio:
|
||||
thumb_image = Image.open(StringIO.StringIO(output_stream.getvalue()))
|
||||
thumb_image.thumbnail((new_w / thumbnail_ratio, new_h / thumbnail_ratio), Image.ANTIALIAS)
|
||||
thumb_image.thumbnail((new_w // thumbnail_ratio, new_h // thumbnail_ratio), Image.ANTIALIAS)
|
||||
thumb_image.save(output_stream, image_format)
|
||||
return output_stream.getvalue().encode('base64')
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ PY2 = sys.version_info[0] == 2
|
||||
if PY2:
|
||||
# pylint: disable=long-builtin,dict-iter-method
|
||||
integer_types = (int, long)
|
||||
round = round
|
||||
|
||||
keys = lambda d: iter(d.iterkeys())
|
||||
values = lambda d: iter(d.itervalues())
|
||||
@@ -23,8 +24,24 @@ if PY2:
|
||||
|
||||
exec ('def reraise(tp, value, tb=None):\n raise tp, value, tb')
|
||||
else:
|
||||
import builtins, math
|
||||
# pylint: disable=bad-functions
|
||||
integer_types = (int,)
|
||||
def round(f):
|
||||
# P3's builtin round differs from P2 in the following manner:
|
||||
# * it rounds half to even rather than up (away from 0)
|
||||
# * round(-0.) loses the sign (it returns -0 rather than 0)
|
||||
# * round(x) returns an int rather than a float
|
||||
#
|
||||
# this compatibility shim implements Python 2's round in terms of
|
||||
# Python 3's so that important rounding error under P3 can be
|
||||
# trivially fixed, assuming the P2 behaviour to be debugged and
|
||||
# correct.
|
||||
roundf = builtins.round(f)
|
||||
if builtins.round(f + 1) - roundf != 1:
|
||||
return f + math.copysign(0.5, f)
|
||||
# copysign ensures round(-0.) -> -0 *and* result is a float
|
||||
return math.copysign(roundf, f)
|
||||
|
||||
keys = lambda d: iter(d.keys())
|
||||
values = lambda d: iter(d.values())
|
||||
|
||||
Reference in New Issue
Block a user