Mercurial > pyarq-presupuestos
diff Generic/fiebdc.py @ 1:2ac1551ad2ab version 0.0.0
add code
author | Miguel Ángel Bárcena Rodríguez <miguelangel@obraencurso.es> |
---|---|
date | Sun, 31 Oct 2010 20:07:33 +0100 |
parents | |
children | 6502bfdaa84d |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Generic/fiebdc.py Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,2126 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +## File fiebdc.py +## This file is part of pyArq-Presupuestos. +## +## Copyright (C) 2010 Miguel Ángel Bárcena Rodríguez +## <miguelangel@obraencurso.es> +## +## pyArq-Presupuestos is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## pyArq-Presupuestos is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see <http://www.gnu.org/licenses/>. + +# specifications in http://www.fiebdc.org + +# Modules +import time +import re +import calendar +import os.path + +# pyArq-Presupuestos modules +import base +from Generic import utils +from Generic import globals + +class Read(object): + """fiebdc.Read: + + Description: + Reads and parses a fiebdc file + Constructor: + fiebdc.Read(filename=None, budget=None) + Ancestry: + +-- object + +-- Read + Atributes: + "__budget": budget ("base.Budget" object) + "__file_format": File format of the fiebdc file + "__format_list": List of file format that can be readed + "__character_sets_dict": Dictionary with the character sets supported + "__character_set": character_set of the file + "__generator": program which the file is created + "__cancel": Boolean value, True mean that the read process must stop + "__filename": The filename of the fiebdc file that is readed + "__pattern": re compiled pattern dict + Methods: + __init__(self, filename=None, budget=None) + cancel(self) + eraseControlCharacters(self, string) + validateCode(self, code) + parseDate(self, date) + parseRecord(self,record) + _parseV(self, field_list) + _parseC(self, field_list) + _parseDY(self, field_list) + _parseMN(self, field_list) + _parseT(self, field_list) + _parseK(self, field_list) + _parseW(self, field_list) + _parseL(self, field_list) + _parseQ(self, field_list) + _parseJ(self, field_list) + _parseG(self, field_list) + _parseE(self, field_list) + _parseX(self, field_list) + _parseF(self, field_list) + readFile(self, budget=None, filename=None) + """ + def __init__(self, filename=None, budget=None): + """def __init__(self, filename=None, budget=None) + + Sets the instance attributes + """ + self.__budget = budget + self.__filename = filename + if not self.__budget is None: + self.__budget.filename = self.__filename + self.__cancel = False + self.__format_list = ["FIEBDC-3/95", "FIEBDC-3/98", "FIEBDC-3/2002", + "FIEBDC-3/2004", "FIEBDC-3/2007"] + # ANSI->¿"ISO-8859-15" or "latin1 ISO-8859-1" or "cp1252 windows-1252"? + # 850 -> IBM850 -> cp850 + # 437 -> IBM437 -> cp437 + self.__character_sets_dict = {"ANSI" : "cp1252", + "850" : "850", + "437" : "cp437"} + self.__file_format = "FIEBDC-3/2007" + self.__generator = globals.version + self.__character_set = "850" + self.__pattern = { + "control_tilde" : re.compile("((\r\n)| |\t)+~"), + "control_vbar" : re.compile("((\r\n)| |\t)+\|"), + "control_backslash" : re.compile(r"((\r\n)| |\t)+\\"), + "valid_code" : re.compile("[^A-Za-z0-9ñÑ.$#%&_]"), + "special_char": re.compile("[#%&]"), + "no_float": re.compile("[^0-9.]"), + "formula" : re.compile(".*[^0123456789\.()\+\-\*/\^abcdp ].*"), + "comment": re.compile("#.*\r\n"), + "empty_line": re.compile(r"(\r\n) *\r\n"), + "space_before_backslash" : re.compile(r"( )+\\"), + "space_after_backslash" : re.compile(r"\\( )+"), + "start_noend_backslash" : re.compile("(\r\n\\\.*[^\\\])\r\n"), + "end_oper": re.compile("(\+|-|\*|/|/^|@|&|<|>|<=|>=|=|!) *\r\n"), + "matricial_var" : re.compile("(\r\n *[%|\$][A-ZÑ].*=.*,) *\r\n"), + "descomposition" : re.compile("^([^:]+):(.*)$"), + "var" : re.compile("^([$%][A-ZÑ][()0-9, ]*)=(.*)$"), + "after_first_tilde" : re.compile("^[^~]*~"), + "end_control" : re.compile("((\r\n)| |\t)+$"), + } + + def cancel(self): + """def cancel(self) + + Sets the "__cancel" attribute to True, It stops the read process. + """ + self.__cancel = True + + def eraseControlCharacters(self, string): + """eraseControlCharacters(self,string) + + Return a copy of the string with the blank characters (32), + tabs (9) and end of line (13 and 10) before of the separators + '~', '|' erased. + Before separator \ not deleted because it affects the reading of the + record ~P + """ + # "control_tilde" : "((\r\n)| |\t)+~" + string = self.__pattern["control_tilde"].sub("~",string) + # "control_vbar" : "((\r\n)| |\t)+\|" + string = self.__pattern["control_vbar"].sub("|",string) + # "control_backslash" : r"((\r\n)| |\t)+\\" + #string = self.__pattern["control_backslash"].sub(r"\\",string) + return string + + def validateCode(self, code): + """validateCode(self, code) + + Test if the code have invalid characters and try to erase it, + if it is posible return a valid code else return a empty string. + """ + if not isinstance(code, str): + print _("Invalid code, it must be a string") + return "" + # Valid chararcter: A-Z a-z 0-9 ñ Ñ . $ # % & _ + # "valid_code" : "[^A-Za-z0-9ñÑ.$#%&_]" + _code = self.__pattern["valid_code"].sub("", code) + if _code != code: + print utils.mapping(_("The code '$1' have invalid characters."), + (code,)) + code = _code + # the lasts characters can not be <#> or <##> + # <##> -> root record in FIEFDC-3 + # <#> -> chapter record in FIEFDC-3 + if len(code) > 0: + while code[-1] == "#": + code = code[:-1] + if len(code) > 20: + code = code[:20] + # only one charecter # % or & + if sum([code.count(c) for c in '#%&']) > 1: + print utils.mapping(_("The code '$1' contains special "\ + "characters repeated."),(code,)) + _i = min([code.find(c) for c in '#%&']) + code = code[:_i+1] + \ + self.__pattern["special_char"].sub("", code[_i+1:]) + return code + + def parseDate(self, date): + """parseDate(self, date) + + date: in the format: + uneven len: add a Leading 0 + len = 8 DDMMYYYY + len <= 6 DDMMYY “80/20”. >80 -> >1980 <80 -> <2080 + len < 5 MMYY + len < 3 YY + Test date string and return a tuple (YYYY, MM, DD) + or None if the date format is invalid + """ + # All characters must be numbers, len <= 8 and not empty string + if not date.isdigit() or len(date) > 8 or date == "": + return None + else: + if len(date)%2 == 1: # uneven len: add a leading 0 + date = "0" + date + if len(date) == 8: + _d = int(date[:2]) + _m = int(date[2:4]) + _y = int(date[4:8]) + elif len(date) <= 6: + _y = int(date[-2:]) + if _y < 80: _y = 2000 + _y + else: _y = 1900 + _y + if len(date) == 6: + _d = int(date[:2]) + _m = int(date[2:4]) + elif len(date) == 4: + _d = 0 + _m = int(date[:2]) + elif len(date) == 2: + _d = 0 + _m = 0 + if not _d in range(1,31): _d = 0 + if not _m in range(1,12): _m = 0 + if _m == 0: _d = 0 + if _m != 0 and _d != 0: + if calendar.monthrange(_y, _m)[1] < _d: + _d = 0 + return (_y, _m, _d) + + def parseRecord(self,record): + """parseRecord(self,record) + + record: the record line readed from the file whith the format: + type|field|field|subfield\subfield|... + [a] nothing or "a" + {a} zero or more #-#twice#-# "a" + <a> one or more #-#twice#-# "a" + Types: V C D Y M N T K L Q J G E X B F A + V: Property and Version + 1- [File_Owner] + 2- Format_Version[\DDMMYYYY] + 3- [Program_Generator] + 4- [Header]\{Title\} + 5- [Chaters_set] + 6- [Comment] + C: Record: + 1- Code{\Code} + 2- [Unit] + 3- [Summary] + 4- {Price\} + 5- {Date\} + 6- [Type] + D or Y: DECOMPOSITION or ADD DECOMPOSITION + 1- Parent Code + 2- <Child Code\ [Factor]\ [Yield]> + M or N: MEASURE or ADD MEASURE + 1- [Parent Code\]Child Code + 2- {Path\} + 3- TOTAL MEASURE + 4- {Type\Comment\Unit\Length\Width\Height\} + 5- [Label] + T: Text + 1- Code + 2- Description text + K: Coefficients + 1- { DN \ DD \ DS \ DR \ DI \ DP \ DC \ DM \ DIVISA \ } + 2- CI \ GG \ BI \ BAJA \ IVA + 3- { DRC \ DC \ DRO \ DFS \ DRS \ DFO \ DUO \ DI \ DES \ DN \ + DD \ DS \ DIVISA \ } + 4- [ n ] + L: Sheet of Conditions 1 + A) + 1- Empty + 2- {Section Code\Section Title} + B) + 1- Record Code + 2- {Section Code\Section Text} + 3- {Section Code\RTF file} + 4- {Section Code\HTM file} + Q: Sheet of Conditions 2 + 1- Record Code + 2- {Section Code\Paragraph key\{Field key;}\}| + J: Sheet of Conditions 3 + 1- Paragraph code + 2- [Paragraph text] + 3- [RTF file] + 4- [HTML file] + G: Grafic info + 1- <grafic_file.ext\> + E: Company + 1- company Code + 2 [ summary ] + 3- [ name ] + 4- { [ type ] \ [ subname ] \ [ address ] \ [ postal_code ] + \ [ town ] \ [ province ] \ [ country ] \ { phone; } + \ { fax; } \ {contact_person; } \ } + 5- [ cif ] \ [ web ] \ [ email ] \ + X: Tecnical information + A) + 1- Empty + 2- < TI_Code \ TI_Descitption \ TI_Unit > + B) + 1- Record_code + 2- < TI_Code \ TI_value > + F: #-#Adjunto#-# File + 1- Record code + 2- { Type \ { Filenames; } \ [Description] } + B: Change code + 1- Record Code + 2- New code + A: Labels + 1- Record Code + 2- <Label\> + """ + # TODO: ~L ~J RTF and HTML files + # TODO: test ~Q ~J ~G + # TODO: ~P. Registro tipo Descripción Paramétrica. + # TODO: ~O. Registro tipo Relación Comercial. + # TODO: test records + _field_list = record.split("|") + self._record_number = self._record_number +1 + _budget = self.__budget + if _field_list[0] == "V": + self._record_V_number += 1 + self._parseV(_field_list) + elif _field_list[0] == "C": + self._record_C_number += 1 + self._parseC(_field_list) + elif _field_list[0] == "D": + self._record_D_number += 1 + self._parseDY(_field_list) + elif _field_list[0] == "Y": + self._record_Y_number += 1 + self._parseDY(_field_list) + elif _field_list[0] == "M": + self._record_M_number += 1 + self._parseMN(_field_list) + elif _field_list[0] == "N": + self._record_N_number += 1 + self._parseMN(_field_list) + elif _field_list[0] == "T": + self._record_T_number += 1 + self._parseT(_field_list) + elif _field_list[0] == "K": + self._record_K_number += 1 + self._parseK(_field_list) + elif _field_list[0] == "W": + self._record_W_number += 1 + self._parseW(_field_list) + elif _field_list[0] == "L": + self._record_L_number += 1 + self._parseL(_field_list) + elif _field_list[0] == "Q": + self._record_Q_number += 1 + self._parseQ(_field_list) + elif _field_list[0] == "J": + self._record_J_number += 1 + self._parseJ(_field_list) + elif _field_list[0] == "G": + self._record_G_number += 1 + self._parseG(_field_list) + elif _field_list[0] == "E": + self._record_E_number += 1 + self._parseE(_field_list) + elif _field_list[0] == "O": + self._record_O_number += 1 + elif _field_list[0] == "P": + self._record_P_number += 1 + self._parseP(_field_list) + elif _field_list[0] == "X": + self._record_X_number += 1 + self._parseX(_field_list) + elif _field_list[0] == "B": + self._record_B_number += 1 + self._parseB(_field_list) + elif _field_list[0] == "F": + self._record_F_number += 1 + self._parseF(_field_list) + elif _field_list[0] == "A": + self._record_A_number += 1 + self._parseA(_field_list) + else: + self._record_Unknow_number += 1 + + def _parseV(self, field_list): + """_parseV(self, field_list) + + field_list: field list of the record + 0- V :Property and Version + 1- [File_Owner] + 2- Format_Version[\DDMMYYYY] + 3- [Program_Generator] + 4- [Header]\{Title\} + 5- [Chaters_set] + 6- [Comment] + 7- [Data type] + 8- [Number budget certificate] + 9- [Date budget certificate] + """ + if self._record_number != 1: + print utils.mapping(_("The 'V' record (Property and Version) "\ + "must be the first record in the file but it is the "\ + "number: $1"), (self._record_number,)) + print _("The default values were taken and this V record is "\ + "ignored") + return + # _____number of fields_____ + # Any INFORMATION after last field separator is ignored + if len(field_list) > 10: + field_list = field_list[:10] + # If there are no sufficient fields, the fields are added + # with empty value:"" + else: + field_list = field_list + [""]*(10-len(field_list)) + # control character are erased: end of line, tab, space + # only leading and trailing whitespace in owner, generator, comment + # _____Fields_____ + _record_type = self.delete_control_space(field_list[0]) + _owner = field_list[1].strip() + _owner = self.delete_control(_owner) + _version_date = self.delete_control_space(field_list[2]) + _generator = field_list[3].strip() + _generator = self.delete_control(_generator) + _header_title = field_list[4].strip() + _header_title = self.delete_control(_header_title) + _character_set = self.delete_control_space(field_list[5]) + _comment = field_list[6].strip("\t \n\r") + _data_type = self.delete_control_space(field_list[7]) + _number_certificate = self.delete_control_space(field_list[8]) + __date_certificate = self.delete_control_space(field_list[9]) + # _____Owner_____ + self.__budget.setOwner(_owner) + # _____Version-Date_____ + _version_date = _version_date.split("\\") + _file_format = _version_date[0] + if _file_format in self.__format_list: + self.__file_format = _file_format + print _("FIEBDC format: %s" % _file_format) + if len(_version_date) > 1: + _date = _version_date[1] + if _date != "": + _parsed_date = self.parseDate(_date) + if _parsed_date is not None: + self.__budget.setDate(_parsed_date) + # _____Generator_____ + # ignored field + print _("FIEBDC file generated by %s" % _generator) + # _____Header_Title_____ + _header_title = _header_title.split("\\") + _header_title = [_title.strip() for _title in _header_title] + _header = _header_title.pop(0) + _title = [ ] + for _title_index in _header_title: + if _title_index != "": + _title.append(_title_index) + if _header != "": + self.__budget.setTitleList([ _header, _title ]) + # _____Characters_set_____ + # field parsed in readFile method + # _____Comment_____ + if _comment != "": + self.__budget.setComment(_comment) + # _____Data type_____ + # 1 -> Base data. + # 2 -> Budget. + # 3 -> Budget certificate. + # 4 -> Base date update. + try: + _data_type = int(_data_type) + except ValueError: + _data_type = "" + if _data_type == 3: + # _____Number budget certificate_____ + try: + _number_certificate = int(_number_certificate) + except ValueError: + _number_certificate = "" + # _____Date budget certificate_____ + if _date_certificate != "": + _parsed_date_certificate = self.parseDate(_date_certificate) + if _parsed_date_certificate is None: + _date_certificate = "" + else: + _date_certificate = _parsed_date_certificate + self.__budget.setBudgetype(_data_type) + self.__budget.setCertificateOrder(_number_certificate) + self.__budget.setCertificateDate(_parsed_date_cerfificate) + elif _data_type != "": + self.__budget.setBudgeType(_data_type) + self.num_valid_record = self.num_valid_record + 1 + + def _parseK(self, field_list): + """_parseK(self, field_list) + + field_list: field list of the record + 0- K: Coefficients + 1- { DN \ DD \ DS \ DR \ DI \ DP \ DC \ DM \ DIVISA \ } + 2- CI \ GG \ BI \ BAJA \ IVA + 3- + A){ DRC \ DC \ DRO \ DFS \ DRS \ DFO \ DUO \ DI \ DES \ DN \ + DD \ DS \ DIVISA \ } + B){ DRC \ DC \ \ DFS \ DRS \ \ DUO \ DI \ DES \ DN \ + DD \ DS \ DSP\ DEC\ DIVISA \ } + 4- [ n ] + """ + # _____Number of fields_____ + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + # The last field is ignored, pyArq hate dll's + if len(field_list) > 4: + field_list = field_list[1:4] + # The record must have 3 fields + else: + field_list = field_list[1:] + [""]*(4-len(field_list)) + # control character are erased: end of line, tab, space + # _____Fields_____ + _field0 = self.delete_control_space(field_list[0]) + _field1 = self.delete_control_space(field_list[1]) + _field2 = self.delete_control_space(field_list[2]) + # _____Field 1_____ + if len(_field1) > 0 and _field1[-1] == "\\": + _field1 = _field1[:-1] + # if there are a \ character at the end it must be erased + _percentages = _field1.split("\\") + if len(_percentages) > 5: + _percentages = _percentages[:5] + # If there are no sufficient subfields, the subfields are added + # with empty value:"" + else: + _percentages = _percentages + [""]*(5-len(_percentages)) + _percentage_titles = [ "CI", "GG", "BI", "BAJA", "IVA" ] + _percentage_dict = {} + for _percentage_index in range(len(_percentages)): + try: + _percentage = int(_percentages[_percentage_index]) + except ValueError: + _percentage = "" + _percentage_dict[_percentage_titles[_percentage_index]] = \ + _percentage + self.__budget.setPercentages(_percentage_dict) + # _____Field 0 and 1_____ + # Default number of decimal places + # Number of titles in ~V record + _title_num = len(self.__budget.getTitleList()[1]) + if _title_num == 0: _title_num = 1 + # If the field 2 is empty, the field 0 is readed + if _field2 == "": + # _____Field 0_____ + if _field0[-1] == "\\": + _field0 = _field0[:-1] + # if there are a \ character at the end it must be erased + _decimal_list = _field0.split("\\") + _decimal_index = 0 + if len(_decimal_list)%9 != 0: + # if it is not multiple of 9, empty subfield are added + _decimal_list = _decimal_list + [""]*(9 - \ + len(_decimal_list)%9) + # The number of decimal values is the same as the numbers of + # titles in the V record + if len(_decimal_list)//9 > _title_num: + _decimal_list = _decimal_list[:_title_num*9] + elif len(_decimal_list)//9 < _title_num: + _decimal_list = _decimal_list + _decimal_list[-9:] * \ + (_title_num-(len(_decimal_list)//9)) + while _decimal_index <= len(_decimal_list)-9: + _decimals = _decimal_list[_decimal_index:(_decimal_index + 9)] + _forlist = range(len(_decimals)-1) + for _index in range(len(_decimals)): + try: + #TODO: test this + _decimals[_index] = int(_decimals[_index]) + except ValueError: + _decimals[_index] = "" + _DN = _decimals[0] + _DD = _decimals[1] + _DS = _decimals[2] + _DR = _decimals[3] + _DI = _decimals[4] + _DP = _decimals[5] + _DC = _decimals[6] + _DM = _decimals[7] + _DIVISA = _decimals[8] + _percentage_dict = {"DN" : _DN, + "DD" : _DD, + "DSP" : _DS, + "DS" : _DS, + "DFC" : _DR, + "DFPU" : _DR, + "DFUO" : _DR, + "DFA" : _DR, + "DRC" : _DR, + "DRPU" : _DR, + "DRUO" : _DR, + "DRA" : _DR, + "DP" : _DC, + "DC" : _DC, + "DPU" : _DC, + "DUO" : _DC, + "DEA" : _DC, + "DES" : _DC, + "DIR" : _DI, + "DIRC" : _DI, + "DCD" : _DP, + "DIVISA": _DIVISA } + _decimal_index = _decimal_index + 9 + self.__budget.setDecimals(_percentage_dict, + (_decimal_index//9)) + else: + # _____Field 3_____ + if _field2[-1] == "\\": + _field2 = _field2[:-1] + # if there are a \ character at the end it must be erased + _decimal_list = _field2.split("\\") + # test if the Divisa subfield is 12 or 14 position + # Divisa is the only Alphanumeric subfield + # "no_float": "[^0-9.]" + if len(_decimal_list) >= 13 and \ + self.__pattern["no_float"].search(_decimal_list[12]): + _multiple = 13 + elif len(_decimal_list) >= 15 and \ + self.__pattern["no_float"].search(_decimal_list[14]): + _multiple = 15 + else: + if self.__file_format == "FIEBDC-3/2002": + _multiple = 13 + elif self.__file_format == "FIEBDC-3/2004": + _multiple = 13 + elif self.__file_format == "FIEBDC-3/2007": + _multiple = 15 + else: + _multiple = 15 + _decimal_index = 0 + if len(_decimal_list)%_multiple != 0 : + # if it is not multiple of _multiple, empty subfield are added + _decimal_list = _decimal_list + \ + [""]*(_multiple-len(_decimal_list)%_multiple) + # The number of decimal values is the same as the numbers of + # titles in the V record + if len(_decimal_list)//_multiple > _title_num: + _decimal_list = _decimal_list[:_title_num*_multiple] + elif len(_decimal_list)//_multiple < _title_num: + _decimal_list = _decimal_list + [_decimal_list[-_multiple:]]*\ + (_title_num-(len(_decimal_list)//_multiple)) + while _decimal_index <= len(_decimal_list)-_multiple: + _decimals = _decimal_list[_decimal_index:(_decimal_index +\ + _multiple)] + for _index in range(len(_decimals)-1): + try: + _decimals[_index] = int(_decimals[_index]) + except: + _decimals[_index] = "" + if _multiple == 13: + _DRC = _decimals[0] + _DC = _decimals[1] + _DRO = _decimals[2] + _DFS = _decimals[3] + _DRS = _decimals[4] + _DFO = _decimals[5] + _DUO = _decimals[6] + _DI = _decimals[7] + _DES = _decimals[8] + _DN = _decimals[9] + _DD = _decimals[10] + _DS = _decimals[11] + _DIVISA = _decimals[12] + _percentage_dict = { + "DN" : _DN, + "DD" : _DD, + "DSP" : _DS, + "DS" : _DS, + "DFC" : _DFS, + "DFPU" : _DRC, + "DFUO" : _DFS, + "DFA" : _DFS, + "DRC" : _DRS, + "DRPU" : _DRC, + "DRUO" : _DRS, + "DRA" : _DRS, + "DP" : _DC, + "DC" : _DC, + "DPU" : _DC, + "DUO" : _DUO, + "DEA" : _DES, + "DES" : _DES, + "DIR" : _DI, + "DIRC" : _DC, + "DCD" : _DI, + "DIVISA": _DIVISA, + } + else: # _multiple == 15: + _DRC = _decimals[0] + _DC = _decimals[1] + _DRO = _decimals[2] + _DFS = _decimals[3] + _DRS = _decimals[4] + _DFO = _decimals[5] + _DUO = _decimals[6] + _DI = _decimals[7] + _DES = _decimals[8] + _DN = _decimals[9] + _DD = _decimals[10] + _DS = _decimals[11] + _DSP = _decimals[12] + _DEC = _decimals[13] + _DIVISA = _decimals[14] + _percentage_dict = { + "DN" : _DN, + "DD" : _DD, + "DSP" : _DSP, + "DS" : _DS, + "DFC" : _DFS, + "DFPU" : _DRC, + "DFUO" : _DFS, + "DFA" : _DFS, + "DRC" : _DRS, + "DRPU" : _DRC, + "DRUO" : _DRS, + "DRA" : _DRS, + "DP" : _DC, + "DC" : _DC, + "DPU" : _DC, + "DUO" : _DUO, + "DEA" : _DEC, + "DES" : _DES, + "DIR" : _DI, + "DIRC" : _DC, + "DCD" : _DI, + "DIVISA": _DIVISA} + _decimal_index = _decimal_index + 13 + self.__budget.setDecimals(_percentage_dict, + (_decimal_index//13)) + self.num_valid_record = self.num_valid_record +1 + + def _parseC(self, field_list): + """_parseC(self, field_list) + + field_list: field list of the record + 0- C: Record + 1- Code{\Code} + 2- [Unit] + 3- [Summary] + 4- {Price\} + 5- {Date\} + 6- [Type] + """ + # _____number of fields_____ + # Any INFORMATION after last field separator is ignored + if len(field_list) > 7: + field_list = field_list[:7] + # If there are no sufficient fields, the fields are added + # with empty value:"" + else: + field_list = field_list + [""]*(7-len(field_list)) + # control character are erased: en of line, tab, space + # _____Fields_____ + _record_type = field_list[0] + _codes = self.delete_control_space(field_list[1]) + _unit = self.delete_control_space(field_list[2]) + _summary = self.delete_control(field_list[3]) + _prices = self.delete_control_space(field_list[4]) + _dates = self.delete_control_space(field_list[5]) + _type = self.delete_control_space(field_list[6]) + # _____Code_____ + _codes = _codes.split("\\") + if len(_codes) > 0: + # parse the hierarchy of the first code + # hierarchy: 0->root, 1->Chapter/subchapter, 2->other + if len(_codes[0]) > 2 and _codes[0][-2:] == "##": + _hierarchy = 0 + elif len(_codes[0]) > 1 and _codes[0][-1:] == "#": + _hierarchy = 1 + else: + _hierarchy = 2 + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + # maximun len 20 characters + _codes = [self.validateCode(_code) for _code in _codes] + # empty codes are ignored + while "" in _codes: + _codes.remove("") + if len(_codes) > 0: + #TODO: test this + _code = _codes[0] + _synonyms = _codes + else: + print _("Record C without a valid code") + return + # _____Unit_____ + # nothing to do + # _____Summary_____ + # nothing to do + # _____Price_____ and _____Dates_____ + # last \ is erased + if len(_dates) > 0 and _dates[-1] == "\\": + _dates = _dates[:-1] + if len(_prices) > 0 and _prices[-1] == "\\": + _prices = _prices[:-1] + _dates = _dates.split("\\") + _prices = _prices.split("\\") + # number of prices = number of titles in "V" line + # if there are no sufficient prices it takes the last price defined + _title_num = len(self.__budget.getTitleList()[1]) + if _title_num == 0: _title_num = 1 + if len(_prices) > _title_num: _prices = _prices[:_title_num] + elif len(_prices) < _title_num: + _prices = _prices + [_prices[-1]]*(_title_num-len(_prices)) + # number of dates = number of prices + # if there are no sufficient dates it takes the last date defined + if len(_dates) > len(_prices): _dates = _dates[:len(_prices)] + elif len(_dates) < len(_prices): + _dates = _dates + [_dates[-1]]*(len(_prices)-len(_dates)) + for _index in range(len(_prices)): + # TODO: lack to specify the number of decimals of the price + try: + _prices[_index] = float(_prices[_index]) + except: + _prices[_index] = 0.0 + _parsed_date = self.parseDate(_dates[_index]) + if _parsed_date is None: + _dates[_index] = "" + else: + _dates[_index] = _parsed_date + # _____Type_____ + # 0 Without classifying + # EA Auxiliary element + # EU Unitary element + # EC Complex element + # EF Functional element + # OB Construction site + # PA Cost overrun + # PU Unitary budget + # 1 Labourforce + # H Labourforce + # 2 Machinery and auxiliary equipment + # Q Machinery + # % Auxiliary equipment + # 3 Building materials + # MC Cement + # MCr Ceramic + # MM Wood + # MS Iron and steel + # ME Energy + # MCu Copper + # MAl Aluminium + # ML Bonding agents + # M Others materials + # Hierarchy type subtype + # 0->root -> 0 -> None,OB + # 1->[sub]chapter -> 0 -> None,PU + # 2->Other -> 0 -> None,EA,EU,EC,EF,PA + # 1 -> None,H + # 2 -> None,Q,% + # 3 -> None,MC,MCr,MM,MS,ME,MCu,Mal,ML,M + if _hierarchy == 0: + if _type == "OB": + _subtype = _type + _type = 0 + elif _type == "0" or _type == "": + _subtype = "" + _type = 0 + else: + print utils.mapping(_("Incorrect type ($1) in the code $2"), + (str(_type), _code)) + _type = 0 + _subtype = "" + elif _hierarchy == 1: + if _type == "PU": + _subtype = _type + _type = 0 + elif _type == "0" or _type == "": + _subtype = "" + _type = 0 + else: + print utils.mapping(_("Incorrect type ($1) in the code $2"), + (str(_type), _code)) + _type = 0 + _subtype = "" + else: + if _type == "EA" or _type == "EU" or _type == "EC" or \ + _type == "EF" or _type == "PA": + _subtype = _type + _type = 0 + elif _type == "H": + _subtype = _type + _type = 1 + elif _type == "Q" or _type == "%": + _subtype = _type + _type = 2 + elif _type == "MC" or _type == "MCr" or _type == "MM" or \ + _type == "MS" or _type == "ME" or _type == "MCu" or \ + _type == "Mal" or _type == "ML" or _type == "M": + _subtype = _type + _type = 3 + elif _type == "0" or _type == "1" or _type == "2" or \ + _type == "3": + _subtype = "" + _type = int(_type) + elif _type == "": + _subtype = "" + _type = 0 + else: + print utils.mapping(_("Incorrect type ($1) in the code $2"), + (str(_type), _code)) + _type = 0 + _subtype = "" + self.__budget.setRecord(_code, _synonyms, _hierarchy, + _unit, _summary, _prices, _dates, _type, _subtype) + self.num_valid_record = self.num_valid_record + 1 + + def _parseDY(self, field_list): + """_parseDY(self, field_list) + + field_list: field list of the record + 0- D or Y: DECOMPOSITION or ADD DECOMPOSITION + 1- Parent Code + 2- <Child Code\ [Factor]\ [Yield]> + """ + # _____number of fields_____ + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[:3] + # If there are no sufficient fields, the fields are added + # with empty value:"" + else: + field_list = field_list + [""]*(3-len(field_list)) + # control character are erased: end of line, tab, space + # _____Fields_____ + _record_type = field_list[0] + _code = self.delete_control_space(field_list[1]) + _children = self.delete_control_space(field_list[2]) + # _____Code_____ + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + _code = self.validateCode(_code) + # _____children_____ + # TODO: test the number of decimals in factor an yield values + _children = _children.split( "\\" ) + _children_list = [ ] + _child_index = 0 + while _child_index < len(_children)-3: + # _____subfields_____ + _child_code = _children[_child_index] + _factor = _children[_child_index+1] + _yield = _children[_child_index+2] + # _____child_code_____ + _child_code = self.validateCode(_child_code) + # _____factor_____ + if _factor != "": + try: + _factor = float(_factor) + except ValueError: + print utils.mapping(_("ValueError loadig the "\ + "descomposition of the record $1, the factor "\ + "of the child $2 must be a float number and "\ + "can not be $3, seted default value 1.0"), + (_code, _child_code, _factor)) + _factor = 1.0 + #____yield___ + if _yield != "": + try: + _yield = float(_yield) + except ValueError: + print utils.mapping(_("ValueError loading the "\ + "descomposition of the record $1, the yield of "\ + "the child $2, must be a float number and can"\ + "not be $3, seted default value 1.0"), + (_code, _child_code, _factor)) + _yield = 1.0 + if _child_code != "" and _code != "": + _children_list.append([_child_code, _factor, _yield ]) + if _record_type == "D": + _position = _child_index / 3 + else: #_record_type == "Y" + _position = -1 + self.__budget.setTree(_code, _child_code, _position, _factor, + _yield, "", "", "", "") + _child_index = _child_index + 3 + self.num_valid_record = self.num_valid_record +1 + + def _parseT(self, field_list): + """_parseT(self, field_list) + + field_list: field list of the record + 0- T: Text + 1- Record code + 2- Description text + """ + # _____Number of fields_____ + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[0:3] + field_list = field_list[1:] + if len(field_list) != 2: + return + # control character are erased: end of line, tab, space + # _____Fields_____ + _code = self.delete_control_space(field_list[0]) + _text = field_list[1] + # _____Code_____ + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + _code = self.validateCode(_code) + # _____Text_____ + self.__budget.setText(_code, _text) + self.num_valid_record = self.num_valid_record + 1 + + def _parseMN(self, field_list): + """_parseMN(self, field_list) + + field_list: field list of the record + 0- M or N: MEASURE or ADD MEASURE + 1- [Parent Code\]Child Code + 2- {Path\} + 3- TOTAL MEASURE + 4- {Type\Comment\Unit\Length\Width\Height\} + 5- [Label] + """ + + # _____Number of fields_____ + # Any INFORMATION after last field separator is ignored + # The record must have 6 fields + if len(field_list) > 6: + field_list = field_list[:6] + # If there are no sufficient fields, the fields are added + # with empty value:"" + else: + field_list = field_list + [""]*(6-len(field_list)) + # control character are erased: end of line, tab, space + # _____Fields_____ + _record_type = field_list[0] + _codes = self.delete_control_space(field_list[1]) + _path = self.delete_control_space(field_list[2]) + _total = self.delete_control_space(field_list[3]) + _lines = self.delete_control(field_list[4]) + _label = self.delete_control_space(field_list[5]) + # _____Codes_____ + _code_list = _codes.split( "\\" ) + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + if len(_code_list) == 2: + _parent_code = self.validateCode(_code_list[0]) + if _parent_code == "": + _parent_code = None + _child_code = self.validateCode(_code_list[1]) + elif len(_code_list) == 1: + _child_code = self.validateCode(_code_list[0]) + _parent_code = None + else: + print utils.mapping(_("Invalid codes in $1 record, codes $2"), + (_record_type, _codes)) + return + if _child_code == "": + print utils.mapping(_("Empty child code in $1 record, codes: "\ + "$2"), (_record_type, _codes)) + return + # _____Path_____ + # TODO: path=0, no-estructured measures + _path_list = _path.split( "\\" ) + if len(_path_list) > 0: + while _path_list[-1] == "": + _path_list = _path_list[:-1] + _path = _path_list[-1] + try: + _path = int(_path) + except ValueError: + print utils.mapping(_("Invalid path in $1 record, "\ + "codes $2"), (_record_type, _codes)) + return + if _path > 0: + _path -= 1 + else: + _path = 0 + # _____Total_____ + try: + _total = float(_total) + except ValueError: + print utils.mapping(_("Invalid Total Measure value in $1 "\ + "record, codes $2"), (_record_type, _codes)) + return + # _____Measure lines_____ + _lines = _lines.split( "\\" ) + _line_index = 0 + _line_list = [ ] + while _line_index < len(_lines)-6: + _linetype = _lines[_line_index] + if _linetype == "": + _linetype = 0 + elif _linetype == "1" or _linetype == "2" or \ + _linetype == "3": + _linetype = int(_linetype) + else: + _linetype = 0 + _comment= _lines[_line_index + 1] + if _linetype == 3: + # "formula": ".*[^0123456789\.()\+\-\*/\^abcdp ].*" + if self.__pattern["formula"].match(_comment): + print utils.mapping(_("The comment is not a formula or "\ + "its have invalid characters, in the $1 record, "\ + "codes $2"), (_record_type, _codes)) + return + else: + _formula = _comment + _comment = "" + else: + _formula = "" + _units = _lines[_line_index + 2] + _length = _lines[_line_index + 3] + _width = _lines[_line_index + 4] + _height = _lines[_line_index + 5] + try: + if _units != "": _units = float(_units) + if _length != "": _length = float(_length) + if _width != "": _width = float(_width) + if _height != "": _height = float(_height) + except ValueError: + print utils.mapping("The measure values are not float "\ + "numbers, code $1", (_codes,)) + return + _line_list.append([_linetype, _comment, _units, + _length, _width, _height, _formula]) + _line_index = _line_index + 6 + self.__budget.setTree(_parent_code, _child_code, _path, "", "", + _total, _line_list, _label, _record_type) + self.num_valid_record = self.num_valid_record + 1 + + def _parseW(self, field_list): + """_parseW(self, field_list) + + field_list: field list of the record + 0- W: Geografical field + 1- Field Code + 2- Field + """ + # _____Number of fields_____ + # Any INFORMATION after last field separator is ignored + # The record must have 2 fields + if len(field_list) >= 2: + field_list = field_list[1:2] + else: + return + # control character are erased: end of line, tab, space + # _____Fields_____ + _code_fields = field_list[0] + # last \ is erased + if len(_code_fields) and _code_fields[-1] == "\\": + _code_fields = _code_fields[:-1] + _code_fields = _code_fields.split("\\") + _field_dict = {} + _field_index = 0 + while _field_index < len(_code_fields)-1: + # _____subfields_____ + _field_code = _code_fields[_field_index] + _field_title = _code_fields[_field_index+1] + # control character are erased: end of line, tab, space + # _____section_code_____ + #"control": "[\t \n\r]" + _field_code = self.delete_control_space(_field_code) + # _____section_title_____ + if _field_code != "": + _field_dict[_field_code] = _field_title + _field_index = _field_index + 2 + self.__budget.setSheetFields(_field_dict) + self.num_valid_record = self.num_valid_record +1 + + def _parseL(self, field_list): + """_parseL(self, field_list) + + field_list: field list of the record + 0- L: Sheet of Conditions 1 + A: + 1- Empty + 2- {Section Code\Section Title} + B: + 1- Record Code + 2- {Section Code\Section Text} + 3- {Section Code\RTF file} + 4- {Section Code\HTM file} + """ + # _____Number of fields_____ + # The record must have at least 3 fields + if len(field_list) < 3: + return + _code = field_list[1] + if _code == "": + # A: Section Titles + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[0:3] + field_list = field_list[1:3] + # _____Fields_____ + _section_codes = field_list[1] + # last \ is erased + if len(_section_codes) and _section_codes[-1] == "\\": + _section_codes = _section_codes[:-1] + _section_codes = _section_codes.split("\\") + _section_dict = {} + _section_index = 0 + while _section_index < len(_section_codes)-1: + # _____subfields_____ + _section_code = _section_codes[_section_index] + + _section_title = _section_codes[_section_index+1] + # control character are erased: end of line, tab, space + # _____section_code_____ + _section_code = self.delete_control_space(_section_code) + # _____section_title_____ + _section_title = self.delete_control_space(_section_title) + if _section_code != "": + _section_dict[_section_code] = _section_title + _section_index = _section_index + 2 + self.__budget.setSheetSections(_section_dict) + self.num_valid_record = self.num_valid_record +1 + + else: + # Any INFORMATION after last field separator is ignored + # The record must have 5 fields + if len(field_list) > 5: + field_list = field_list[0:5] + field_list = field_list[1:] + # _____Fields_____ + # _____Record Code_____ + _record_code = self.delete_control_space(field_list[0]) + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + _record_code = self.validateCode(_record_code) + _scodes_text = field_list[1] + if _scodes_text == "": + # TODO: rtf and html files + print "Html and rtf files not implemented in ~L record" + else: + # _____Section-code_Section-text_____ + # last \ is erased + if len(_scodes_text) and _scodes_text[-1] == "\\": + _scodes_text = _scodes_text[:-1] + _scodes_text = _scodes_text.split("\\") + _paragraph_dict = {} + _section_dict = {} + _section_index = 0 + while _section_index < len(_scodes_text)-1: + # _____subfields_____ + _section_code = _scodes_text[_section_index] + _section_text = _scodes_text[_section_index+1] + # control character are erased: end of line, tab, space + # _____section_code_____ + _section_code = self.delete_control_space(_section_code) + # _____section_text_____ + if _section_code != "" and _section_text != "": + #-# paragraph #-# + _paragraph_code = _record_code + _section_code + "*" + _paragraph_dict[ _paragraph_code ] = _section_text + _section_dict[_section_code] = _paragraph_code + _section_index = _section_index + 2 + self.__budget.setSheetParagraphs(_paragraph_dict) + self.__budget.setSheetRecord(_record_code, "*", _section_dict) + self.num_valid_record = self.num_valid_record +1 + + def _parseQ(self, field_list): + """_parseQ(self, field_list) + + field_list: field list of the record + 0- Q: Sheet of Conditions 2 + 1- Record Code + 2- {Section Code\Paragraph key\{Field key;}\}| + """ + # _____Number of fields_____ + # The record must have at least 3 fields + if len(field_list) < 3: + return + _code = field_list[1] + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[0:3] + field_list = field_list[1:] + # _____Fields_____ + # _____Record Code_____ + _record_code = self.delete_control_space(field_list[0]) + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + _record_code = self.validateCode(_record_code) + _scodes_pkey = field_list[1] + # last \ is erased + if len(_scodes_pkey) and _scodes_pkey[-1] == "\\": + _scodes_pkey = _scodes_pkey[:-1] + _scodes_pkey = _scodes_pkey.split("\\") + _field_dict = {} + _section_index = 0 + while _section_index < len(_scodes_pkey) -1: + # _____subfields_____ + _section_code = _scodes_pkey[_section_index] + _paragraph_key = _scodes_text[_section_index+1] + _field_keys = _scodes_text[_section_index+2] + # control character are erased: end of line, tab, space + # _____section_code_____ + _section_code = self.delete_control_space(_section_code) + # _____section_text_____ + _paragraph_key = self.delete_control_space(_paragraph_key) + # _____Fields keys_____ + _field_keys = self.delete_control_space(_field_keys) + # last ; is erased + if len(_field_keys) and _field_keys[-1] == ";": + _field_keys = _field_keys[:-1] + _field_keys_list = _scodes_pkey.split(";") + for _field_key in _field_keys_list: + if _field_key != "" and _section_code != "" and \ + _paragraph_key != "": + if _field_key in _field_dict: + _section_dict = _field_dict[_field_key] + else: + _section_dict = {} + _field_dict[_field_key] = _section_dict + _section_dict[_section_code] = _paragraph_code + _section_index = _section_index + 3 + for _field, _section_dict in _field_dict.iteritems(): + self.__budget.setSheetRecord(_record_code, _field, _section_dict) + self.num_valid_record = self.num_valid_record +1 + + def _parseJ(self, field_list): + """_parseJ(self, field_list) + + field_list: field list of the record + 0- J: Sheet of Conditions 3 + 1- Paragraph code + 2- [Paragraph text] + 3- [RTF file] + 4- [HTML file] + """ + # _____Number of fields_____ + # The record must have at least 3 fields + if len(field_list) < 3: + return + # Any INFORMATION after last field separator is ignored + # The record must have 5 fields + if len(field_list) > 5: + field_list = field_list[0:5] + field_list = field_list[1:] + # _____Fields_____ + # _____Paragraph code_____ + _paragraph_code = self.delete_control_space(field_list[0]) + # _____Paragraph text_____ + _paragraph_text = field_list[1] + if _paragraph_text == "": + # TODO: rtf and html files + print "Html and rtf files not implemented in ~J record" + else: + self.__budget.setSheetParagraph(paragraph_code, paragraph_text) + self.num_valid_record = self.num_valid_record +1 + + def _parseG(self, field_list): + """_parseG(self, field_list) + + field_list: field list of the record + 0- G: Grafic info + 1- record code + 2- <grafic_file.ext\> + """ + # _____Number of fields_____ + # The record must have at least 3 fields + if len(field_list) < 3: + return + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[0:3] + field_list = field_list[1:] + # _____Fields_____ + # _____Record Code_____ + _record_code = self.delete_control_space(field_list[0]) + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + _record_code = self.validateCode(_record_code) + # _____Grafic files_____ + _grafic_files = self.delete_control(field_list[1]) + # _____subfields_____ + # last \ is erased + if len(_grafic_files) and _grafic_files[-1] == "\\": + _grafic_files = _grafic_files[:-1] + _grafic_file_list = _grafic_files.split("\\") + _tested_grafic_file_list = [] + for _grafic_file in _grafic_file_list: + _path = os.path.dirname(self.__filename) + _grafic_file_path = os.path.join(_path, _grafic_file) + if os.path.exists(_grafic_file_path): + _tested_grafic_file_list.append(_grafic_file_path) + else: + _name_ext = os.path.splitext(_grafic_file) + _grafic_file_name = _name_ext[0] + _grafic_file_ext = _name_ext[1] + _grafic_file_name_u = _grafic_file_name.upper() + _grafic_file_name_l = _grafic_file_name.lower() + _grafic_file_ext_u = _grafic_file_ext.upper() + _grafic_file_ext_l = _grafic_file_ext.lower() + _uu = _grafic_file_name_u + _grafic_file_ext_u + _ul = _grafic_file_name_u + _grafic_file_ext_l + _lu = _grafic_file_name_l + _grafic_file_ext_u + _ll = _grafic_file_name_l + _grafic_file_ext_l + _grafic_file_path_uu = os.path.join(_path, _uu) + _grafic_file_path_ul = os.path.join(_path, _ul) + _grafic_file_path_lu = os.path.join(_path, _lu) + _grafic_file_path_ll = os.path.join(_path, _ll) + if os.path.exists(_grafic_file_path_uu): + _tested_grafic_file_list.append(_grafic_file_path_uu) + elif os.path.exists(_grafic_file_path_ul): + _tested_grafic_file_list.append(_grafic_file_path_ul) + elif os.path.exists(_grafic_file_path_lu): + _tested_grafic_file_list.append(_grafic_file_path_lu) + elif os.path.exists(_grafic_file_path_ll): + _tested_grafic_file_list.append(_grafic_file_path_ll) + else: + print utils.mapping(_("The file $1 do not exist"), + (_grafic_file_path,)) + if len(_grafic_file_list) > 0: + for _grafic_file in _tested_grafic_file_list: + self.__budget.addFile(_record_code, _grafic_file, "img", "") + self.num_valid_record = self.num_valid_record +1 + + def _parseE(self, field_list): + """_parseE(self, field_list) + + field_list: field list of the record + 0- E: Company + 1- company Code + 2 [ summary ] + 3- [ name ] + 4- { [ type ] \ [ subname ] \ [ address ] \ [ postal_code ] + \ [ town ] \ [ province ] \ [ country ] \ { phone; } + \ { fax; } \ {contact_person; } \ } + 5- [ cif ] \ [ web ] \ [ email ] \ + """ + + # _____Number of fields_____ + # Any INFORMATION after last field separator is ignored + # The record must have 6 fields + if len(field_list) > 6: + field_list = field_list[1:6] + # If there are no sufficient fields, the fields are added + # with empty value:"" + else: + field_list = field_list[1:] + [""]*(6-len(field_list)) + # _____Fields_____ + # _____company Code_____ + _company_code = self.delete_control_space(field_list[0]) + if _company_code == "": + return + # _____Summary_____ + + _sumamary = self.delete_control(field_list[1]) + # _____Name_____ + _name = self.delete_control(field_list[2]) + # _____local_offices_____ + _local_offices = self.delete_control(field_list[3]) + # _____subfields of local_offices_____ + # last \ is erased + if len(_local_offices) and _local_offices[-1] == "\\": + _local_offices = _local_offices[:-1] + _local_offices_list = _local_offices.split("\\") + # If there are no sufficent subfields, the subfields are added + # whith empty value + _nsub = len(_local_offices_list) % 10 + if _nsub != 0: + _local_offices_list = _local_offices_list + \ + [""]*(10-len(field_list)) + _local_offices = [] + _local_offices_index = 0 + while _local_offices_index < len(_local_offices_list)-9: + # _____subfields_____ + _type = _local_offices_list[_local_offices_index] + _subname = _local_offices_list[_local_offices_index+1] + _address = _local_offices_list[_local_offices_index+2] + _postal_code = _local_offices_list[_local_offices_index+3] + _town = _local_offices_list[_local_offices_index+4] + _province = _local_offices_list[_local_offices_index+5] + _country = _local_offices_list[_local_offices_index+6] + _phone = _local_offices_list[_local_offices_index+7] + # last ; is erased + if len(_phone) and _phone[-1] == ";": + _phone = _phone[:-1] + _phone_list = _phone.split(";") + _fax = _local_offices_list[_local_offices_index+8] + # last ; is erased + if len(_fax) and _fax[-1] == ";": + _fax = _fax[:-1] + _fax_list = _fax.split(";") + _contact_person = _local_offices_list[_local_offices_index+9] + if _type != "" or _subname != "" or _address != "" or \ + _postal_code != "" or _town != "" or _province != "" or \ + _country != "" or _phone != "" or _fax != "" or \ + _contact_person != "": + _local_offices.append([_type, _subname, _address, + _postal_code, _town, _province, + _country, _phone_list, _fax_list, + _contact_person]) + _local_offices_index = _local_offices_index + 10 + # _____cif web email_____ + _c_w_e = self.delete_control_space(field_list[4]) + # last \ is erased + if len(_c_w_e) and _c_w_e[-1] == "\\": + _c_w_e = _c_w_e[:-1] + _c_w_e_list = _c_w_e.split("\\") + # _____subfields_____ + # If there are no sufficient fields, the fields are added + # with empty value:"" + _c_w_e_list = _c_w_e_list + [""]*(3-len(_c_w_e_list)) + _cif = _c_w_e_list[0] + _web = _c_w_e_list[1] + _email = _c_w_e_list[2] + self.__budget.setCompany(_company_code, _sumamary, _name, + _local_offices, _cif, _web, _email) + self.num_valid_record = self.num_valid_record +1 + + def _parseX(self, field_list): + """_parseX(self, field_list) + + field_list: field list of the record + A) + 0- X: Tecnical information + 1- Empty + 2- < TI_Code \ TI_Descitption \ TI_Unit > + B) + 0- X: Tecnical information + 1- Record_code + 2- < TI_Code \ TI_value > + """ + # Tecnical information + # The record must have at least 3 fields + if len(field_list) < 3: + return + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[0:3] + field_list = field_list[1:] + # _____Fields_____ + # "control": "[\t \n\r]" + _field_1 = self.delete_control_space(field_list[0]) + _field_2 = self.delete_control_space(field_list[1]) + if _field_1 == "": + # A) + _field_2_list = _field_2.split("\\") + _ti_index = 0 + while _ti_index < len(_field_2_list)-3: + _ti_code = _field_2_list[_ti_index] + _ti_description = _field_2_list[_ti_index+1] + _ti_unit = _field_2_list[_ti_index+2] + if _ti_code != "": + self.__budget.addTecInfo(_ti_code, _ti_description, + _ti_unit) + _ti_index = _ti_index + 3 + else: + # B) + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + _record_code = self.validateCode(_field_1) + _field_2_list = _field_2.split("\\") + _ti_index = 0 + _ti_dict = {} + while _ti_index < len(_field_2_list)-2: + _ti_code = _field_2_list[_ti_index] + _ti_value = _field_2_list[_ti_index+1] + if _ti_code != "" and _ty_value != "": + _ti_dict[_ti_code] = _ty_value + _ti_index = _ti_index + 2 + self.__budget.setTecnicalInformation(_record_code, _ti_dict) + self.num_valid_record = self.num_valid_record +1 + + def _parseF(self, field_list): + """_parseF(self, field_list) + + field_list: field list of the record + 0- F: Files + 1- Record code + 2- { Type \ { Filenames; } \ [Description] } + """ + + # _____Number of fields_____ + # The record must have at least 3 fields + if len(field_list) < 3: + return + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[0:3] + field_list = field_list[1:] + # _____Fields_____ + # _____Record Code_____ + _record_code = self.delete_control_space(field_list[0]) + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + _record_code = self.validateCode(_record_code) + # _____Grafic files_____ + _files = self.delete_control(field_list[1]) + # _____subfields_____ + # last \ is erased + if len(_files) and _files[-1] == "\\": + _files = _files[:-1] + _files_list = _files.split("\\") + # adding empty subfiels if necesary + if len(_files_list)%3 > 0: + _files_list.extend[""]*(3 - len(_files_list)%3) + _file_index = 0 + _tested_files_list = [] + while _file_index < len(_files_list)-3: + _type = _files_list[_file_index].replace(" ","") +## _types = { +## "0": _("others"), +## "1": _("características técnicas y de fabricación"), +## "2": _("manual de colocación, uso y mantenimiento"), +## "3": _("certificado/s de elementos y sistemas"), +## "4": _("normativa y bibliografía"), +## "5": _("tarifa de precios"), +## "6": _("condiciones de venta"), +## "7": _("carta de colores"), +## "8": _("ámbito de aplicación y criterios selección"), +## "9": _("cálculo de elementos y sistemas"), +## "10": _("presentación, datos generales, objetivos, etc. de "\ +## "empresa"), +## "11": _("certificado/s de empresa"), +## "12": _("obras realizadas")} + _types = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "11", "12"] + if not _type in _types: + _type = "0" + _filenames = _files_list[_file_index + 1] + _description = _files_list[_file_index + 2] + _file_index += 3 + if len(_filenames) and _filenames[-1] == ";": + _files = _files[:-1] + _filenames_list = _files.split(";") + _path = os.path.dirname(self.__filename) + for _filename in filenames_list: + _file_path = os.path.join(_path, _filename) + if os.path.exists(_file_path): + _tested_files_list.append([_file_path, _type, + _description]) + else: + _name_ext = os.path.splitext(_filename) + _file_name = _name_ext[0] + _file_ext = _name_ext[1] + _file_name_u = _file_name.upper() + _file_name_l = _file_name.lower() + _file_ext_u = _file_ext.upper() + _file_ext_l = _file_ext.lower() + _uu = _file_name_u + _file_ext_u + _ul = _file_name_u + _file_ext_l + _lu = _file_name_l + _file_ext_u + _ll = _file_name_l + _file_ext_l + _file_path_uu = os.path.join(_path, _uu) + _file_path_ul = os.path.join(_path, _ul) + _file_path_lu = os.path.join(_path, _lu) + _file_path_ll = os.path.join(_path, _ll) + if os.path.exists(_file_path_uu): + _tested_files_list.append([_file_path_uu, _type, + _description]) + elif os.path.exists(_grafic_file_path_ul): + _tested_files_list.append([_file_path_ul, _type, + _description]) + elif os.path.exists(_grafic_file_path_lu): + _tested_files_list.append([_file_path_lu, _type, + _description]) + elif os.path.exists(_grafic_file_path_ll): + _tested_files_list.append([_file_path_ll, _type, + _description]) + else: + print utils.mapping(_("The file $1 do not exist"), + (_file_path,)) + if len(_tested_files_list) > 0: + for _file in _tested_file_list: + self.__budget.addFile(_record_code, _file[0], file[1], file[2]) + self.num_valid_record = self.num_valid_record +1 + + def _parseB(self, field_list): + """_parseB(self, field_list) + + field_list: field list of the record + 0- B: Change code + 1- Record Code + 2- New code + """ + # _____Number of fields_____ + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[0:3] + field_list = field_list[1:] + if len(field_list) != 2: + return + # control character are erased: end of line, tab, space + # _____Fields_____ + _code = self.delete_control_space(field_list[0]) + _new_code = self.delete_control_space(field_list[1]) + # _____Codes_____ + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + _code = self.validateCode(_code) + _new_code = self.validateCode(_new_code) + # change code + self.__budget.changeCode(_code, _new_code) + self.num_valid_record = self.num_valid_record + 1 + + def _parseA(self, field_list): + """_parseA(self, field_list) + + field_list: field list of the record + 0- A: Labels + 1- Record Code + 2- <Label\> + """ + # _____Number of fields_____ + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[0:3] + field_list = field_list[1:] + if len(field_list) != 2: + return + # control character are erased: end of line, tab, space + # _____Fields_____ + # "control": "[\t \n\r]" + _code = self.delete_control_space(field_list[0]) + _labels = self.delete_control_space(field_list[1]) + # _____Codes_____ + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + _code = self.validateCode(_code) + # _____Labels_____ + # last \ is erased + # TODO: change the others parsers to this: + while len(_labels) > 0 and _labels[-1] == "\\": + _labels = _labels[:-1] + # replace "_" to " " + _labels = _labels.replace("_"," ") + _label_list = _labels.split("\\") + for _label in _label_list: + self.__budget.addLabel(_code, _label) + self.num_valid_record = self.num_valid_record + 1 + + def _parseP(self, field_list): + """_parseP(self, field_list) + + field_list: Parametric record + A) Global paremetric record + 0- P: Parametric + 1- Empty + 2- [Parametric description] + 3- [library.DLL] + B) Family Parametric record + 0- P: Parametric + 1- Family Code + 2- [Parametric description] + """ + # TODO: Use global parametric record + if len(field_list) > 2: + # delete control caracters and spaces + _family_code = self.delete_control_space(field_list[1]) + if _family_code == "": # A)Global paremetric record + # The record must have 3 or 4 fields + if len(field_list) > 4: + field_list = field_list[0:4] + field_list = field_list[1:] + if len(field_list) == 2: + field_list.append("") + if len(field_list) != 3: + return + else: # B)Family Parametric record + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[0:3] + field_list = field_list[1:] + if len(field_list) != 2: + print _("PyArq hates parametric DLLs") + return + else: + return + # _____Description_____ + _description = field_list[1] + if _description == "": + print _("PyArq hates parametric DLLs") + return + # Adding last end of line + _description = _description + "\r\n" + # Delete comments + # "comment" : "#.*\r\n" + _description = self.__pattern["comment"].sub("\r\n",_description) + # Tabs to spaces + _description = _description.replace("\t"," ") + # Delete empty lines + # "empty_line": r"(\r\n) *\r\n" + while self.__pattern["empty_line"].search(_description): + _description = self.__pattern["empty_line"].sub( + lambda x: x.groups()[0], _description) + # Delete spaces before and after / + # "space_before_backslash" : r"( )+\\" + _description = self.__pattern["space_before_backslash"].sub( + r"\\",_description) + # "space_after_backslash" : r"\\( )+" + _description = self.__pattern["space_after_backslash"].sub( + r"\\",_description) + # Join lines that start but not end with / + _description = "\r\n" + _description # add leading end of line + # "start_noend_backslash": "(\r\n\\\.*[^\\\])\r\n" + while self.__pattern["start_noend_backslash"].search(_description): + _description = self.__pattern["start_noend_backslash"].sub( + lambda x: x.groups()[0], _description) + # Join lines that end with a + - * / ^ and @ & < > <= >= = <> ! + # "end_oper" : "(\+|-|\*|/|/^|@|&|<|>|<=|>=|=|!) *\r\n" + _description = self.__pattern["end_oper"].sub( + lambda x: x.groups()[0], _description) + # Join lines for matricial vars + # matricial_var : "(\r\n *[%|\$][A-ZÑ].*=.*,) *\r\n" + while self.__pattern["matricial_var"].search(_description): + _description = self.__pattern["matricial_var"].sub( + lambda x: x.groups()[0], _description) + _description = _description[2:] # remove leading end of line + #_description = re.sub(r"\\( )+",r"\\",_description) + _lines = _description.split("\r\n") + _final_description = "" + _pass_line = 0 + for index in range(len(_lines)): + _line = _lines[index] + # Parse lines + if len(_line) != 0: # Delete empty lines + if _pass_line > 0: + _pass_line = _pass_line -1 + _line = "" + elif _line.isspace(): + _line = "" + elif _line[0] != "\\": + # Delete spaces out "" delimiter + _list = _line.split('"') + _final_line = "" + for index1 in range(len(_list)): + if index1 % 2 != 0: + _parcial_line = '"' + _list[index1] + else: + _parcial_line = '"' + _list[index1].replace(" ","") + _final_line = _final_line + _parcial_line + _line = _final_line[1:] + _lines[index] = _line + # parse data + if len(_line) > 2 and _line[:2] == "::": + # Delete spaces out " delimiter + #print "__PRECIO__" + _line[2:] + pass + elif len(_line) > 2 and _line[:2] == "%:": + # Delete spaces out " delimiter + #print "__%AUX__" + _line[2:] + pass + elif len(_line) > 3 and _line[:2] == "%%:": + # Delete spaces out " delimiter + #print "__%%AUX__" + _line[2:] + pass + elif self.__pattern["var"].search(_line): + # Delete spaces out " delimiter + #print "line =", _line + while _line.count('"') % 2 == 1 and \ + index + _pass_line + 1 < len(_lines) -1: + _line = _line + _lines[index + _pass_line + 1] + _pass_line = _pass_line + 1 + _search = self.__pattern["var"].search(_line) + if _search is not None: + _var = _search.groups()[0] + " = " + _search.groups()[1] + #print "__VAR__" + str(_var) + pass + else: + #print "no __VAR__", _line + pass + elif self.__pattern["descomposition"].search(_line): + # Delete spaces out " delimiter + #_patern = "(^[^:]*):(.*)$" + _search = self.__pattern["descomposition"].search(_line) + if _search is not None: + _var = _search.groups()[0] + ":" + _search.groups()[1] + #print "__Descomposición__" + str(_var) + pass + else: + #print "no __Descomposición__", _line + pass + else: + print "Parametric: code: " + _family_code + print "******* Desconocido *** : " + _line + if index-10 > 0: print "-11 :", _lines[index-11] + if index-10 > 0: print "-10 :", _lines[index-10] + if index-9 > 0: print "-9 :", _lines[index-9] + if index-8 > 0: print "-8 :", _lines[index-8] + if index-7 > 0: print "-7 :", _lines[index-7] + if index-6 > 0: print "-6 :", _lines[index-6] + if index-5 > 0: print "-5 :", _lines[index-5] + if index-4 > 0: print "-4 :", _lines[index-4] + if index-3 > 0: print "-3 :", _lines[index-3] + if index-2 > 0: print "-2 :", _lines[index-2] + if index-1 > 0: print "-1 :", _lines[index-1] + print "-0 :", _lines[index-0] + pass + else: + _parameter_list = _line.split("\\")[1:-1] + if len(_parameter_list) >= 2: + if _parameter_list[0] == "C" or \ + _parameter_list[0] == "COMENTARIO": + #print "__COMENTARIO__" + _parameter_list[1] + self.__budget.setParametricSelectComment( + _family_code, _parameter_list[1]) + elif _parameter_list[0] == "R" or \ + _parameter_list[0] == "RESUMEN": + #print "__RESUMEN__" + _parameter_list[1] + self.__budget.setParametricSummary(_family_code, + _parameter_list[1]) + elif _parameter_list[0] == "T" or \ + _parameter_list[0] == "TEXTO": + #print "__TEXTO__" + _parameter_list[1] + self.__budget.setParametricText(_family_code, + _parameter_list[1]) + elif _parameter_list[0] == "P" or \ + _parameter_list[0] == "PLIEGO": + #print "__PLIEGO__" + str(_parameter_list[1:]) + pass + elif _parameter_list[0] == "K" or \ + _parameter_list[0] == "CLAVES": + #print "__CLAVES__" + str(_parameter_list[1:]) + pass + elif _parameter_list[0] == "F" or \ + _parameter_list[0] == "COMERCIAL": + #print "__COMERCIAL__" + str(_parameter_list[1:]) + pass + else: + #print "==PARAMETRO==" + str(_parameter_list[:]) + pass + _final_description = _final_description + _line + "\r\n" + + #print _line + # Delete last empty line + _description = _final_description[:-2] + _lines = _description.split("\r\n") + for _line in _lines: + pass + #print _line + self.num_valid_record = self.num_valid_record + 1 + + def readFile(self, budget=None, filename=None, interface=None): + """readFile(self, budget=None, filename=None) + + filename: the filename of the fiebdc file + budget: base.obra object + interface: a object to send messages + must have printf(message) progress(percent) + recordStatistics(...) + Return the budget objetc or None if the file can be readed + """ + if filename != None and budget != None: + self.__filename = filename + self.__budget = budget + self.__budget.filename = self.__filename + if self.__filename is None or self.__budget is None or \ + self.__cancel == True: + return None + if not os.path.exists(self.__filename): + return None + _time = time.time() + try: + _file = open(self.__filename, 'r') + except IOError: + print utils.mapping("IOError: $1", (self.__filename,)) + return None + self.__budget.filename = self.__filename + self._record_number = 0 + self.num_valid_record = 0 + self._record_V_number = 0 + self._record_C_number = 0 + self._record_D_number = 0 + self._record_Y_number = 0 + self._record_M_number = 0 + self._record_N_number = 0 + self._record_T_number = 0 + self._record_K_number = 0 + self._record_W_number = 0 + self._record_L_number = 0 + self._record_Q_number = 0 + self._record_J_number = 0 + self._record_G_number = 0 + self._record_E_number = 0 + self._record_O_number = 0 + self._record_P_number = 0 + self._record_X_number = 0 + self._record_B_number = 0 + self._record_F_number = 0 + self._record_A_number = 0 + self._record_Unknow_number = 0 + print utils.mapping(_("Loading file $1"), (self.__filename,)) + _filesize = float(os.path.getsize(self.__filename)) + interface.progress(_file.tell() / _filesize) + _buffer = _file.read(1000) + # set codepage from V record + _record_list = _buffer.split("~") + registro_V = _record_list[1] + # ~V|[PROPIEDAD_ARCHIVO]|VERSION_FORMATO[\DDMMAAAA]|[PROGRAMA_EMISION]| + # [CABECERA]\{ ROTULO_IDENTIFICACION \}|[JUEGO_CARACTERES]| + # [COMENTARIO]|[TIPO INFORMACIÓN]|[NÚMERO CERTIFICACIÓN]| + # [FECHA CERTIFICACIÓN ] | + registro_V = registro_V.split("|") + if registro_V[0] == "V": + #_codepage = registro_V[5] + if len(registro_V) > 5: + _version = registro_V[5].strip() + # remove leading spaces + if _version in self.__character_sets_dict: + self.__character_set = self.__character_sets_dict[_version] + else: + print utils.mapping(_("This codepage do not exist in "\ + "FIEBDC3! Default codepage: $1"), + (self.__character_set,)) + else: + print utils.mapping(_("This V record dot have a codepage! "\ + "Default codepage: $1"), + (self.__character_set,)) + else: + print utils.mapping(_("Not 'V' record in File! Default codepage: "\ + "$1"), (self.__character_set,)) + if self.__character_set != "utf8": + _buffer = unicode(_buffer, self.__character_set) + _buffer = _buffer.encode("utf8") + # Any INFORMATION between the beginning of the file and the + # beginning of the first registry “~” is ignored + #"after_first_tilde" : "^[^~]*~" + _buffer = self.__pattern["after_first_tilde"].sub("",_buffer) + while _buffer != "" and self.__cancel != True: + #-# the blank characters (32), tabs (9) and end of line (13 and 10) + # before the separators '~', '|' are erased. + # Before separator \ not deleted because it affects the reading of + # the record ~P + _buffer = self.eraseControlCharacters(_buffer) + _record_list = _buffer.split("~") + # The last record can be incomplete unless it is the last one of + # the file + if len(_record_list) > 1: + # not the end + _last_record = _record_list.pop() + else: + # the end record + # The blank characters (32), tabs (9) and end of line + # (13 and 10) at the end of the file are ignored. + #"end_control" : "((\r\n)| |\t)+$" + _record_list[-1] = self.__pattern["end_control"].sub("", + _record_list[-1]) + _last_record = "" + for record in _record_list: + if self.__cancel == True: + break + self.parseRecord(record) + interface.progress(_file.tell() / _filesize) + _buffer2 = _file.read(100000) + if self.__character_set != "utf8": + _buffer2 = unicode(_buffer2, self.__character_set) + _buffer2 = _buffer2.encode("utf8") + _buffer = _last_record + _buffer2 + _file.close() + if self.__cancel == True: + print _("Cancelled process") + return None + else: + print utils.mapping(_("Time to load: $1 seconds"), + (("%.2f" %(time.time()-_time)),)) + print utils.mapping(_("Records/Valid Records: $1/$2"), + (self._record_number, self.num_valid_record)) + if self._record_O_number > 0: + print utils.mapping(_("$1 unsuported record type O: "\ + "Comercial Relationship"), (self._record_O_number,)) + if self.num_valid_record == 0: + print _("This file is not a valid FIBDC3 file") + return None + _str = "" + for type in \ + [("V", self._record_V_number), + ("C", self._record_C_number), + ("D", self._record_D_number), + ("Y", self._record_Y_number), + ("M", self._record_M_number), + ("N", self._record_N_number), + ("T", self._record_T_number), + ("K", self._record_K_number), + ("W", self._record_W_number), + ("L", self._record_L_number), + ("Q", self._record_Q_number), + ("J", self._record_J_number), + ("G", self._record_G_number), + ("E", self._record_E_number), + ("O", self._record_O_number), + ("P", self._record_P_number), + ("X", self._record_X_number), + ("B", self._record_B_number), + ("F", self._record_F_number), + ("A", self._record_A_number), + ("?", self._record_Unknow_number)]: + _str = _str + "%s: %s\n" %(type[0], type[1]) + print _str + self._testBudget(self.__budget) + return self.__budget + + def _testBudget(self, budget): + """testBudget(self,budget) + + budget: base.obra object + Test and repair budget object after read it from bc3 file + """ + # TODO: more to do here + print _("Testing budget ...") + # Add price to records without price + _iter = budget.iter() + _titlelist = budget.getTitleList()[1] + if len(_titlelist) == 0: + _titlenum = 1 + else: + _titlenum = len(_titlelist) + for _code in _iter: + _record = budget.getRecord(_code) + _prices = _record.getPrices() + _len_prices = len(_prices) + if _titlenum > _len_prices: + _leftprices = _titlenum - _len_prices + for _index in range(0,_leftprices): + _root = budget.getRecord(budget.getRoot()) + _price = [0.0, _root.getDate(_len_prices + _index)] + budget.addPriceToRecord(_price,_record) + print _("End Test") + + def delete_control_space(self, text): + text = self.delete_control(text) + text = text.replace(" ", "") + return text + + def delete_control(self, text): + text = text.replace("\t", "") + text = text.replace("\r", "") + text = text.replace("\n", "") + return text