python script to download multiple reports from odoo

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
2
down vote

favorite












We have an odoo server running (http://www.odoo.com/documentation/9.0/api_integration.html), which allows us to programmatically query and download invoice reports as PDF's



We're running this on python 2.7.3. I'm looking for tips on DRYness, future maintainability and help on blind spots in the code where something might go wrong, and the ability to output helpful data to the person running the script.



#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import xmlrpclib
import os

class UniReportPrinter():
def __init__(self, mode):

self.mode = mode

from apiv9connector import s51OpenERPConnector_apiv9

# during dev only get vals from prod that are needed
self.odooconnection1 = s51OpenERPConnector_apiv9()
self.odooconnection1.login()
print "logged in"

odooconnection1 = ''
self.partners =

@property
def partners(self):
return self.partners

@partners.setter
def partners(self, value):
self.partners = value

def get_reports(self):
nextmonth = self.__get_next_month()

print('searching for invoices, filtered by:')
print('state: open')
print('date: ' + nextmonth.strftime("%Y-%m-%d"))
print('reference: like SB%')

invoice_ids = self.odooconnection1.models.execute_kw(
self.odooconnection1.db, self.odooconnection1.uid, self.odooconnection1.password, 'account.invoice', 'search',
[[('state', '=', 'open'), ('date_invoice', '=', nextmonth.strftime("%Y-%m-%d")),('reference', 'like', 'SB%')]])

self.__get_partner_details(invoice_ids)
self.__download_report(invoice_ids)

def __get_next_month(self):
from datetime import date
from dateutil.relativedelta import *
today = date.today()
nextmonth = today+relativedelta(months=+1)
nextmonth = nextmonth+relativedelta(day=1)

return nextmonth

def __get_partner_details(self, invoice_ids):
partners = self.odooconnection1.models.execute_kw(
self.odooconnection1.db, self.odooconnection1.uid, self.odooconnection1.password, 'account.invoice', 'search_read',
[[['id', 'in', invoice_ids]]],
'fields': ['reference', 'id', 'partner_id'])

self.partners = partners

def __download_report(self, invoice_ids):
for invoice_id in invoice_ids:
report = xmlrpclib.ServerProxy('/xmlrpc/2/report'.format('https://odoo.example.com'))
result = report.render_report(
self.odooconnection1.db, self.odooconnection1.uid, self.odooconnection1.password, 'account.report_invoice', [invoice_id])
filename = self.__get_file_name(invoice_id)
sys.stdout.write("r[%s]" % filename )
sys.stdout.flush()
self.__save_report(result, filename)

def __get_file_name(self, invoice_id):
partner = ([p for p in self.partners if p["id"] == invoice_id])[0]
return str(partner['partner_id'][0])+'-'+partner['reference'] + '.pdf'

def __save_report(self, report_data, filename):
nextmonth = self.__get_next_month()
year = nextmonth.year
month = nextmonth.month
path = str(year) + '/' + str(month) + '/'

try:
os.makedirs(path)
except OSError:
if not os.path.isdir(path):
raise

fileNamePath = os.path.join(path, filename)
my_file = open(fileNamePath, 'w')
my_file.write(report_data['result'].decode('base64'))
my_file.close()




###############main#############

if __name__ == "__main__":

unireportprinter = UniReportPrinter(mode='live')
unireportprinter.get_reports()






share|improve this question

























    up vote
    2
    down vote

    favorite












    We have an odoo server running (http://www.odoo.com/documentation/9.0/api_integration.html), which allows us to programmatically query and download invoice reports as PDF's



    We're running this on python 2.7.3. I'm looking for tips on DRYness, future maintainability and help on blind spots in the code where something might go wrong, and the ability to output helpful data to the person running the script.



    #!/usr/bin/python
    # -*- coding: utf-8 -*-

    import sys
    import xmlrpclib
    import os

    class UniReportPrinter():
    def __init__(self, mode):

    self.mode = mode

    from apiv9connector import s51OpenERPConnector_apiv9

    # during dev only get vals from prod that are needed
    self.odooconnection1 = s51OpenERPConnector_apiv9()
    self.odooconnection1.login()
    print "logged in"

    odooconnection1 = ''
    self.partners =

    @property
    def partners(self):
    return self.partners

    @partners.setter
    def partners(self, value):
    self.partners = value

    def get_reports(self):
    nextmonth = self.__get_next_month()

    print('searching for invoices, filtered by:')
    print('state: open')
    print('date: ' + nextmonth.strftime("%Y-%m-%d"))
    print('reference: like SB%')

    invoice_ids = self.odooconnection1.models.execute_kw(
    self.odooconnection1.db, self.odooconnection1.uid, self.odooconnection1.password, 'account.invoice', 'search',
    [[('state', '=', 'open'), ('date_invoice', '=', nextmonth.strftime("%Y-%m-%d")),('reference', 'like', 'SB%')]])

    self.__get_partner_details(invoice_ids)
    self.__download_report(invoice_ids)

    def __get_next_month(self):
    from datetime import date
    from dateutil.relativedelta import *
    today = date.today()
    nextmonth = today+relativedelta(months=+1)
    nextmonth = nextmonth+relativedelta(day=1)

    return nextmonth

    def __get_partner_details(self, invoice_ids):
    partners = self.odooconnection1.models.execute_kw(
    self.odooconnection1.db, self.odooconnection1.uid, self.odooconnection1.password, 'account.invoice', 'search_read',
    [[['id', 'in', invoice_ids]]],
    'fields': ['reference', 'id', 'partner_id'])

    self.partners = partners

    def __download_report(self, invoice_ids):
    for invoice_id in invoice_ids:
    report = xmlrpclib.ServerProxy('/xmlrpc/2/report'.format('https://odoo.example.com'))
    result = report.render_report(
    self.odooconnection1.db, self.odooconnection1.uid, self.odooconnection1.password, 'account.report_invoice', [invoice_id])
    filename = self.__get_file_name(invoice_id)
    sys.stdout.write("r[%s]" % filename )
    sys.stdout.flush()
    self.__save_report(result, filename)

    def __get_file_name(self, invoice_id):
    partner = ([p for p in self.partners if p["id"] == invoice_id])[0]
    return str(partner['partner_id'][0])+'-'+partner['reference'] + '.pdf'

    def __save_report(self, report_data, filename):
    nextmonth = self.__get_next_month()
    year = nextmonth.year
    month = nextmonth.month
    path = str(year) + '/' + str(month) + '/'

    try:
    os.makedirs(path)
    except OSError:
    if not os.path.isdir(path):
    raise

    fileNamePath = os.path.join(path, filename)
    my_file = open(fileNamePath, 'w')
    my_file.write(report_data['result'].decode('base64'))
    my_file.close()




    ###############main#############

    if __name__ == "__main__":

    unireportprinter = UniReportPrinter(mode='live')
    unireportprinter.get_reports()






    share|improve this question





















      up vote
      2
      down vote

      favorite









      up vote
      2
      down vote

      favorite











      We have an odoo server running (http://www.odoo.com/documentation/9.0/api_integration.html), which allows us to programmatically query and download invoice reports as PDF's



      We're running this on python 2.7.3. I'm looking for tips on DRYness, future maintainability and help on blind spots in the code where something might go wrong, and the ability to output helpful data to the person running the script.



      #!/usr/bin/python
      # -*- coding: utf-8 -*-

      import sys
      import xmlrpclib
      import os

      class UniReportPrinter():
      def __init__(self, mode):

      self.mode = mode

      from apiv9connector import s51OpenERPConnector_apiv9

      # during dev only get vals from prod that are needed
      self.odooconnection1 = s51OpenERPConnector_apiv9()
      self.odooconnection1.login()
      print "logged in"

      odooconnection1 = ''
      self.partners =

      @property
      def partners(self):
      return self.partners

      @partners.setter
      def partners(self, value):
      self.partners = value

      def get_reports(self):
      nextmonth = self.__get_next_month()

      print('searching for invoices, filtered by:')
      print('state: open')
      print('date: ' + nextmonth.strftime("%Y-%m-%d"))
      print('reference: like SB%')

      invoice_ids = self.odooconnection1.models.execute_kw(
      self.odooconnection1.db, self.odooconnection1.uid, self.odooconnection1.password, 'account.invoice', 'search',
      [[('state', '=', 'open'), ('date_invoice', '=', nextmonth.strftime("%Y-%m-%d")),('reference', 'like', 'SB%')]])

      self.__get_partner_details(invoice_ids)
      self.__download_report(invoice_ids)

      def __get_next_month(self):
      from datetime import date
      from dateutil.relativedelta import *
      today = date.today()
      nextmonth = today+relativedelta(months=+1)
      nextmonth = nextmonth+relativedelta(day=1)

      return nextmonth

      def __get_partner_details(self, invoice_ids):
      partners = self.odooconnection1.models.execute_kw(
      self.odooconnection1.db, self.odooconnection1.uid, self.odooconnection1.password, 'account.invoice', 'search_read',
      [[['id', 'in', invoice_ids]]],
      'fields': ['reference', 'id', 'partner_id'])

      self.partners = partners

      def __download_report(self, invoice_ids):
      for invoice_id in invoice_ids:
      report = xmlrpclib.ServerProxy('/xmlrpc/2/report'.format('https://odoo.example.com'))
      result = report.render_report(
      self.odooconnection1.db, self.odooconnection1.uid, self.odooconnection1.password, 'account.report_invoice', [invoice_id])
      filename = self.__get_file_name(invoice_id)
      sys.stdout.write("r[%s]" % filename )
      sys.stdout.flush()
      self.__save_report(result, filename)

      def __get_file_name(self, invoice_id):
      partner = ([p for p in self.partners if p["id"] == invoice_id])[0]
      return str(partner['partner_id'][0])+'-'+partner['reference'] + '.pdf'

      def __save_report(self, report_data, filename):
      nextmonth = self.__get_next_month()
      year = nextmonth.year
      month = nextmonth.month
      path = str(year) + '/' + str(month) + '/'

      try:
      os.makedirs(path)
      except OSError:
      if not os.path.isdir(path):
      raise

      fileNamePath = os.path.join(path, filename)
      my_file = open(fileNamePath, 'w')
      my_file.write(report_data['result'].decode('base64'))
      my_file.close()




      ###############main#############

      if __name__ == "__main__":

      unireportprinter = UniReportPrinter(mode='live')
      unireportprinter.get_reports()






      share|improve this question











      We have an odoo server running (http://www.odoo.com/documentation/9.0/api_integration.html), which allows us to programmatically query and download invoice reports as PDF's



      We're running this on python 2.7.3. I'm looking for tips on DRYness, future maintainability and help on blind spots in the code where something might go wrong, and the ability to output helpful data to the person running the script.



      #!/usr/bin/python
      # -*- coding: utf-8 -*-

      import sys
      import xmlrpclib
      import os

      class UniReportPrinter():
      def __init__(self, mode):

      self.mode = mode

      from apiv9connector import s51OpenERPConnector_apiv9

      # during dev only get vals from prod that are needed
      self.odooconnection1 = s51OpenERPConnector_apiv9()
      self.odooconnection1.login()
      print "logged in"

      odooconnection1 = ''
      self.partners =

      @property
      def partners(self):
      return self.partners

      @partners.setter
      def partners(self, value):
      self.partners = value

      def get_reports(self):
      nextmonth = self.__get_next_month()

      print('searching for invoices, filtered by:')
      print('state: open')
      print('date: ' + nextmonth.strftime("%Y-%m-%d"))
      print('reference: like SB%')

      invoice_ids = self.odooconnection1.models.execute_kw(
      self.odooconnection1.db, self.odooconnection1.uid, self.odooconnection1.password, 'account.invoice', 'search',
      [[('state', '=', 'open'), ('date_invoice', '=', nextmonth.strftime("%Y-%m-%d")),('reference', 'like', 'SB%')]])

      self.__get_partner_details(invoice_ids)
      self.__download_report(invoice_ids)

      def __get_next_month(self):
      from datetime import date
      from dateutil.relativedelta import *
      today = date.today()
      nextmonth = today+relativedelta(months=+1)
      nextmonth = nextmonth+relativedelta(day=1)

      return nextmonth

      def __get_partner_details(self, invoice_ids):
      partners = self.odooconnection1.models.execute_kw(
      self.odooconnection1.db, self.odooconnection1.uid, self.odooconnection1.password, 'account.invoice', 'search_read',
      [[['id', 'in', invoice_ids]]],
      'fields': ['reference', 'id', 'partner_id'])

      self.partners = partners

      def __download_report(self, invoice_ids):
      for invoice_id in invoice_ids:
      report = xmlrpclib.ServerProxy('/xmlrpc/2/report'.format('https://odoo.example.com'))
      result = report.render_report(
      self.odooconnection1.db, self.odooconnection1.uid, self.odooconnection1.password, 'account.report_invoice', [invoice_id])
      filename = self.__get_file_name(invoice_id)
      sys.stdout.write("r[%s]" % filename )
      sys.stdout.flush()
      self.__save_report(result, filename)

      def __get_file_name(self, invoice_id):
      partner = ([p for p in self.partners if p["id"] == invoice_id])[0]
      return str(partner['partner_id'][0])+'-'+partner['reference'] + '.pdf'

      def __save_report(self, report_data, filename):
      nextmonth = self.__get_next_month()
      year = nextmonth.year
      month = nextmonth.month
      path = str(year) + '/' + str(month) + '/'

      try:
      os.makedirs(path)
      except OSError:
      if not os.path.isdir(path):
      raise

      fileNamePath = os.path.join(path, filename)
      my_file = open(fileNamePath, 'w')
      my_file.write(report_data['result'].decode('base64'))
      my_file.close()




      ###############main#############

      if __name__ == "__main__":

      unireportprinter = UniReportPrinter(mode='live')
      unireportprinter.get_reports()








      share|improve this question










      share|improve this question




      share|improve this question









      asked Apr 30 at 17:16









      Jarede

      199116




      199116




















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          3
          down vote



          accepted










          Wasted looping



          Let's do a pop quiz on this code:




          partner = ([p for p in self.partners if p["id"] == invoice_id])[0]



          If self.partner contains a million values,
          and the first value has id equal to invoice_id,
          how many values will be visited?




          All of them :(




          That's unfortunate.
          It would be better to spell out a loop:



          for p in self.partners:
          if p["id"] == invoice_id:
          partner = p
          break
          else:
          partner = None


          Sure, this is longer, but it avoids a performance issue waiting to happen,
          and it also handles the case gracefully when there is no matching value.



          A compact alternative is possible using generator expressions, which are very cool:



          partner = next(p for p in self.partners if p["id"] == invoice_id)


          However, in this case, you would still need to handle the case when there is no matching value.
          You would do that by expecting StopIteration to be raised.



          Avoid wildcard imports



          It's good to avoid wildcard imports.
          It makes it easier to understand where symbols come from.
          When you import *,
          it can easily become confusing to tell where some names come from.



          In this code the wildcard import seems pointless:




          from dateutil.relativedelta import *
          today = date.today()
          nextmonth = today+relativedelta(months=+1)
          nextmonth = nextmonth+relativedelta(day=1)

          return nextmonth



          It seems the only thing it imports is the relativedelta,
          so you could write just that instead of using a *.



          Also, I think that the last 3 statements are actually easier to read inlined:



          return today + relativedelta(months=1) + relativedelta(day=1)


          Import at the top of the file



          It's recommended to import packages at the top of the file, not inside functions.



          The above link is part of PEP8, a collection of recommended guidelines that should help you achieve the goals such as maintainability outlined in your question.



          Drop pointless statement



          In the constructor of UniReportPrinter there is this line:




          odooconnection1 = ''



          I'm wondering if you might be confusing odooconnection1 with self.odooconnection1.
          They are different variables.
          The first is a local variable,
          the second is a field of the object being created.



          If you want to unset self.odooconnection1, assign None to it instead of an empty string.



          Getters should return something



          I would expect functions with "get" in the name to return something.
          The __get_partner_details function doesn't return anything.
          I suggest to rename the method. Perhaps replace "get" with "fetch".



          Be consistent



          In some places I see print("string"), in others print "string".



          In some places I see '/foo'.format(bar), in others '%s/foo' % bar, or bar + '/foo'.



          It's good to be consistent.
          And to consistently use the right techniques.
          In each of these examples the right technique is the first.



          Use '...'.format(...)



          Especially when formatting complex strings like here,
          definitely use '...'.format(...) instead:




          return str(partner['partner_id'][0])+'-'+partner['reference'] + '.pdf'






          share|improve this answer





















            Your Answer




            StackExchange.ifUsing("editor", function ()
            return StackExchange.using("mathjaxEditing", function ()
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            );
            );
            , "mathjax-editing");

            StackExchange.ifUsing("editor", function ()
            StackExchange.using("externalEditor", function ()
            StackExchange.using("snippets", function ()
            StackExchange.snippets.init();
            );
            );
            , "code-snippets");

            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "196"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            convertImagesToLinks: false,
            noModals: false,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );








             

            draft saved


            draft discarded


















            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f193281%2fpython-script-to-download-multiple-reports-from-odoo%23new-answer', 'question_page');

            );

            Post as a guest






























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            3
            down vote



            accepted










            Wasted looping



            Let's do a pop quiz on this code:




            partner = ([p for p in self.partners if p["id"] == invoice_id])[0]



            If self.partner contains a million values,
            and the first value has id equal to invoice_id,
            how many values will be visited?




            All of them :(




            That's unfortunate.
            It would be better to spell out a loop:



            for p in self.partners:
            if p["id"] == invoice_id:
            partner = p
            break
            else:
            partner = None


            Sure, this is longer, but it avoids a performance issue waiting to happen,
            and it also handles the case gracefully when there is no matching value.



            A compact alternative is possible using generator expressions, which are very cool:



            partner = next(p for p in self.partners if p["id"] == invoice_id)


            However, in this case, you would still need to handle the case when there is no matching value.
            You would do that by expecting StopIteration to be raised.



            Avoid wildcard imports



            It's good to avoid wildcard imports.
            It makes it easier to understand where symbols come from.
            When you import *,
            it can easily become confusing to tell where some names come from.



            In this code the wildcard import seems pointless:




            from dateutil.relativedelta import *
            today = date.today()
            nextmonth = today+relativedelta(months=+1)
            nextmonth = nextmonth+relativedelta(day=1)

            return nextmonth



            It seems the only thing it imports is the relativedelta,
            so you could write just that instead of using a *.



            Also, I think that the last 3 statements are actually easier to read inlined:



            return today + relativedelta(months=1) + relativedelta(day=1)


            Import at the top of the file



            It's recommended to import packages at the top of the file, not inside functions.



            The above link is part of PEP8, a collection of recommended guidelines that should help you achieve the goals such as maintainability outlined in your question.



            Drop pointless statement



            In the constructor of UniReportPrinter there is this line:




            odooconnection1 = ''



            I'm wondering if you might be confusing odooconnection1 with self.odooconnection1.
            They are different variables.
            The first is a local variable,
            the second is a field of the object being created.



            If you want to unset self.odooconnection1, assign None to it instead of an empty string.



            Getters should return something



            I would expect functions with "get" in the name to return something.
            The __get_partner_details function doesn't return anything.
            I suggest to rename the method. Perhaps replace "get" with "fetch".



            Be consistent



            In some places I see print("string"), in others print "string".



            In some places I see '/foo'.format(bar), in others '%s/foo' % bar, or bar + '/foo'.



            It's good to be consistent.
            And to consistently use the right techniques.
            In each of these examples the right technique is the first.



            Use '...'.format(...)



            Especially when formatting complex strings like here,
            definitely use '...'.format(...) instead:




            return str(partner['partner_id'][0])+'-'+partner['reference'] + '.pdf'






            share|improve this answer

























              up vote
              3
              down vote



              accepted










              Wasted looping



              Let's do a pop quiz on this code:




              partner = ([p for p in self.partners if p["id"] == invoice_id])[0]



              If self.partner contains a million values,
              and the first value has id equal to invoice_id,
              how many values will be visited?




              All of them :(




              That's unfortunate.
              It would be better to spell out a loop:



              for p in self.partners:
              if p["id"] == invoice_id:
              partner = p
              break
              else:
              partner = None


              Sure, this is longer, but it avoids a performance issue waiting to happen,
              and it also handles the case gracefully when there is no matching value.



              A compact alternative is possible using generator expressions, which are very cool:



              partner = next(p for p in self.partners if p["id"] == invoice_id)


              However, in this case, you would still need to handle the case when there is no matching value.
              You would do that by expecting StopIteration to be raised.



              Avoid wildcard imports



              It's good to avoid wildcard imports.
              It makes it easier to understand where symbols come from.
              When you import *,
              it can easily become confusing to tell where some names come from.



              In this code the wildcard import seems pointless:




              from dateutil.relativedelta import *
              today = date.today()
              nextmonth = today+relativedelta(months=+1)
              nextmonth = nextmonth+relativedelta(day=1)

              return nextmonth



              It seems the only thing it imports is the relativedelta,
              so you could write just that instead of using a *.



              Also, I think that the last 3 statements are actually easier to read inlined:



              return today + relativedelta(months=1) + relativedelta(day=1)


              Import at the top of the file



              It's recommended to import packages at the top of the file, not inside functions.



              The above link is part of PEP8, a collection of recommended guidelines that should help you achieve the goals such as maintainability outlined in your question.



              Drop pointless statement



              In the constructor of UniReportPrinter there is this line:




              odooconnection1 = ''



              I'm wondering if you might be confusing odooconnection1 with self.odooconnection1.
              They are different variables.
              The first is a local variable,
              the second is a field of the object being created.



              If you want to unset self.odooconnection1, assign None to it instead of an empty string.



              Getters should return something



              I would expect functions with "get" in the name to return something.
              The __get_partner_details function doesn't return anything.
              I suggest to rename the method. Perhaps replace "get" with "fetch".



              Be consistent



              In some places I see print("string"), in others print "string".



              In some places I see '/foo'.format(bar), in others '%s/foo' % bar, or bar + '/foo'.



              It's good to be consistent.
              And to consistently use the right techniques.
              In each of these examples the right technique is the first.



              Use '...'.format(...)



              Especially when formatting complex strings like here,
              definitely use '...'.format(...) instead:




              return str(partner['partner_id'][0])+'-'+partner['reference'] + '.pdf'






              share|improve this answer























                up vote
                3
                down vote



                accepted







                up vote
                3
                down vote



                accepted






                Wasted looping



                Let's do a pop quiz on this code:




                partner = ([p for p in self.partners if p["id"] == invoice_id])[0]



                If self.partner contains a million values,
                and the first value has id equal to invoice_id,
                how many values will be visited?




                All of them :(




                That's unfortunate.
                It would be better to spell out a loop:



                for p in self.partners:
                if p["id"] == invoice_id:
                partner = p
                break
                else:
                partner = None


                Sure, this is longer, but it avoids a performance issue waiting to happen,
                and it also handles the case gracefully when there is no matching value.



                A compact alternative is possible using generator expressions, which are very cool:



                partner = next(p for p in self.partners if p["id"] == invoice_id)


                However, in this case, you would still need to handle the case when there is no matching value.
                You would do that by expecting StopIteration to be raised.



                Avoid wildcard imports



                It's good to avoid wildcard imports.
                It makes it easier to understand where symbols come from.
                When you import *,
                it can easily become confusing to tell where some names come from.



                In this code the wildcard import seems pointless:




                from dateutil.relativedelta import *
                today = date.today()
                nextmonth = today+relativedelta(months=+1)
                nextmonth = nextmonth+relativedelta(day=1)

                return nextmonth



                It seems the only thing it imports is the relativedelta,
                so you could write just that instead of using a *.



                Also, I think that the last 3 statements are actually easier to read inlined:



                return today + relativedelta(months=1) + relativedelta(day=1)


                Import at the top of the file



                It's recommended to import packages at the top of the file, not inside functions.



                The above link is part of PEP8, a collection of recommended guidelines that should help you achieve the goals such as maintainability outlined in your question.



                Drop pointless statement



                In the constructor of UniReportPrinter there is this line:




                odooconnection1 = ''



                I'm wondering if you might be confusing odooconnection1 with self.odooconnection1.
                They are different variables.
                The first is a local variable,
                the second is a field of the object being created.



                If you want to unset self.odooconnection1, assign None to it instead of an empty string.



                Getters should return something



                I would expect functions with "get" in the name to return something.
                The __get_partner_details function doesn't return anything.
                I suggest to rename the method. Perhaps replace "get" with "fetch".



                Be consistent



                In some places I see print("string"), in others print "string".



                In some places I see '/foo'.format(bar), in others '%s/foo' % bar, or bar + '/foo'.



                It's good to be consistent.
                And to consistently use the right techniques.
                In each of these examples the right technique is the first.



                Use '...'.format(...)



                Especially when formatting complex strings like here,
                definitely use '...'.format(...) instead:




                return str(partner['partner_id'][0])+'-'+partner['reference'] + '.pdf'






                share|improve this answer













                Wasted looping



                Let's do a pop quiz on this code:




                partner = ([p for p in self.partners if p["id"] == invoice_id])[0]



                If self.partner contains a million values,
                and the first value has id equal to invoice_id,
                how many values will be visited?




                All of them :(




                That's unfortunate.
                It would be better to spell out a loop:



                for p in self.partners:
                if p["id"] == invoice_id:
                partner = p
                break
                else:
                partner = None


                Sure, this is longer, but it avoids a performance issue waiting to happen,
                and it also handles the case gracefully when there is no matching value.



                A compact alternative is possible using generator expressions, which are very cool:



                partner = next(p for p in self.partners if p["id"] == invoice_id)


                However, in this case, you would still need to handle the case when there is no matching value.
                You would do that by expecting StopIteration to be raised.



                Avoid wildcard imports



                It's good to avoid wildcard imports.
                It makes it easier to understand where symbols come from.
                When you import *,
                it can easily become confusing to tell where some names come from.



                In this code the wildcard import seems pointless:




                from dateutil.relativedelta import *
                today = date.today()
                nextmonth = today+relativedelta(months=+1)
                nextmonth = nextmonth+relativedelta(day=1)

                return nextmonth



                It seems the only thing it imports is the relativedelta,
                so you could write just that instead of using a *.



                Also, I think that the last 3 statements are actually easier to read inlined:



                return today + relativedelta(months=1) + relativedelta(day=1)


                Import at the top of the file



                It's recommended to import packages at the top of the file, not inside functions.



                The above link is part of PEP8, a collection of recommended guidelines that should help you achieve the goals such as maintainability outlined in your question.



                Drop pointless statement



                In the constructor of UniReportPrinter there is this line:




                odooconnection1 = ''



                I'm wondering if you might be confusing odooconnection1 with self.odooconnection1.
                They are different variables.
                The first is a local variable,
                the second is a field of the object being created.



                If you want to unset self.odooconnection1, assign None to it instead of an empty string.



                Getters should return something



                I would expect functions with "get" in the name to return something.
                The __get_partner_details function doesn't return anything.
                I suggest to rename the method. Perhaps replace "get" with "fetch".



                Be consistent



                In some places I see print("string"), in others print "string".



                In some places I see '/foo'.format(bar), in others '%s/foo' % bar, or bar + '/foo'.



                It's good to be consistent.
                And to consistently use the right techniques.
                In each of these examples the right technique is the first.



                Use '...'.format(...)



                Especially when formatting complex strings like here,
                definitely use '...'.format(...) instead:




                return str(partner['partner_id'][0])+'-'+partner['reference'] + '.pdf'







                share|improve this answer













                share|improve this answer



                share|improve this answer











                answered Apr 30 at 18:46









                janos

                95.5k12120343




                95.5k12120343






















                     

                    draft saved


                    draft discarded


























                     


                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function ()
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f193281%2fpython-script-to-download-multiple-reports-from-odoo%23new-answer', 'question_page');

                    );

                    Post as a guest













































































                    Popular posts from this blog

                    Chat program with C++ and SFML

                    Function to Return a JSON Like Objects Using VBA Collections and Arrays

                    Will my employers contract hold up in court?