Mercurial > pyarq-presupuestos
diff Generic/base.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 | a7b9f7e7dfa4 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Generic/base.py Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,3453 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +## File base.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/>. + +"""base module + +In this module are defined the data structures in the +classes: + * Record: data of each record + * ParamentricRecord: data of each parametric record + * Decomposition: data of the decomposition of each record + * Measure: data of the measure of each record + * MeasureLine: each measure line data + * Decimals: data of the decimal places of all the numbers in a budget + * Sheet: data of the sheet of conditions of a budget + * Budget: all data of a budget + * Company: company data + * Office: company office data + * File: file data + * RecordType: Record type data + +schema: + * Budget: + +-- __records: dictionary records { code : Record } + * Record: + +-- code: record code + +-- synonyms: list of synonym codes + +-- hierarchy: A integer number: + 0 -> root + 1 -> Chapter/Subchapter + 2 -> Other + +-- unit: unit of measure of the record + +-- summary: Short description of the record + +-- prices: List of Prices/Dates + +-- type + +-- subtype + "type" and "subtype": + 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 + +-- parents: List of parent codes + +-- children: list of Decomposition + * Decomposition: + +-- position: Position of the child in the parent descomposition + TODO: change this: the position of the record in the budget + +-- code: child record code + +-- budget: list of budget and amended budget measures + * Measure: + +-- measure: Total result of measure + +-- lines: List of measure lines + * MeasureLine: + +-- type: Line type: + empty string -> Normal + 1 -> Parcial Subtotal + 2 -> Accumulated Subtotal + 3 -> Formula, the comment is a formula. + +-- comment: Can be a descriptive text or a formula + Valid Operator: '(', ')', '+', '-', '*', '/' and + '^' + Valid variable: 'a', 'b', 'c','d' y 'p' + (Pi=3.1415926) + +-- units: Number of Units (a) + +-- length: length (b) + +-- width: width (c) + +-- height: height (d) + +-- label: Record Identifiers that are used by some measure + programs + +-- factor: Factor + +-- yield_: Yield + +-- certification: list of certifications for months measures + * Measure + +-- real_cost: list of real cost of construction for months + measures + * Measure + +-- cost_goals: list of cost goals of construction for months + measures + * Measure + +-- cost_planned: list of costs planned and amended cost planned + measures + * Measure + +-- text: Long Description of the record + +-- sheet: Sheet of conditions object + * Sheet: + +-- sheet_dict: + { <Field key> : { <Section key> : <Paragraph key>}} + +-- files: List of file object + +-- file + * Name + * Type + * Description + +-- __synonyms: synonyms dictionary. TODO + +-- __root: root record code + +-- __decimals: decimals dictionay = { int : Decimals } + * Decimals: + +-- DN: Number of decimal places of the field "equal-size parts" in the + measure lines. + Default: 2 decimal places. + +-- DD: Number of decimal places of the three dimensions in the + measure lines. + Default: 2 decimal places. + +-- DS: Number of decimal places of the total sum of a measure. + Default: 2 decimal places. + +-- DFP: Number of decimal places of the yield factor in a + decomposition of a budget record. + Default: 3 decimal places. + +-- DFC: Number of decimal places of the yield factor in a + decomposition of a chapter or subchapter, and in its measure lines. + Dafault: 3 decimal places. + +-- DFUO: Number of decimal places of the yield factor in a + decomposition of a unit of work. + Default: 3 decimal places. + +-- DFA: Number of decimal places of the yield factor in a + decomposition of a Auxiliary element. + Default: 3 decimal places. + +-- DRP: Number of decimal places of the yield in a decomposition + of a budget record. + Number of decumal places of the result of the multiplication of + the factor and the yield in a decompositon of a budget. + Default: 3 decimal places. + +-- DRC: Number of decimal places of the yield (or measure) in a + decomposition of a chapter or subchapter. + Number of decimal places of the result of the multiplictaion of + the yield (or measure) and the factor in a decomposition of a + chapter or subcharter. + Default: 3 decimal places. + +-- DRUO: Number of decimal places of the yield in a decomposition of a + unit of work. + Decimal places of the result of the multiplication of the yield + and the factor in a descomposition of a unit of work. + Default: 3 decimal places. + +-- DRA: Number of decimal places of the yield in a decompositon of a + auxiliar element. + Number of decimal places of the result of the multiplication of + the yield and the factor in a descomposition of a auxilar element. + Default: 3 decimal places. + +-- DP: Number of decimal places of the price of a budget. + Default: 2 decimal places. + +-- DC: Number of decimal places of the price of a chapter or + subchapter. + Default: 2 decimal places. + +-- DUO: Number of decimal places of the price of a unit of work. + Default: 2 decimal places. + +-- DEA: Number of decimal places of the price of a auxiliar element. + Default: 2 decimal places. + +-- DES: Number of decimal places of the price of the simple elements. + Default: 2 decimal places. + +-- DIR: Number of decimal places of the resulting amount to multiply + the total yield and the price of the elements of a unit of work. + (When there are not measures) + +-- DIM: Number of decimal places of the resulting amount to multiply + the total yield and the price of the elements of a unit of work. + (When there are measures) + +-- DIRC: Number of decimal places of the resulting amount to multiply + the total yield and the price of the elements of a budget, chapter + or a subchapter.(When there are not measures) + +-- DIMC: Number of decimal places of the resulting amount to multiply + the total yield and the price of the elements of a budget, chapter + or a subchapter. (When there are measures) + +-- DCD: Number of decimal places ot the resulting amount to sum the + direct costs of a unit of work (and auxiliar element). + Number of decimal places of the indirect costs. + Default: 2 decimal places. + +-- DIVISA: monetary unit. + +-- __percentages: percentages dictionary: + { "CI" : "", + "GG" : "", + "BI" : "", + "BAJA": "", + "IVA" : ""} + +-- __file_owner + +-- __title_list: titles list: [ "Header", ["Title1", "Title2", ... ] ] + +-- __title_index: A integer. The active group of Prices and Decimals. + +-- __sheet_sections: sheet sections dictionary { sheet_code : sheet_title } + +-- __sheet_fields: sheet fields dictionary { field_code : field_title } + +-- __sheet_paragraphs: sheet paragraphs dictionary + { paragraph_code : paragraph_text} + +-- __companys: Dictionary whith companys object + { company_code: company_object } + * Comapany: + +-- code: company code + +-- summary: short name + +-- name: long name + +-- offices: List of offices: + * Office: + +-- type: office type + "C" Central office. + "D" Local Office. + "R" Performer. + +-- subname: Office name + +-- address: Ofiice address + +-- postal_code: postal code + +-- town: town + +-- province: province/state + +-- country: country + +-- phone: list of phone numbers + +-- fax: list of fax numbers + +-- contact_person: Contact person in the office + +-- cif: CIF + +-- web: web page + +-- email: email +""" + +# Modules +import re +import datetime +import os + +# pyArq-Presupuestos modules +from Generic import fiebdc +from Generic import utils + +class Record(object): + """base.Record: + + Description: + Record object + Constructor: + base.Record(code, synonyms, hierarchy, unit, summary, prices, type, + subtype, text="") + Ancestry: + +-- object + +-- Record + Atributes: + "code": Code string + "recordType": RecordType object + "synonyms": List of synonym codes. + "parents":List of parent codes + "children": Decomposition list, + list of "Decomposition" instances + "unit": measure unit of the record + "summary": Short description of the record + "prices": List of prices/dates + "text": Long Description of the record + "sheet": Sheet of conditions object + "files": List of file object + "labels": List of record labels + Methods: + __getstate__(self) + __setstate__(self, tuple) + __init__(self, filename=None, budget=None) + {get/set}Code + {get/set}Synonyms + {get/set}RecordType + {get/set}Unit + {get/set}Summary + {get/set}Prices + addPrice + _validate_price_date + getPrice + getAmount + getDate + {get/set}Parents + appendParent + {get/set}children + appendChild + {get/set}Text + {get/set}Sheet + {get/set}Files + addFile + {get/set}Labels + addLabel + """ + __slots__ = ["_Record__code", "_Record__synonyms", + "_Record__recordType", "_Record__unit", + "_Record__summary", "_Record__prices", + "_Record__parents", "_Record__children", + "_Record__text", "_Record__sheet", + "_Record__files", "_Record__labels"] + + def __getstate__(self): + return (self.__code, self.__synonyms, self.__recordType, + self.__unit, self.__summary, self.__prices, + self.__parents, self.__children, self.__text, + self.__sheet, self.__files, self.__labels) + + def __setstate__(self, tuple): + self.__code = tuple[0] + self.__synonyms = tuple[1] + self.__recordType = tuple[2] + self.__unit = tuple[3] + self.__summary = tuple[4] + self.__prices = tuple[5] + self.__parents = tuple[6] + self.__children = tuple[7] + self.__text = tuple[8] + self.__sheet = tuple[9] + self.__files = tuple[10] + self.__labels = tuple[11] + + def __init__(self, decimals, code, synonyms, hierarchy, unit, summary, + prices, type, subtype, parents=[], text=""): + self.code = code + self.synonyms = synonyms + self.recordType = (hierarchy, type, subtype) + self.unit = unit + self.summary = summary + self.setPrices(prices, decimals) + self.parents = parents + self.children = [] + self.text = text + self.sheet = Sheet() + self.files = [] + self.labels = [] + + def getCode(self): + return self.__code + + def setCode(self, code): + """setCode(self,code) + + Sets the code, must be a valid code + """ + if not utils.is_valid_code(code)[0]: + raise ValueError, utils.mapping(_("Invalid code: $1"),(str(code),)) + self.__code = code + + def getSynonyms(self): + return self.__synonyms + + def setSynonyms(self,synonyms): + """setSynonyms(self,synonyms) + + Sets the synonyms codes of the record. + synonyms must fulfill: + - must be a list + - the items must be valid codes + """ + if not isinstance(synonyms, list): + raise TypeError, utils.mapping(_("Synonyms ($1) must be a list, " \ + "code: $2"), (str(synonyms), self.__code)) + for code in synonyms: + if not utils.is_valid_code(code)[0]: + raise ValueError, utils.mapping(_("Invalid Code in synomyms "\ + "list ($1) code: $2"), (str(code), self.__code)) + self.__synonyms = synonyms + + def getRecordType(self): + return self.__recordType + + def setRecordType(self, recordType): + """setRecordType(self, recordType) + + Set the record type. + recordType (hierarchy, type,subtype) + + hierarchy must be -1, 0, 1 or 2 + type must be 0, 1, 2, 3 or a empty string + subtype must be EA, EU, EC, EF, OB, PA, PU, H, Q, %, MC, MCr, + MM, MS, ME, MCu, MAl, ML, M, or a empty string + """ + _recordType = RecordType(recordType[0],recordType[1],recordType[2]) + self.__recordType = _recordType + + def getUnit(self): + return self.__unit + + def setUnit(self,unit): + """setUnit(self,unit) + + Set the unit of measure + The unit must be a string. + """ + if not isinstance(unit, str): + raise TypeError, utils.mapping(_("Unit ($1) must be a string: $2"), + (str(unit), self.__code)) + self.__unit = unit + + def getSummary(self): + return self.__summary + + def setSummary(self,summary): + """setSummary(self,summary) + + Set the summary of a record + The summary must be a string. + """ + if not isinstance(summary, str): + raise TypeError, utils.mapping(_("Summary ($1) must be a string: "\ + "$1"), (str(summary), self.__code)) + self.__summary = summary + + def getPrices(self): + return self.__prices + + def setPrices(self, prices, decimals): + """setPrice(self, prices, decimals) + + Set the price list of the record. + prices must fulfill: + - it must be a list + - the items must be a list with two items + - the first item: price must be a float + """ + if not isinstance(prices, list): + raise TypeError, utils.mapping(_("Prices ($1) must be a list: $2"), + (str(prices), self.__code)) + for index in range(len(prices)): + _price_date = prices[index] + _price_date = self._validate_price_date(_price_date, decimals) + prices[index] = _price_date + self.__prices = prices + + def addPrice(self, price_date, decimals): + """addPrice(self, price_date, decimals) + + Add a price to the price list of the record. + price must fulfill: + - must be a list with two items + - the first item: price must be a float + """ + price_date = self._validate_price_date(price_date, decimals) + self.__prices.append(price_date) + + def _validate_price_date(self, price_date, decimals): + if not isinstance(price_date, list) and len(price_date) == 2: + raise ValueError, utils.mapping(_("Price ($1) must be a list"\ + " with two items: $2"), (str(price_date), self.__code)) + _price = price_date[0] + _date = price_date[1] + if not isinstance(_price, float): + raise TypeError, utils.mapping(_("Price must be a float "\ + "number: $1"), (str(_price),)) + _D = decimals.getD(self.recordType) + _price = round(_price, _D) + price_date[0] = _price + # TODO: validate date + return price_date + + def getPrice(self, index_price): + if len(self.__prices) <= index_price: + raise IndexError, _("The record do not have this Price. Code: %s" + % self.__code) + return self.__prices[index_price][0] + + def getDate(self, index_price): + if len(self.__prices) <= index_price: + raise IndexError, _("The record do not have this Price") + return self.__prices[index_price][1] + + def getParents(self): + return self.__parents + + def setParents(self,parents): + """setParents(self,parents) + + Sets the list of parents codes of the record. + parents must fulfill + - it must be a list + - the items must be valid codes + """ + if not isinstance(parents, list): + raise TypeError, utils.mapping(_("Parents ($1) must be a list: $2"), + (str(parents), self.__code)) + for parent in parents: + if not utils.is_valid_code(parent)[0]: + raise ValueError, utils.mapping(_("Invalid parent code ($1) " \ + "in the record: $2"), (str(padre), self.__code)) + self.__parents = parents + + def appendParent(self, parent): + """appendParent(self, parent) + + parent must be a valid code + Append a parent to the list of parents codes of the record. + + """ + if not utils.is_valid_code(parent)[0]: + raise ValueError, utils.mapping(_("Invalid parent code ($1) " \ + "in the record: $2"), (str(parent), self.__code)) + self.__parents.append(parent) + + def getchildren(self): + return self.__children + + def setchildren(self,children): + """setchildren(self,children) + + Sets the list of children of a record + children must fulfill + - it must be a list + - the items must be instances of Decomposition class + """ + if not isinstance(children, list): + raise TypeError, utils.mapping(_("children ($1) must be a list, "\ + "record: $2"), (str(children), self.__code)) + for _child in children: + if not isinstance(_child, Decomposition): + raise ValueError, utils.mapping(_("child ($1) must be a "\ + "Decomposition object, record: $2"), + (str(_child), self.__code)) + _record_code = self.code + for _measure_list in [_child.budgetMeasures, _child.certification, + _child.real_cost, _child.cost_goals, + _child.cost_planned]: + if isinstance(_measure_list, list): + for _measure in _measure_list: + _measurerecordCode = _record_code + self.__children = children + + def appendChild(self, child_code, decimals, factor=0.0, yield_=0.0, + measure=0.0, measure_list=[], type ="", label=""): + """appendChildren(self, child_code, factor=0.0, yield_=0.0, + measure=0.0, measure_list=[], type ="", label="")) + + position: + child_code: + factor: + yield_: + measure: + measure_list: + type: + label: + + Append a child to the list of children + """ + _measure = Measure(decimals, self.recordType, + measure, [], label, factor, yield_) + if len(measure_list) > 0: + measure.buildMeasure(_measure, measure_list, type, decimals, + self.recordType) + _position = len(self.__children) + _child = Decomposition(_position, child_code, [_measure]) + self.__children.append(_child) + return _child + + def getText(self): + return self.__text + + def setText(self,text): + """setText(self,text) + + Sets the text of the record + It must be a string + """ + if not isinstance(text, str): + raise TypeError, utils.mapping(_("Text ($1) must be a string, "\ + "record: $2"), (str(text), self.__code)) + self.__text = text + + def getSheet(self): + return self.__sheet + + def setSheet(self, sheet): + """setSheet(self, sheet) + + Sets the sheet of condition object + """ + if not isinstance(sheet, Sheet): + raise ValueError, _("sheet must be a Sheet instance") + self.__sheet = sheet + + def getFiles(self): + return self.__files + + def setFiles(self, files): + """setFiles(self, files) + + Sets the files list + """ + # TODO: only sets files and File object format (durusdatabase) + if not isinstance(files, list): + raise ValueError, utils.mapping(_("files must be a list: $1"), + str(files)) + _files = [] + for file in files: + if isinstance(file, File): + _files.append(file) + elif isinstance(file, list): + _file_path = file[0] + _type = file[1] + _description = file[2] + if not os.path.exists(file[0]): + raise ValueError, _("Incorrect path") + _file = File(file_path, type, description) + _files.append(_file) + else: + raise ValueError, utils.mapping(_( + "file must be a list or a File object: $1"),str(file)) + self.__files = _files + + + def addFile(self, file_path, type, description): + """addFile(self, file_path, type, description) + + Add a file to a record instance + """ + if not os.path.exists(file_path): + raise ValueError, _("Incorrect path") + _name = os.path.basename(file_path) + _isin = False + for _ofile in self.__files: + if _ofile.name == _name: + _isin = True + if not _isin: + _file = File(_name, type, description) + self.__files.append(_file) + + def getLabels(self): + return self.__labels + + def setLabels(self, labels): + """setLabels(self, labels) + + Sets the labels list of a record + """ + if not isinstance(labels, list): + raise ValueError, _("labels must be a list") + _labels = [] + for _label in labels: + if isinstance(_label, str): + _labels.append(_label) + else: + raise ValueError, _("label must be a string") + self.__labels = _labels + + def addLabel(self, label): + """addLabel(self, label) + + Add a label to a record instance + """ + if not isinstance(label, str): + raise ValueError, _("Label must be a string") + if not label in self.__labels: + self.__labels.append(label) + + recordType = property(getRecordType, setRecordType, None, + """Record Type object + """) + code = property(getCode, setCode, None, + """Record code + """) + synonyms = property(getSynonyms, setSynonyms, None, + """List of codes synonyms of the code + """) + unit = property(getUnit,setUnit, None, + """Measure Unit of the record + """) + summary = property(getSummary, setSummary, None, + """Short description of the record + """) + prices = property(getPrices, None, None, + """List of Price/Date + """) + parents = property(getParents, setParents, None, + """List of codes of the records which the record is in + its decomposition + """) + children = property(getchildren, setchildren, None, + """List of Decompositon intances""") + text = property(getText, setText, None, + """Long description of the record""") + sheet = property(getSheet, setSheet, None, + """Sheet of conditions object""") + files = property(getFiles, setFiles, None, + """File list""") + labels = property(getLabels, setLabels, None, + """Label list""") + +class ParametricRecord(Record): + """base.ParametricRecord: + + Description: + Parametric Record object + Constructor: + base.ParametricRecord(code, synonyms, hierarchy, unit, summary, prices, + type, subtype, text="") + Ancestry: + +-- object + +-- Record + +-- ParametricRecord + Atributes: + + Methods: + + """ + + __slots__ = ["_ParametricRecord__budget", + "_ParametricRecord__code", "_ParametricRecord__synonyms", + "_ParametricRecord__hierarchy", "_ParametricRecord__unit", + "_ParametricRecord__summary", "_ParametricRecord__prices", + "_ParametricRecord__type", "_ParametricRecord__subtype", + "_ParametricRecord__parents", "_ParametricRecord__children", + "_ParametricRecord__text", "_ParametricRecord__sheet", + "_ParametricRecord__files", "_ParametricRecord__labels", + "_ParametricRecord__parameters", + "_ParametricRecord__select_comment", + "_ParametricRecord__vars", + "_ParametricRecord__parametric_summary", + "_ParametricRecord__parametric_text",] + + def __getstate__(self): + return (self.__budget, self.__code, self.__synonyms, self.__hierarchy, + self.__unit, self.__summary, self.__prices, self.__type, + self.__subtype, self.__parents, self.__children, self.__text, + self.__sheet, self.__files, self.__labels, self.__parameters, + self.__select_comment, self.__vars, + self.__parametric_summary, self.__parametric_text) + + def __setstate__(self, tuple): + self.__budget = tuple[0] + self.__code = tuple[1] + self.__synonyms = tuple[2] + self.__hierarchy = tuple[3] + self.__unit = tuple[4] + self.__summary = tuple[5] + self.__prices = tuple[6] + self.__type = tuple[7] + self.__subtype = tuple[8] + self.__parents = tuple[9] + self.__children = tuple[10] + self.__text = tuple[11] + self.__sheet = tuple[12] + self.__files = tuple[13] + self.__labels = tuple[14] + self.__parameters = tuple[15] + self.__select_comment = tuple[16] + self.__vars = tuple[17] + self.__parametric_summary = tuple[18] + self.__parametric_text = tuple[19] + + def __init__(self, budget, code, synonyms, hierarchy, unit, summary, + prices, type, subtype, parents=[], text=""): + Record.__init__(self, budget, code, synonyms, hierarchy, unit, summary, + prices, type, subtype, parents=[], text="") + self.__parameters = {} + self.__select_comment = "" + self.__vars = {} + self.parametric_summary = "" + self.parametric_text = "" + + def getParameter(self, parameter): + if parameter in self.__parameters: + return self.__parameters[parameter] + else: + return None + + def setParameter(self, parameter, parameter_list): + self.__parameters[parameter] = parameter_list + + def getSelectComment(self): + return self.__select_comment + + def setSelectComment(self, select_comment): + self.__select_comment = select_comment + def getVar(self, var): + if var in self.__vars: + return self.__vars[var] + else: + return None + + def setVar(self, var, var_list): + self.__vars[var] = var_list + + def getParametricSummary(self): + return self.__parametric_summary + + def setParametricSummary(self, parametric_summary): + self.__parametric_summary = parametric_summary + + def getParametricText(self): + return self.__parametric_text + + def setParametricText(self, parametric_text): + self.__parametric_text = parametric_text + + parameter = property(getParameter, setParameter, None, + """Record parameter + """) + select_comment = property(getSelectComment, setSelectComment, None, + """Seclect comment + """) + var = property(getVar, setVar, None, + """Record var + """) + parametric_summary = property(getParametricSummary, setParametricSummary, + None, + """Parametric summary + """) + parametric_text = property(getParametricText, setParametricText, None, + """Seclect comment + """) + +class Decomposition(object): + """base.Decomposition: + + Description: + Decomposition object + Constructor: + base.Decomposition(position, code, budgetMeasures, certification=None, + real_cost=None, cost_goals=None, cost_planned=None) + Ancestry: + +-- object + +-- Decomposition + Atributes: + "position": the position of the child record in the parent record + "code": Record code. + Measures: + "budgetMeasures": list of budget and Amended budget measures + "certification": list of certifications for months measures + "real_cost": list of real cost of construction for months measures + "cost_goals": list of cost goals of construction for months measures + "cost_planned": list of costs planned and amended cost planned measures + Methods: + __getstate__(self) + __setstate__(self, tuple) + __init__( position, code, budgetMeasures, certification=None, + real_cost=None, cost_goals=None, cost_planned=None) + {get/set}position + {get/set}Code + {get/set}BudgetMeasures + {get/set}Certification + {get/set}RealCost + {get/set}CostGoals + {get/set}CostPlanned + """ + __slots__ = ["_Decomposition__position", + "_Decomposition__code", + "_Decomposition__budgetMeasures", + "_Decomposition__certification", + "_Decomposition__real_cost", + "_Decomposition__cost_goals", + "_Decomposition__cost_planned", + ] + def __getstate__ (self): + return (self.__position, self.__code, self.__budgetMeasures, + self.__certification, self.__real_cost, self.__cost_goals, + self.__cost_planned) + def __setstate__(self,tuple): + self.__position = tuple[0] + self.__code = tuple[1] + self.__budgetMeasures = tuple[2] + self.__certification = tuple[3] + self.__real_cost = tuple[4] + self.__cost_goals = tuple[5] + self.__cost_planned = tuple[6] + + def __init__(self, position, code, budgetMeasures, certification=None, + real_cost=None, cost_goals=None, cost_planned=None): + self.position = position + self.code = code + self.budgetMeasures = budgetMeasures + self.certification = certification + self.real_cost = real_cost + self.cost_goals = cost_goals + self.cost_planned = cost_planned + def getPosition(self): + return self.__position + def setPosition(self, position): + if not isinstance(position, int): + raise ValueError, _("Position must be a integer") + self.__position = position + def getCode(self): + return self.__code + def setCode(self, code): + self.__code = code + def getBudgetMeasures(self): + return self.__budgetMeasures + def setBudgetMeasures(self, budgetMeasures): + if not isinstance(budgetMeasures, list): + raise ValueError, _("BudgetMeasures atribute must be a list") + for _measure in budgetMeasures: + if not isinstance(_measure, Measure): + raise ValueError, _("BudgetMeasures item must be a Measure "/ + "object") + self.__budgetMeasures = budgetMeasures + def getCertification(self): + return self.__certification + def setCertification(self, certification): + if not (certification is None or isinstance(certification, list)): + raise ValueError, _("Certification atribute must be a list or None") + self.__certification = certification + def getRealCost(self): + return self.__real_cost + def setRealCost(self, real_cost): + if not (real_cost is None or isinstance(real_cost, list)): + raise ValueError, _("Real cost atribute must be a list or None") + self.__real_cost = real_cost + def getCostGoals(self): + return self.__cost_goals + def setCostGoals(self, cost_goals): + if not (cost_goals is None or isinstance(cost_goals, list)): + raise ValueError, _("Cost goals atribute must be a list or None") + self.__cost_goals = cost_goals + def getCostPlanned(self): + return self.__cost_planned + def setCostPlanned(self, cost_planned): + if not (cost_planned is None or isinstance(cost_planned, list)): + raise ValueError, _("Cost Planned atribute must be a list or None") + self.__cost_planned = cost_planned + position = property(getPosition, setPosition, None, + """Postion of the record in the budget + """) + code = property(getCode, setCode, None, + """Record code + """) + budgetMeasures = property(getBudgetMeasures, setBudgetMeasures, None, + """list of budget and Amended budget measures + """) + certification = property(getCertification, setCertification,None, + """ list of certifications by months measures + """) + real_cost = property(getRealCost, setRealCost, None, + """ list of real cost of construction for months measures + """) + cost_goals = property(getCostGoals, setCostGoals, None, + """ list of cost goals of construction for months measures + """) + cost_planned = property(getCostPlanned, setCostPlanned, None, + """ list of costs planned and amended cost planned measures + """) + + +class Measure(object): + """base.Measure: + + Description: + Measure object + Constructor: + base.Measure(decimals, recordType, measure, lines, + label, factor, yield_) + Ancestry: + +-- object + +-- Measure + Atributes: + "measure": Total result of measure. + "lines": List of measure lines, List of LineM instances. + "label": Record Identifiers that are used by some measure programs. + "factor": + "yield": + "fixed": If fixed is True the yield is not calculated from measure + Methods: + __getstate__() + __setstate__(tuple) + __init__(decimals, recordType, measure, lines, + label, factor, yield_) + getMeasure() + setMeasure(measure, decimals) + {get/set}Lines + {get/set}Label + getFactor() + setFactor(factor, decimals, recordType) + getYield() + setYield(yield_, decimals, recordType) + getFixed() + setFixed(decimals) + buildMeasure(list_lines, type, decimals) + calculateMeasure(decimals) + updateYield(decimals) + """ + __slots__ = ["_Measure__measure", + "_Measure__lines", + "_Measure__label", + "_Measure__factor", + "_Measure__yield_", + "_Measure__fixed"] + def __getstate__ (self): + return (self.__measure, self.__lines, self.__label, + self.__factor, self.__yield_, self.__fixed) + def __setstate__(self,tuple): + self.__measure = tuple[0] + self.__lines = tuple[1] + self.__label = tuple[2] + self.__factor = tuple[3] + self.__yield_ = tuple[4] + self.__fixed = tuple[5] + def __init__(self, decimals, recordType, measure, lines, + label, factor, yield_): + self.setMeasure(measure, decimals) + self.lines = lines + self.label = label + self.setFactor(factor, decimals, recordType) + self.setYield(yield_, decimals, recordType) + self.__fixed = False + + def getMeasure(self): + return self.__measure + def setMeasure(self, measure, decimals): + if not isinstance(measure, float): + raise ValueError, utils.mapping(_("Measure must be a float "\ + "number. Type: $1"), (type(measure),)) + # TODO: test after + _DS = decimals.DS + measure = round(measure, _DS) + self.__measure = measure + + def getLines(self): + return self.__lines + def setLines(self, lines): + if not isinstance(lines, list): + raise ValueError, _("Lines must be a list") + for _line in lines: + if not isinstance(_line, MeasureLine): + raise ValueError, _("Line must be a MeasureLine objetc") + self.__lines = lines + def getLabel(self): + return self.__label + def setLabel(self, label): + self.__label = label + def setFactor(self, factor, decimals, recordType): + if not isinstance(factor, float): + raise ValueError, utils.mapping(_("Factor must be a float number "\ + "|$1|"), (factor,)) + # TODO: test after + _DF = decimals.getDF(recordType) + factor = round(factor, _DF) + self.__factor = factor + + def getFactor(self): + return self.__factor + + def setYield(self, yield_, decimals, recordType): + if not isinstance(yield_, float): + raise ValueError, _("Yield must be a float number") + # TODO: test after + _DR = decimals.getDR(recordType) + yield_ = round(yield_, _DR) + self.__yield_ = yield_ + + def getYield(self): + return self.__yield_ + + def setFixed(self, fixed, decimals): + if not isinstance(fixed, bool): + raise ValueError, _("Fixed must be boolean object") + self.__fixed = fixed + self.updateYield(decimals) + + def getFixed(self): + return self.__fixed + + measure = property(getMeasure, None, None, + """Total result of the measure + """) + lines = property(getLines, setLines, None, + """List of measure lines, List of "MeasureLine" instances + """) + label = property(getLabel, setLabel, None, + """Record identifiers that are used in some measure programs + """) + factor = property(getFactor, None, None, + """Factor + """) + yield_ = property(getYield, None, None, + """Yield of a record + """) + fixed = property(getFixed, setFixed,None, + """If fixed is True the yield is not calculated from measure + """) + + def buildMeasure(self, list_lines, type, decimals, recordType): + """setMeasure(self, list_lines, type, decimals) + + list_lines: list of measure lines + [ [linetype, comment, units, length, width, height, formula], ... ] + linetype: + #-#empty string -> Normal + 0 -> Normal + 1 -> Parcial Subtotal + 2 -> Accumulated Subtotal + 3 -> Formula + comment: comment string + units: Number of Units (a) + length: Length (b) + width: Width (c) + height: Height (d) + formula: Can be a formula or a empty string + Valid Operator: '(', ')', '+', '-', '*', '/' and '^' + Valid variable: 'a', 'b', 'c','d' and 'p' (Pi=3.1415926) + type: type of action + M: Set measure + A: Add measure + decimal: budget decimals object + + Sets the measurelines for a record + """ + # TODO: calcutate measure from lines + _parcial = 0 + _total = 0 + _lines = [] + for _line in list_lines: + _type, _comment = _line[0], _line[1] + _units, _length = _line[2], _line[3] + _width, _height = _line[4], _line[5] + _formula = _line[6] + _measure_line = MeasureLine(decimals, _type, _comment, _units, + _length, _width, _height, _formula) + _lines.append(_measure_line) + if type == "M": + self.lines = _lines + elif type == "A": + self.lines.extend(_lines) + else: + raise ValueError, utils.mapping(_("Type must be M or A. Type: $1"), + (type,)) + self.calculateMeasure(decimals, recordType) + + def calculateMeasure(self, decimals, recordType): + #TODO: round acumulated_subtotal and parcial_subtotal + if len(self.lines) > 0: + _acumulated_total = 0.0 + _parcial_total = 0.0 + for line in self.lines: + _parcial = line.parcial + _acumulated_total += _parcial + if line.lineType == 2: + line.setAcumulatedSubtotal(_acumulated_total, decimals) + elif line.lineType == 1: + _parcialSubtotal = _acumulated_total - _parcial_total + line.setParcialSubtotal(_parcialSubtotal, decimals) + _parcial_total = _acumulated_total + self.setMeasure(_acumulated_total, decimals) + _DR = decimals.getDR(recordType) + self.updateYield(decimals, recordType) + def updateYield(self, decimals, recordType): + if not self.fixed: + self.setYield(self.measure, decimals, recordType) + +class MeasureLine(object): + """base.MeasureLine: + + Description: + MeasureLine object + Constructor: + base.MeasureLine(budget, type, comment, units, length, width, height, + formula) + Ancestry: + +-- object + +-- MeasureLine + Atributes: + "lineType": Line type: + #-#empty string -> Normal + 0 -> Normal + 1 -> Parcial Subtotal + 2 -> Accumulated Subtotal + 3 -> Formula, the comment is a formula. + "comment": Descriptive text string + "units": Number of Units (a) + "length": length (b) + "width": Width (c) + "height": Height (d) + "formula": can be a valid formula or a empty string + Valid Operator: '(', ')', '+', '-', '*', '/' and '^' + Valid variable: 'a', 'b', 'c','d'y 'p' (Pi=3.1415926) + "partial" : result of measure line + "parcial_subtotal" + "acumulated_subtotal" + Methods: + __getstate__(self) + __setstate__(self, tuple) + __init__(self, decimals, type, comment, units, length, width, height, + formula) + {get/set}LineType + {get/set}Comment + {get/set}Units + {get/set}Length + {get/set}Width + {get/set}Height + {get/set}Formula + getParcial + {get/set}ParcialSubtotal + {get/set}AcumulatedSubtotal + calculateParcial + eval_formula + """ + __slots__ = ["_MeasureLine__lineType", + "_MeasureLine__comment", + "_MeasureLine__units", + "_MeasureLine__length", + "_MeasureLine__width", + "_MeasureLine__height", + "_MeasureLine__formula", + "_MeasureLine__parcial", + "_MeasureLine__parcial_subtotal", + "_MeasureLine__acumulated_subtotal", + ] + def __getstate__ (self): + return (self.__lineType, self.__comment, self.__units, + self.__length, self.__width, self.__height, self.__formula, + self.__parcial) + def __setstate__(self,tuple): + self.__lineType = tuple[0] + self.__comment = tuple[1] + self.__units = tuple[2] + self.__length = tuple[3] + self.__width = tuple[4] + self.__height = tuple[5] + self.__formula = tuple[6] + self.__parcial = tuple[7] + #self.calculateParcial() + def __init__(self, decimals, type, comment, units, length, width, height, + formula): + self.__parcial = 0.0 + self.__parcial_subtotal = 0.0 + self.__acumulated_subtotal = 0.0 + self.lineType = type + self.comment = comment + self.setUnits(units, decimals) + self.setLength(length, decimals) + self.setWidth(width, decimals) + self.setHeight(height, decimals) + self.setFormula(formula, decimals) + #self.calculateParcial() + def getLineType(self): + return self.__lineType + def getComment(self): + return self.__comment + def getUnits(self): + return self.__units + def getLength(self): + return self.__length + def getWidth(self): + return self.__width + def getHeight(self): + return self.__height + def getFormula(self): + return self.__formula + def getParcial(self): + return self.__parcial + def getParcialSubtotal(self): + return self.__parcial_subtotal + def getAcumulatedSubtotal(self): + return self.__acumulated_subtotal + def setParcialSubtotal(self, parcial_subtotal, decimals): + if not isinstance(parcial_subtotal, float): + raise ValueError, utils.mapping(_(" Parcial Subtotal must be a "\ + "float number. Parcial: $1"), (str(parcial_subtotal),)) + _DS = decimals.DS + parcial_subtotal = round(parcial_subtotal, _DS) + self.__parcial_subtotal = parcial_subtotal + def setAcumulatedSubtotal(self, acumulated_subtotal, decimals): + if not isinstance(acumulated_subtotal, float): + raise ValueError, utils.mapping(_(" Acumulated Subtotal must be "\ + "a float number. Parcial: $1"), + (str(acumulated_subtotal),)) + _DS = decimals.DS + acumulated_subtotal = round(acumulated_subtotal, _DS) + self.__acumulated_subtotal = acumulated_subtotal + def calculateParcial(self, decimals): + _DS = decimals.DS + if self.lineType == 1 or self.lineType == 2: + _parcial = 0.0 + elif self.lineType == 0: # self.formula == "": + if isinstance(self.units, float): + _a = self.units + else: + _a = 0.0 + if isinstance(self.length, float): + _b = self.length + else: + _b = 1.0 + if isinstance(self.width, float): + _c = self.width + else: + _c = 1.0 + if isinstance(self.height, float): + _d = self.height + else: + _d = 1.0 + _parcial = _a * _b * _c * _d + else: + _parcial = self.eval_formula() + _parcial = round(_parcial, _DS) + self.__parcial = _parcial + + def setLineType(self, type): + if not type in [0, 1, 2, 3]: + raise ValueError, utils.mapping(_("Invalid measure line type ($1)"), + (str(type),)) + self.__lineType = type + def setComment(self, comment): + if not isinstance(comment, str): + raise ValueError, utils.mapping(_("Measure Comment must be a "\ + "string ($1)"), (str(comment),)) + self.__comment = comment + def setUnits(self, units, decimals): + if units != "": + if not isinstance(units, float): + raise ValueError, utils.mapping(_("Invalid Measure Units ($1)"), + (str(units),)) + _DN = decimals.DN + units = round(units, _DN) + self.__units = units + try: + self.calculateParcial(decimals) + except AttributeError: + pass + def setLength(self, length, decimals): + if length != "": + if not isinstance(length, float): + raise ValueError, utils.mapping(_("Invalid Measure length ($1)"), + (str(units),)) + _DD = decimals.DD + length = round(length, _DD) + self.__length = length + try: + self.calculateParcial(decimals) + except AttributeError: + pass + def setWidth(self, width, decimals): + if width != "": + if not isinstance(width, float): + raise ValueError, utils.mapping(_("Invalid Measure Width ($1)"), + (str(units),)) + _DD = decimals.DD + width = round(width, _DD) + self.__width = width + try: + self.calculateParcial(decimals) + except AttributeError: + pass + def setHeight(self, height, decimals): + if height != "": + if not isinstance(height, float): + raise ValueError, utils.mapping(_("Invalid Measure Height ($1)"), + (str(height),)) + _DD = decimals.DD + height = round(height, _DD) + self.__height = height + try: + self.calculateParcial(decimals) + except AttributeError: + pass + def setFormula(self, formula, decimals): + if not isinstance(formula, str): + raise ValueError, utils.mapping(_("Formula must be a "\ + "string ($1)"), (str(formula),)) + if re.match(".*[^0123456789\.()\+\-\*/\^abcdp ].*", formula): + raise ValueError, utils.mapping(_("There is invalid characters"\ + "in formula ($1)"), (str(formula),)) + self.__formula = formula + try: + self.calculateParcial(decimals) + except AttributeError: + pass + + lineType = property(getLineType, setLineType, None, + """Type of measure line + """) + comment = property(getComment, setComment, None, + """Text + """) + units = property(getUnits, None, None, + """Number of units + """) + length = property(getLength, None, None, + """Length measure + """) + width = property(getWidth, None, None, + """Width measure + """) + height = property(getHeight, None, None, + """Height measure + """) + formula = property(getFormula, None, None, + """Formula + """) + parcial = property(getParcial, None, None, + """result of measure line + """) + acumulated_subtotal = property(getAcumulatedSubtotal, + None, None, + """Acumulated subtotal + """) + parcial_subtotal = property(getParcialSubtotal, + None, None, + """Parcial subtotal + """) + def eval_formula(self): + """eval_formula() + + formula: + Valid Operator: '(', ')', '+', '-', '*', '/' and '^' + Valid variable: 'a', 'b', 'c','d'y 'p' (Pi=3.1415926) + units: Number of Units (a) + length: Length (b) + width: Width (c) + height: Height (d) + + Evals the formula and return the result + """ + formula = self.formula + a = self.units + b = self.length + c = self.width + d = self.height + if a == "": a = 0.0 + if b == "": b = 0.0 + if c == "": c = 0.0 + if d == "": d = 0.0 + try: + a = float(a) + except: + raise ValueError, _("'a' value must be a float number") + try: + b = float(b) + except: + raise ValueError, _("'b' value must be a float number") + try: + c = float(c) + except: + raise ValueError, _("'c' value must be a float number") + try: + d = float(d) + except: + raise ValueError, _("'d' value must be a float number") + # spaces are erased + formula.replace(" ","") + # operators and varibles are replaced + formula = formula.replace("+", " + ") + formula = formula.replace("-", " - ") + formula = formula.replace("*", " * ") + formula = formula.replace("/", " / ") + formula = formula.replace("^", " ** ") + formula = formula.replace("(", " ( ") + formula = formula.replace(")", " ) ") + formula = formula.replace("a", str(a)) + formula = formula.replace("b", str(b)) + formula = formula.replace("c", str(c)) + formula = formula.replace("d", str(d)) + formula = formula.replace("p", "3.1415926") + _list_formula = formula.split(" ") + _formula2 = "" + for oper in _list_formula: + try: + _float_oper= str(float(oper)) + _formula2 = _formula2 + _float_oper + except ValueError: + _formula2 = _formula2 + oper + _g ={"__builtins__":{}} + try: + return eval(_formula2, _g) + except: + raise ValueError, _("Invalid formula") + +class Decimals(object): + """base.Decimals: + + Description: + Decimals object + Constructor: + base.Decimals(DN=2, DD=2, DSP=2, DS=2, + DFC=3, DFPU=3, DFUO=3, DFA=3, + DRP=3, DRC=3, DRUO=3, DRA=3, + DP=2, DC=2, DPU=2, DUO=2, DEA=2, DES=2, + DIR=2, DIM=2, DIRC=2, DIMC=2, DCD=2, + DIVISA="EUR") + Ancestry: + +-- object + +-- Decimals + Atributes: + "DN": Number of decimal places of the field "equal-size parts" in the + measure lines. + Default: 2 decimal places. + "DD": Number of decimal places of the three dimensions in the + measure lines. + Default: 2 decimal places. + "DSP": Number of decimal places of the subtotal of a measure. + Default: 2 decimal places. + "DS": Number of decimal places of the total sum of a measure. + Default: 2 decimal places. + "DFC": Number of decimal places of the yield factor in a decomposition + of a chapter or subchapter. + Dafault: 3 decimal places. + "DFPU": Number of decimal places of the yield factor in a decomposition + of a unitary budget. + Default: 3 decimal places. + "DFUO": Number of decimal places of the yield factor in a decomposition + of a unit of work. + Default: 3 decimal places. + "DFA": Number of decimal places of the yield factor in a decomposition + of a Auxiliary element. + Default: 3 decimal places. + "DRC": Number of decimal places of the yield in a + decomposition of a chapter or subchapter. + Number of decimal places of the result of the multiplictaion of + the yield (or measure) and the factor in a decomposition of a + chapter or subcharter. + Default: 3 decimal places. + "DRPU": Number of decimal places of the yield in a decomposition + of a unitary budget record. + Number of decumal places of the result of the multiplication of + the factor and the yield in a decompositon of a untitary budget. + Default: 3 decimal places. + "DRUO": Number of decimal places of the yield in a decomposition of a + unit of work. + Decimal places of the result of the multiplication of the yield + and the factor in a descomposition of a unit of work. + Default: 3 decimal places. + "DRA": Number of decimal places of the yield in a decompositon of a + auxiliar element. + Number of decimal places of the result of the multiplication of + the yield and the factor in a descomposition of a auxilar element.Decimales + Default: 3 decimal places. + "DP": Number of decimal places of the price of a budget. + Default: 2 decimal places. + "DC": Number of decimal places of the price of a chapter or subchapter. + Default: 2 decimal places. + "DPU": Number of decimal places of the price of a unitary budget. + Default: 2 decimal places. + "DUO": Number of decimal places of the price of a unit of work. + Default: 2 decimal places. + "DEA": Number of decimal places of the price of a auxiliar element. + Default: 2 decimal places. + "DES": Number of decimal places of the price of the simple elements. + Default: 2 decimal places. + "DIR": Number of decimal places of the resulting amount to multiply + the total yield and the price of the elements of a unit of work or + a auxiliar element. + "DIRC": Number of decimal places of the resulting amount to multiply + the total yield and the price of the elements of a budget, chapter + or a subchapter. + "DCD": Number of decimal places ot the resulting amount to sum the + direct costs of a unit of work (and auxiliar element). + Number of decimal places of the indirect costs. + Default: 2 decimal places. + "DIVISA": monetary unit. + Methods: + __init__(DN=2, DD=2, DSP=2, DS=2, + DFC=3, DFPU=3, DFUO=3, DFA=3, + DRC=3, DRPU=3, DRUO=3, DRA=3, + DP=2, DC=2, DPU=2, DUO=2, DEA=2, DES=2, + DIR=2, DIRC=2, DCD=2, + DIVISA="EUR") + __getitem__(key) + haskey(key) + getD(recordtype) + getDF(recordType) + getDR(recordType) + getDI(recordType) + """ + # TODO: get/set methods + def __init__(self, + DN=2, DD=2, DSP=2, DS=2, + DFC=3, DFPU=3, DFUO=3, DFA=3, + DRC=3, DRPU=3, DRUO=3, DRA=3, + DP=2, DC=2, DPU=2, DUO=2, DEA=2, DES=2, + DIR=2, DIRC=2, DCD=2, + DIVISA="EUR" ): + self.DN = DN + self.DD = DD + self.DSP = DSP + self.DS = DS + self.DFP = 3 + self.DFC = DFC + self.DFPU = DFPU + self.DFUO = DFUO + self.DFA = DFA + self.DRP = 3 + self.DRC = DRC + self.DRPU = DRPU + self.DRUO = DRUO + self.DRA = DRA + self.DP = DP + self.DC = DC + self.DPU = DPU + self.DUO = DUO + self.DEA = DEA + self.DES = DES + self.DIR = DIR + self.DIRC = DIRC + self.DCD = DCD + self.DIVISA = DIVISA + def __getitem__(self, key): + return self.__dict__[key] + def haskey(self, key): + return key in self.__dict__ + def getD(self, recordType): + # DP: budget. + # DC: chapter and subcharter. + # DUO: unit. + # DEA: auxiliar element. + # DES: simple element. + _hierarchy = recordType.hierarchy + if _hierarchy == 0: #budget, type 0, subtipe "OB" + _decimal = self.DP + elif _hierarchy == 1: #chapter/subcharter, type 0, subtipe "" + _decimal = self.DC + else: # other + _type = recordType.type + _subtype = recordType.subtype + if _subtype == "EA": # auxiliar element type 0 subitype "EA" + _decimal = self.DEA + if _subtype == "PU": # unitary budget type 0 subitype "PU" + _decimal = self.DPU + elif (_type in [1, 2, 3] or + _subtype in ["H", "Q", "%", "MC", "MCr", "MM", "MS", "ME", + "MCu", "Mal","ML", "M"] + ): # simple element + _decimal = self.DES + else: # unit type 0, subtipe ["EU", "EC", "EF", "PA"] + _decimal = self.DUO + return _decimal + def getDF(self, recordType): + # Factor: DF + # ->DFP: Budget + # ->DFC: Chapter/Subchapter + # ->DFUO: Unit + # ->DFA: Auxiliar + # ->DFPU: Unitary budget + if recordType.hierarchy == 0: #budget + _decimal = self.DFP + elif recordType.hierarchy == 1: #chapter/subcharter + _decimal = self.DFC + else: # other + if recordType.subtype == "EA": # auxiliar element + _decimal = self.DFA + if recordType.subtype == "PU": # unitary budget element + _decimal = self.DFPU + else: # unit EU EC EF PA + _decimal = self.DFUO + return _decimal + def getDR(self, recordType): + # Yield: DR + # ->DRP: Budget + # ->DRC: Chapter/Subchapter + # ->DRUO: Unit + # ->DRA: Auxiliar + # ->DRPU: Unitary budget + if recordType.hierarchy == 0: #budget + _decimal = self.DRP + elif recordType.hierarchy == 1: #chapter/subcharter + _decimal = self.DRC + else: # other + if recordType.subtype == "EA": # auxiliar element + _decimal = self.DRA + if recordType.subtype == "PU": # unitary budget element + _decimal = self.DRPU + else: # unit + _decimal = self.DRUO + return _decimal + def getDI(self, recordType): + # DIRC: budget, chapter and subcharter. + # DIR: unit, auxiliar element. + _hierarchy = recordType.hierarchy + _subtype = recordType.subtype + if _hierarchy == 0 or _hierarchy == 1 or _subtype == "PU": + #budget, type 0, subtipe "OB" + #chapter/subcharter, type 0, subtipe "" + #unitary budget, type 2, subtype "PU" + _decimal = self.DIRC + else: # other + # auxiliar element type 0 subitype "EA" + # unit type 0, subtipe ["EU", "EC", "EF", "PA", "PU"] + _decimal = self.DIR + return _decimal + +class Sheet(object): + """base.Sheet: + Description: + Sheet of conditions object + Constructor: + base.Sheet(sheet_dict) + Ancestry: + +-- object + +-- Sheet + Atributes: + "sheet_dict": { <Field key> : { <Section key> : <Paragraph key>} + <Field key>: must be in Budget.SheetFields + <Section key>: must be in Budget.SheetSections + <Paragraph key>: must be in Budget.SheetParagraph + Methods: + __getstate__(self) + __setstate__(self, tuple) + __init__(self, sheet_dict={}) + {get/set}Sheet_dict + getFields + getSections + getParagraph + addField + addSection + """ + __slots__ = ["_Sheet__sheet_dict"] + def __getstate__ (self): + return (self.__sheet_dict,) + def __setstate__(self,tuple): + self.__sheet_dict = tuple[0] + def __init__(self): + self.__sheet_dict = {} + def getSheet_dict(self): + return self.__sheet_dict + def setSheet_dict(self, sheet_dict): + if not isinstance(sheet_dict, dict): + raise ValueError, _("sheet_dict must be a dictionay") + self.__sheet_dict = sheet_dict + def getFields(self): + return self.sheet_dict.keys() + def getSections(self, field): + if field in self.__sheet_dict: + return self.__sheet_dict[field].keys() + else: + return None + def getParagraph(self, field, section): + if (field in self.__sheet_dict and + section in self.__sheet_dict[field]): + return self.__sheet_dict[field][section] + else: + return None + def addField(self, field, section_dict): + if not isinstance(field, str): + raise ValueError, _("sheet field must be a string") + if not isinstance(section_dict, dict): + raise ValueError, _("section_dict must be a dictionary") + self.__sheet_dict[field] = section_dict + def addSection(self, field, section, paragraph): + if not isinstance(field, str): + raise ValueError, _("sheet field must be a string") + if not isinstance(section, str): + raise ValueError, _("sheet section must be a string") + if not isinstance(paragraph, str): + raise ValueError, _("sheet paragraph must be a string") + if not field in self.__sheet_dict: + self.addField(field, { }) + _field = self.__sheet_dict[field] + _field[section] = paragraph + sheet_dict = property(getSheet_dict, setSheet_dict, None, + """Sheet dictionary { <Field key> : { <Section key> : <Paragraph key>}""") + +class Budget(object): + """base.Budget: + + Description: + Budget objetc + Constructor: + base.Budget() + Ancestry: + +-- object + +-- Budget + Atributes: + "filename": file name of the budget file (FIEBDC) + "__records": Dictionary with the budget records. + { "code" : Record object, } + "__synonyms": Dictionary with the records synonums. + { "code" : ["synonym",],} + Each record code can have synonym codes. + "__root": The root record code. + "__title_list": List with the Headers and list of Titles for prices and + decimal places. + [ "Header", ["Title1", "Title2", ... ] ] + The records can have diferent prices for diferent ages, geografical + places, ... + The Headers is the type of hierarchy of the prices + Each Title have a group of Prices and a Decimals definition + "__decimals": List with the decimal places used to round the + result of the calculations with prices and measures + The values are Decimals objects + The <0> objets is the default Decimals (seted in FIEBDC-3), + The others keys are for the diferent groups of Prices + "__percentages": Dictionary with the percentages + keys: + "CI" Indirect Cost + "GG" General expenses + "BI" Industrial benefit + "BAJA" Low (what this do here?) + "IVA" Tax + "__file_owner" + "__comment" + "__date" + "__budgetType" A integer. Type of data in budget + 0 -> Undefined + 1 -> Base data. + 2 -> Budget. + 3 -> Certificate. + 4 -> Base date update. + "__budgetCerficateOrder" Only valid if budgetType is 3. + "__budgetCerficateDate" Only valid if budgetType is 3 + "__title_index": A integer. The active group of Prices and Decimals. + "__sheet_sections": Dictionary whith de sheet sections + "__sheet_fields": Dictionary whith sheet fields + "__sheet_paragraphs": Dictionary whith sheet paragraphs + "__companys": Dictionary whith companys object + { company_code: company_object } + "__tec_info": Dictionary whith tecnical information + {ti_code : ["desciption text", "unit"]} + "__labels": Label dictionary { "label": [ "code", ], } + Methods: + iter + iterPreOrder + iterPostOrder + getRoot(self) + hasPath(self, path) + getchildren(self, code) + setOwner(self, owner) + setDate(self, date) + setComment(self, comment) + setBudgetType(self, type) + setCertificateOrder(self, order) + setCertificateDate(self, date) + setTitleList(self, title) + getTitleList(self) + getActiveTitle(self) + setDecimals(self, dictionary) + getDecimals(self, decimal="All", N=None) + setPercentages(self, dictionary) + getPercentages(self, percentage="All") + getAllParents(self, code) + getAllchildren(self, code) + getNDecomposition(self, code, N) + getDecomposition(self,code) + getMeasure(self, path) + getStrYield + getStrFactor + setTree(sef, code, child_code, position, factor, yield_, total, + list_lines, label, type) + eval_formula(self, formula, a, b, c, d) + getText(self, code) + setText(self, code, text) + setRecord(self, code, synonyms, hierarchy, unit, sumary, ... + hasRecord(self, code) + getRecord + addPriceToRecord + getStrPriceFromRecord + getCode(self, path) + getAmount + getStrAmount + setSheetSection(self, sheet_code, sheet_title) + hasSheetSection(self, section) + setSheetSections(self,dictionary) + setSheetField(self, field_code, field_title) + hasSheetField(self, field) + getSheetField(self, field) + setSheetFields(self, field_dict) + setSheetParagraph(self, paragraph_code, paragraph_text) + hasSheetParagraph(self, paragraph) + getSheetParagraph(self, paragraph) + setSheetParagraphs(self, paragraph_dict) + setSheetRecord(self, record_code,field, section_dict) + addFile(self, record_code, filename) + setCompany(self, code, summary, name, offices, cif, web, email ) + getCompany + getCompanyKeys + addTecInfo(self, ti_code, text, unit) + hasTecInfo(self, ti_code) + getTecInfo(self, ti_code) + setTecnicalInformation(self, _record_code, _ti_dict) + changeCode(self, record_code, new_rocord_code) + addLabel + setParametricSelectComment + setParametricSummary + setParametricText + """ + + + def __init__(self): + """__init__(self) + + Initialize the budget atributes + """ + self.__title_index = 0 + self.__decimals = [Decimals(), Decimals()] + self.__percentages = { "CI" : "" ,"GG": "", "BI": "", + "BAJA": "", "IVA" : ""} + self.__title_list = [ "", [ ] ] + self.__root = None + self.__file_owner = "" + self.__comment = "" + self.__budgetCerficateOrder = None + self.__budgetCerficateDate = None + self.__date = (0,0,0) + self.__budgetType = 0 + self.__records = { } + self.__synonyms = { } + self.__sheet_sections = { } + self.__sheet_fields = { } + self.__sheet_paragraphs = { } + self.__companys = { } + self.__tec_info = { } + self.__labels = { } + + def __getstate__(self): + return (self.__title_index, self.__decimals, self.__percentages, + self.__title_list, self.__root, self.__file_owner, + self.__records, self.__synonyms, self.__sheet_sections, + self.__sheet_fields, self.__sheet_paragraphs,self.__companys, + self.__tec_info, self.__labels) + + def __setstate__(self, tuple): + self.__title_index = tuple[0] + self.__decimals = tuple[1] + self.__percentages = tuple[3] + self.__title_list = tuple[4] + self.__root = tuple[4] + self.__file_owner = tuple[5] + self.__records = tuple[6] + self.__synonyms = tuple[7] + self.__sheet_sections = tuple[8] + self.__sheet_fields = tuple[9] + self.__sheet_paragraphs = tuple[10] + self.__companys = tuple[11] + self.__tec_info = tuple[12] + self.__labels = tuple[13] + + def iter(self): + for record in self.__records: + yield record + def iterPreOrder(self, recordCode, codes=[]): + _children = self.getchildren(recordCode) + for _child in _children: + if not _child in codes: + codes.append(_child) + self.iterPreOrder(_child, codes) + return codes + def iterPostOrder(self, recordCode, codes=[]): + _children = self.getchildren(recordCode) + for _child in _children: + if not _child in codes: + self.iterPreOrder(_child, codes) + codes.append(_child) + return codes + + def getRoot(self): + """getRoot(self) + + Returns the root record code + """ + return self.__root + + def hasPath(self, path): + """hasPath(self, path) + + path: The path of the record in the budget, It is a tuple. + Tests if the path is valid in the budget + """ + try: + self.getCode(path) + return True + except ValueError: + return False + + def getchildren(self, code): + """getchildren(self, code) + + code: a record code. + Return a list whith the child codes of a record + """ + _record = self.__records[code] + _children = _record.children + _child_code = [ _child.code for _child in _children ] + return _child_code + def setOwner(self, owner): + """setOwner(self, owner) + + owner: data owner + Set the data owner. + """ + if isinstance(owner, basestring): + self.__file_owner = owner + else: + raise TypeError, _("Owner must be a string") + + def setDate(self, date): + """setOwner(self, date) + + date (_y, _m, _d) + Set the date when de file was generated + """ + if isinstance(date, tuple) and len(date) == 3 and \ + isinstance(date[0], int) and isinstance(date[1], int) and \ + isinstance(date[2], int) and date[1] in range(13) and \ + date[2] in range(32): + if date[1] != 0 and date[2] != 0: + datetime.date(*date) + self.__date = date + else: + raise TypeError, _("Invalid Date: %s" % str(date)) + + def setComment(self, comment): + """setOwner(self, comment) + + comment: text to comment the budged + Set the comment. + """ + if isinstance(comment, basestring): + self.__comment = comment + else: + raise TypeError, _("Comment must be a string") + + def setBudgeType(self, budget_type): + """setOwner(self, budget_type) + + budget_type: type of data in budget + 0 -> Undefined + 1 -> Base data. + 2 -> Budget. + 3 -> Budget certificate. + 4 -> Base date update. + Set the budget type. + """ + if budget_type in [1, 2, 3, 4]: + self.__budgetType = budget_type + else: + raise ValueError, _("Budget type must be 1, 2, 3 or 4.") + + def setCertificateOrder(self, certificate_order, certificate_date): + """setOwner(self, budget_type) + + certificate_order: certificate number + certificate_date: certificate date + Set the certificate order and date. + """ + if isinstance(certificate_order, int): + self.__budgetCerficateOrder = certificate_order + else: + raise ValueError, _("Certificate order must be a integer.") + + def setCertificateDater(self, certificate_date): + """setCertidicateDate(self, certificate_date) + + Set the certificate date. + """ + if isinstance(certificate_date, tuple) and \ + len(certificate_date) == 3 and \ + isinstance(certificate_date[0], int) and \ + isinstance(certificate_date[1], int) and \ + isinstance(certificate_date[2], int): + datetime.date(*certificate_date) + self.__budgetCerficateDate = certificate_date + else: + raise ValueError, _("Budget certificate Date must be a valid Date.") + + def setTitleList(self, title_list): + """setTitleList(self, title_list) + + title_list: [ "Header", ["Title1", "Title2", ... ] ] + Set the header and titles for the price groups and decimals. + """ + title_list[0] = str(title_list[0]) + if isinstance(title_list, list) and isinstance(title_list[1], list): + for i in range(len(title_list[1])): + title_list[1][i] = str(title_list[1][i]) + self.__title_list = title_list + else: + raise TypeError, _("Invalid title list format") + + def getTitleList(self): + """ getTitleList(self) + + Returns the header and titles for the price groups and decimals. + """ + return self.__title_list + + def getActiveTitle(self): + """getActiveTitle(self) + + Returns the active Title of price group + """ + return self.__title_index + + def setDecimals(self, dictionary, N): + """setDecimals(self, dictionary, N) + + dictionay: the decimal dictionary + N: the price group + Sets the Decimals for a price group. + """ + if N == -1 or N == len(self.__decimals): + _default_decimals = self.__decimals[0] + self.__decimals.append(_default_decimals) + elif N < len(self.__decimals): + _default_decimals = self.__decimals[N] + else: + raise IndexError, _("Invalid Index Title") + for _decimal in dictionary: + if dictionary[_decimal] == "": + dictionary[_decimal] = eval("_default_decimals." + _decimal) + decimals = Decimals(dictionary["DN"], dictionary["DD"], + dictionary["DSP"], dictionary["DS"], + dictionary["DFC"], + dictionary["DFPU"], dictionary["DFUO"], + dictionary["DFA"], dictionary["DRC"], + dictionary["DRPU"], dictionary["DRUO"], + dictionary["DRA"], dictionary["DP"], + dictionary["DC"], dictionary["DPU"], + dictionary["DUO"], dictionary["DEA"], + dictionary["DES"], dictionary["DIR"], + dictionary["DIRC"], dictionary["DCD"], + dictionary["DIVISA"]) + self.__decimals[N] = decimals + def getDecimals(self, decimal="All", N=None): + """getDecimals(self,decimal="All",N=None) + + decimal: + "All": Return a Decimals objet for a price group + "keys": Return the keys of a Decimal object + key: Return a Decimal value for a price group + N: the price group None,1,2,.. + None: Return the active price group + """ + if N is None: N = self.getActiveTitle() + if decimal == "All": + return self.__decimals[N+1] + elif decimal == "keys": + return self.__decimals[N+1].keys + elif self.__decimals[N+1].haskey(decimal): + return self.__decimals[N+1][decimal] + else: + raise KeyError, _("Decimal Key error") + + def setPercentages(self, dictionary): + """setPercentages(self, dictionary): + + dictionary: the percentage dictionary + Sets the percentage dictionary. + """ + _default_percentages = self.__percentages + for percentage in dictionary: + if dictionary[percentage] == 0: + dictionary[percentage] = "" + elif dictionary[percentage] == "": + dictionary[percentage] = _default_percentages[percentage] + _percentages = { "CI": dictionary["CI"], + "GG": dictionary["GG"], + "BI": dictionary["BI"], + "BAJA": dictionary["BAJA"], + "IVA" : dictionary["IVA"]} + self.__percentages = _percentages + + def getPercentages(self, key="All"): + """getPercentages(self, percentage="All") + + key: + "All": Return the Percentages dictionary + "keys": Return the keys of a Percentages object + key: Return a Percentages value for the key + """ + if key == "All": + return self.__percentages + elif key == "keys": + return self.__percentages.keys + elif key in self.__percentages: + return self.__percentages[key] + else: + raise KeyError, _("Invalid Percentage key") + + def getAllParents(self,code): + """getAllParents(self,code) + + code: a record code. + Returns a list with all the parents of a record + All record which the record is in its descomposition list, + including the parents of the parents + """ + if code in self.__records: + _parents = self.__records[code].parents + if len(_parents) == 0: return [ ] + for _antecesor in _parents[:]: + _parents = _parents + self.getAllParents(_antecesor) + return _parents + else: + return [ ] + + def getAllchildren(self,code): + """getAllchildren(self,code + + code: a record code. + Returns a list with all the children of a record, including + the children of the children + """ + if code in self.__records: + _children = self.__records[code].children + _children = [ _child.code for _child in _children ] + for _child in _children[:]: + _children = _children + self.getAllchildren(_child) + return _children + else: + return [ ] + + def getNDecomposition(self, code, N): + """getDecomposition(self,path) + + path: the path for a record + Returns the Decomposition object of a record + """ + _record = self.getRecord(code) + _decomposition_list = _record.children + _decomposition = _decomposition_list[N] + return _decomposition + + def getDecomposition(self, path): + """getDecomposition(self,path) + + path: the path for a record + Returns the Decomposition object of a record + """ + if path == (0,): + _type = self.getRecord(self.__root).recordType + return Decomposition( 0, self.__root, + [Measure(self.getDecimals(), _type, + 0.0, [], "", 1.0, 1.0)]) + else: + return self.getNDecomposition(self.getCode(path[:-1]), path[-1]) + + def getMeasure(self, path): + """getMeasure(self, path) + + path: the path for a record + Return the measute object of a record + """ + _decomposition = self.getDecomposition(path) + _measure = _decomposition.budgetMeasures[0] + return _measure + + def getStrYield(self, measure, recordType): + #_DR = measure.getDR(self.getDecimals()) + _DR = self.getDecimals().getDR(recordType) + _yield = ("%." + str(_DR) + "f" ) % measure.yield_ + return _yield + + def getStrFactor(self, measure, recorType): + _DF = self.getDecimals().getDF(recordType) + #_DF = measure.getDF(self.getDecimals()) + _factor = ("%." + str(_DF) + "f" ) % measure.factor + return _factor + + def setTree(self, code, child_code, position, factor, yield_, total, + list_lines, label, type): + """setTree(self, code, child_code, position, factor,yield_, total, + list_lines, label, type) + + code: the parent record code + child_code: child record code + position: position of child record in record parent record + decomposition. Position == -1 -> new child + factor: + yield_: + total: total measure (float) + list_lines: list of measure lines + [ [linetype, comment, units, length, width, height], ... ] + linetype: + empty string -> Normal + 1 -> Parcial Subtotal + 2 -> Accumulated Subtotal + 3 -> Formula, the comment is a formula. + comment: Can be a descriptive text or a formula + Valid Operator: '(', ')', '+', '-', '*', '/' and '^' + Valid variable: 'a', 'b', 'c','d'y 'p' (Pi=3.1415926) + units: Number of Units (a) + length: Length (b) + width: Width (c) + height: Height (d) + label: Record Identifiers that are used by some measure programs. + type: type of action + M: Set measure + A: Add measure + Sets the decomposition of a record in a child record + """ + if not utils.is_valid_code(code)[0]: + raise ValueError, utils.mapping(_("Invalid parent code: $1"), + (code,)) + if not utils.is_valid_code(child_code)[0]: + raise ValueError, utils.mapping(_("Invalid child code: $1"), + (code,)) + if not isinstance(position, int): + raise ValueError, utils.mapping(_("Invalid position in measure "\ + "$1, in code $2"), (parent_code, position)) + # Test circular references + _all_parent_list = self.getAllParents(code) + [ code ] + _all_child_list = self.getAllchildren(child_code) + [ child_code ] + for _parent_code in _all_parent_list: + if _parent_code in _all_child_list: + # TODO: change return to except + print utils.mapping(_("Circular Decomposition, parent code: "\ + "$1, child code: $2, repeated code: $3"), + (code, child_code, _parent_code)) + return + # Creating reference to parent code in child record + if child_code in self.__records: + _child_record = self.__records[child_code] + else: + _child_record = self.setRecord(child_code, [], -1, "", "", [], [], + "", "") + if code in self.__records: + code = self.__records[code].code + _child_record.appendParent(code) + child_code = self.__records[child_code].code + if code in self.__records: + # if the code exits retake previous values. + _record = self.__records[code] + _child_number = len(_record.children) + if position == -1: + position = _child_number + if position == _child_number: + # The record do not have the child + if not isinstance(factor, float): factor = 1.0 + if not isinstance(yield_, float): yield_ = 1.0 + if not isinstance(total, float): total = 0.0 + if not isinstance(list_lines, list): list_lines = [] + _child = _record.appendChild(child_code, self.getDecimals(), + factor, yield_, total, list_lines, type, label) + elif position < _child_number: + # The record have the child + _child = _record.children[position] + if child_code != "" and child_code != _child.code: + _child.code = child_code + if factor != "" : + if not isinstance(factor, float): + factor == 1.0 + _child.budgetMeasures[0].setFactor(factor, + self.getDecimals(), _record.recordType) + if yield_ != "": + if not isinstance(yield_, float): + yield_ = 1.0 + _child.budgetMeasures[0].setYield(yield_, + self.getDecimals(), _record.recordType) + _measure = _child.budgetMeasures[0] + if total != "": + if not isinstance(total, float): + yield_ = 0.0 + _measure.setMeasure(total, self.getDecimals()) + if isinstance(list_lines, list) and len(list_lines) > 0: + _measure.buildMeasure(list_lines, type, self.getDecimals(), + _record.recordType) + if isinstance(label, str) and label != "" : + _measure.label = label + else: + # TODO: change return for except + print utils.mapping(_("Error: Invalid child position in " + "decomposition. Parent code: $1 Child code: $2 "\ + "Position: $3"), (code, child_code, position)) + return + else: + if child_code == "" : + print utils.mapping(_("Error: Empty child code. Parent code: "\ + "$1 Position: $2"), (code, position)) + return + if position == -1: + position = 0 + elif position != 0: + print utils.mapping(_("Error: Invalid child position in "\ + "decomposition. Parent code: $1 Child code: $2 "\ + "Position: $3"), (code, child_code, position)) + return + if not isinstance(factor, float): + factor == 1.0 + if not isinstance(yield_, float): + yield_ = 1.0 + _record = self.setRecord(code, [], "", "", "", [], [], + "", "") + _child = _record.appendChild(child_code, self.getDecimals(), + factor, yield_, total, list_lines, type, label) + _child.budgetMeasures[0] = measure + + def eval_formula(self, formula, a, b, c, d): + """eval_formula(self, formula, a, b, c, d) + + formula: + Valid Operator: '(', ')', '+', '-', '*', '/' and '^' + Valid variable: 'a', 'b', 'c','d'y 'p' (Pi=3.1415926) + units: Number of Units (a) + length: Length (b) + width: Width (c) + height: Height (d) + + Evals the formula and return the result + """ + if a == "": a = 0.0 + if b == "": b = 0.0 + if c == "": c = 0.0 + if d == "": d = 0.0 + try: + a = float(a) + except: + raise ValueError, _("'a' value must be a float number") + try: + b = float(b) + except: + raise ValueError, _("'b' value must be a float number") + try: + c = float(c) + except: + raise ValueError, _("'c' value must be a float number") + try: + d = float(d) + except: + raise ValueError, _("'d' value must be a float number") + # spaces are erased + sre.sub("[ ]","",formula) + # operators and varibles are replaced + formula = formula.replace("+", " + ") + formula = formula.replace("-", " - ") + formula = formula.replace("*", " * ") + formula = formula.replace("/", " / ") + formula = formula.replace("^", " ** ") + formula = formula.replace("(", " ( ") + formula = formula.replace(")", " ) ") + formula = formula.replace("a", str(a)) + formula = formula.replace("b", str(b)) + formula = formula.replace("c", str(c)) + formula = formula.replace("d", str(d)) + formula = formula.replace("p", "3.1415926") + _list_formula = formula.split(" ") + _formula2 = "" + for oper in _list_formula: + try: + _float_oper= str(float(oper)) + _formula2 = _formula2 + _float_oper + except ValueError: + _formula2 = _formula2 + oper + _g = {"__builtins__":{}} + try: + return eval(_formula2, _g) + except: + raise ValueError, _("Invalid formula") + + def getText(self,code): + """getText(self,code) + + code: the record code + Returns the description text of a record + """ + if code in self.__records: + return self.__records[code].text + else: + raise IndexError, _("Invalid code") + + def setText(self,code,text): + """setText(self,code,text) + + code: the parent record code + text: the descripion text + Sests the description text of a record + """ + if not utils.is_valid_code(code)[0]: + raise ValueError, utils.mapping(_("Invalid record: $1"), (code,)) + if not code in self.__records: + _record = self.setRecord(code, [], "", "", "", [], [], + "", "") + _record.text = text + else: + _record = self.__records[code] + _record.text = text + + def setRecord(self, code, synonyms, hierarchy, unit, summary, price, date, + type, subtype): + """setRecord(self, code, synonyms, hierarchy, unit, summary, price, + date, type, subtype) + + code: Code string + synonyms: List of synonym codes of the record + hierarchy: + 0 -> root + 1 -> Chapter/Subchapter + 2 -> Other + unit: unit of measure record + summary: Short description of a record + price: List of prices + date: List of dates + "type" and "subtype": + 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 + Adds a record in the budget + """ + # hierarchy + if hierarchy == 0 : + # is the root record + if self.__root is None: + self.__root = code + else: + print _("Only can be one root record") + return + # retake previous values. + # TODO: test synonyms + _budget = self + if not code in self.__records: + if code[-1] == "$": + _record = ParametricRecord(_budget.getDecimals(), code, + synonyms, hierarchy, + unit, summary, [], type, subtype, + [], "") + else: + _record = Record(_budget.getDecimals(), code, synonyms, + hierarchy, unit, + summary, [], type, subtype,[], "") + self.__records[code] = _record + _prices = [[price[i], date[i]] for i in range(len(price))] + _record.setPrices(_prices, self.getDecimals()) + else: + _record = self.__records[code] + code = _record.code + if len(synonyms) != 0 and synonyms[0] == "": + synonyms = _record.synonyms + if unit == "": + unit = _record.unit + if summary == "": + summary = _record.summary + #TODO: test empty price list + if len(price) == 0 or price[0] == "": + _prices = _record.prices + else: + _prices = [ [price[i], date[i]] for i in range(len(price))] + if type == "": + type = _record.recordType.type + _record.synonyms = synonyms + _record.unit = unit + _record.summary = summary + _record.setPrices(_prices, self.getDecimals()) + _record.recordType.hierarchy = hierarchy + _record.recordType.type = type + _record.recordType.subtype = subtype + return _record + + def hasRecord(self,code): + """hasRecord(self,code) + + code: Code record + Return True if the budget have this record code. + """ + if code in self.__records: + return True + else: + return False + + def getRecord(self, code): + """getRecord(self, code) + + code: Code record + Return the Record object + """ + return self.__records[code] + + def addPriceToRecord(self, price_date, record): + """addPriceToRecord(self, price, record) + + Add a price to the price list of the record. + price must fulfill: + - must be a list with two items + - the first item: price must be a float + """ + record.addPrice(price_date, self.getDecimals()) + + def getStrPriceFromRecord(self, index_price, record): + _price = record.getPrice(index_price) + _D = self.getDecimals().getD(record.recordType) + _price = ("%." + str(_D) + "f" ) % _price + return _price + + def getCode(self, path): + """getCode(self, path) + + path: path record in the budget. + Return the code record + """ + if isinstance(path, tuple) and len(path)>= 1: + if path[0] == 0: + _code = self.__root + for i in path[1:]: + if isinstance(i, int): + _record = self.__records[_code] + _children_list = _record.children + try: + _child = _children_list[i] + except: + raise ValueError, _("This record does not exits") + _code = _child.code + else: + raise ValueError, _("Path item must be a integer") + return _code + else: + raise ValueError, _("This record does not exits") + else: + raise ValueError, utils.mapping(_("Path must be a not empty "\ + "tuple: $1"), (str(path),)) + + def getAmount(self, path): + """def getAmount(self,path) + + path: record path + Calculate the record amount + """ + if len(path) == 1: + # root: amount is the root price + _root = self.getRecord(self.getRoot()) + _amount = _root.getPrice(self.__title_index) + return _amount + else: + _parent_code = self.getCode(path[:-1]) + _parent_record = self.getRecord(_parent_code) + _child_number = path[-1] + + _decomposition = _parent_record.children[_child_number] + _factor = _decomposition.budgetMeasures[0].factor + _yield = _decomposition.budgetMeasures[0].yield_ + _child_code = _decomposition.code + _child_record = self.getRecord(_child_code) + _price = _child_record.getPrice(self.getActiveTitle()) + _DR = self.getDecimals().getDR(_parent_record.recordType) + _total_yield = round(_factor * _yield, _DR) + _DI = self.getDecimals().getDI(_parent_record.recordType) + _amount = round(_total_yield * _price, _DI) + return _amount + + def getStrAmount(self, path): + """def getStrAmount(self, path) + + path: record path + Calculate the string record amount + """ + if len(path) == 1: #root + _root = self.getRecord(self.getRoot()) + _amount = self.getStrPriceFromRecord(self.__title_index, _root) + return _amount + else: + _parent_code = self.getCode(path[:-1]) + _parent_record = self.getRecord(_parent_code) + _amount = self.getAmount(path) + _DI = self.getDecimals().getDI(_parent_record.recordType) + _amount = ("%." + str(_DI) + "f") % _amount + return _amount + + def setSheetSection(self,sheet_code,sheet_title): + if not isinstance(sheet_code, str): + raise ValueError, _("The sheet code must be a string") + if not isinstance(sheet_title, str): + raise ValueError, _("The sheet title must be a string") + self.__sheet_sections[sheet_code] = sheet_title + def hasSheetSection(self, section): + return section in self.__sheet_sections + def getSheetSection(self, section): + return self.__sheet_sections[section] + def setSheetSections(self,dictionary): + if not isinstance(dictionary, dict): + raise ValueError, _("The sheet sections must be a dictionary") + for sheet_code in dictionary.keys(): + self.setSheetSection(sheet_code, dictionary[sheet_code]) + def setSheetField(self, field_code, field_title): + if not isinstance(field_code, str): + raise ValueError, _("The field code must be a string") + if not isinstance(field_title, str): + raise ValueError, _("The field title must be a string") + self.__sheet_fields[field_code] = field_title + def hasSheetField(self, field): + return field in self.__sheet_fields + def getSheetField(self, field): + return self.__sheet_fields[field] + def setSheetFields(self, field_dict): + if not isinstance(field_dict, dict): + raise ValueError, _("The sheet field must be a dictionary") + for field_code in field_dict.keys(): + self.setSheetField( field_code, field_dict[field_code]) + def setSheetParagraph(self, paragraph_code, paragraph_text): + if not isinstance(paragraph_code, str): + raise ValueError, _("The paragraph code must be a string") + if not isinstance(paragraph_text, str): + raise ValueError, _("The paragraph text must be a string") + self.__sheet_paragraphs[paragraph_code] = paragraph_text + def hasSheetParagraph(self, paragraph): + return paragraph in self.__sheet_paragraphs + def getSheetParagraph(self, paragraph): + return self.__sheet_paragraphs[paragraph] + def setSheetParagraphs(self, paragraph_dict): + if not isinstance(paragraph_dict, dict): + raise ValueError, _("The paragraph dict must be a dictionary") + for paragraph_code in paragraph_dict.keys(): + self.setSheetParagraph( paragraph_code, paragraph_dict[paragraph_code]) + def setSheetRecord(self, record_code, field, section_dict): + if not isinstance(record_code, str): + raise ValueError, _("The record_code code must be a string") + if not isinstance(field, str): + raise ValueError, _("The field must be a string") + if not isinstance(section_dict, dict): + raise ValueError, _("The section dict must be a dictionary") + #-# + # TODO: Add a empty record? + if not self.hasRecord(record_code): + print utils.mapping(_("Error: The budget do not have this record "\ + "code and can not be added the sheet text in the field $1. "\ + "Record Code: $2"), ( field, record_code)) + return + #-# + if not self.hasSheetField(field): + self.setSheetField(field, "") + for section, paragraph in section_dict.iteritems(): + if not self.hasSheetParagraph(paragraph): + self.setSheetParagraph(paragraph,"") + if not self.hasSheetSection(section): + self.setSheetSection(section, "") + _sheet = self.getRecord(record_code).getSheet() + _sheet.addSection(field, section, paragraph) + def addFile(self, record_code, filepath, type, description): + if not isinstance(record_code, str): + raise ValueError, _("The record_code code must be a string") + if not isinstance(filepath, str): + raise ValueError, _("The filename must be a string") + #-# + # TODO: Add a empty record? + if not self.hasRecord(record_code): + print utils.mapping(_("Error: The budget do not have the record "\ + "code $1 and can not be added the file: $2"), + (record_code, filepath)) + return + #-# + _record = self.getRecord(record_code) + _record.addFile(filepath, type, description) + def setCompany(self, company_code, sumamary, name, offices, + cif, web, email): + if not isinstance(company_code, str): + raise ValueError, _("The company code must be a string") + if not isinstance(sumamary, str): + raise ValueError, _("The summary must be a string") + if not isinstance(name, str): + raise ValueError, _("The name must be a string") + if not isinstance(offices, list): + raise ValueError, _("The name must be a list") + _offices = [] + for _office in offices: + if not isinstance(_office, list): + raise ValueError, _("The office must be a list") + if not len(_office) == 10: + raise ValueError, _("The office must be a 10 items list") + for _item in _office[:7] + _office[9:10]: + if not isinstance(_item, str): + raise ValueError, _("This office item must be a "\ + "string") + for _item in _office[7:8]: + if not isinstance(_item, list): + raise ValueError, _("This office item must be a "\ + "list") + _offices.append(Office(_office[0], + _office[1], + _office[2], + _office[3], + _office[4], + _office[5], + _office[6], + _office[7], + _office[8], + _office[9])) + if not isinstance(cif, str): + raise ValueError, _("The name must be a string") + if not isinstance(web, str): + raise ValueError, _("The web must be a string") + if not isinstance(email, str): + raise ValueError, _("The email must be a string") + + self.__companys[company_code] = Company(company_code, sumamary, name, + _offices, cif, web, email) + def getCompany(self, company_code): + return self.__companys[company_code] + def getCompanyKeys(self): + return self.__companys.keys() + def addTecInfo(self, ti_code, text, unit): + if not isinstance(ti_code, str): + raise ValueError, _("The tecnical info code must be a string") + if not isinstance(text, str): + raise ValueError, _("The tecnical info description must be a "\ + "string") + if not isinstance(unit, str): + raise ValueError, _("The tecnical info unit must be a string") + self.__tec_info[ti_code] = [text, unit] + def hasTecInfo(self, ti_code): + return ti_code in self.__tec_info + def getTecInfo(self, ti_code): + return self.__tec_info[ti_code] + def setTecnicalInformation(self, record_code, ti_dict): + """setTecnicalInformation(record_code, ti_dict) + + Sets the tecnical information to a record + record_code: the record code + ti_dict: {ti_code : ti_value} + """ + # TODO: setTecnicalInformation + pass + def changeCode(self, record_code, new_record_code): + """changeCode(self, record_code, new_record_code): + + Change the record code for a new recor code. + """ + if self.hasRecord(record_code) and not self.hasRecord(new_record_code): + _record = self.__records[code] + _record.code = new_record_code + _parents = _record.parents + for _parent in _parents: + _decomposition_list = self.__records[_parent].children + for _decomposition in _decomposition_list: + if _decomposition.code == record_code: + _decomposition.code = new_record_code + break + _children = self.getchildren(record_code) + for _child in _children: + _parents_list = self.__records[_child].parents + for index in range(len(_parents_list)): + if _parents_list[index] == record_code: + _parents_list[index] = new_record_code + break + self.__records[new_record_code] = _record + del self.__records[record_code] + # TODO: attachment files + + def addLabel(self, record_code, label): + """addLabel(self, record_code, label) + + Add a label to a record + """ + if not isinstance(label,str): + raise ValueError, _("The label must be a string") + if self.hasRecord(record_code): + _record = self.__records[record_code] + _record.addLabel(label) + if not label in self.__labels: + self.__labels[label] = [record_code] + else: + _codes = self.__labels[label] + if not record_code in _codes: + _codes.append(record_code) + def setParametricSelectComment(self, record_code, comment): + """setParametricSelectComment(self, record_code, comment) + + Sets Paramtric Record Select Comment + """ + if not isinstance(record_code, str): + raise ValueError, _("The record_code code must be a string") + if not isinstance(comment, str): + raise ValueError, _("The parametric select comment must be a "\ + "string") + if not self.hasRecord(record_code): + print utils.mapping(_("Error: The budget do not have the record "\ + "code $1 and can not be added the Parametric select comment: "\ + "$2"), + (record_code, comment)) + return + _record = self.getRecord(record_code) + if not isinstance(_record, ParametricRecord): + print utils.mapping(_("Error: The Record $1 is not a "\ + "Parametric Record and can not have Parametric comment"), + (record_code,)) + else: + _record.select_comment = comment + + def setParametricSummary(self, record_code, summary): + """setParametricSummary(self, record_code, summary) + + Sets parametric record summary + """ + if not isinstance(record_code, str): + raise ValueError, _("The record_code code must be a string") + if not isinstance(summary, str): + raise ValueError, _("The summary record must be a string") + if not self.hasRecord(record_code): + print utils.mapping(_("Error: The budget do not have the record "\ + "code $1 and can not be seted the summary: $2"), + (record_code, summary)) + return + _record = self.getRecord(record_code) + if not isinstance(_record, ParametricRecord): + print utils.mapping(_("Error: The Record $1 is not a "\ + "Parametric Record and can not have Parametric summary"), + (record_code,)) + else: + self.getRecord(record_code).parametric_summary = summary + + def setParametricText(self, record_code, text): + """setParametricText(self, record_code, text) + + Sets parametric record text + """ + if not isinstance(record_code, str): + raise ValueError, _("The record_code code must be a string") + if not isinstance(text, str): + raise ValueError, _("The text record must be a string") + if not self.hasRecord(record_code): + print utils.mapping(_("Error: The budget do not have the record "\ + "code $1 and can not be seted the text: $2"), + (record_code, text)) + return + _record = self.getRecord(record_code) + if not isinstance(_record, ParametricRecord): + print utils.mapping(_("Error: The Record $1 is not a "\ + "Parametric Record and can not have Parametric text"), + (record_code,)) + else: + self.getRecord(record_code).parametric_text = text + +class Office(object): + """base.Office: + + Description: + Office of a company + Constructor: + base.Office(type, subname, address, postal_code, town, province, + country, phone, fax, contact_person) + Ancestry: + +-- object + +-- Office + Atributes: + "officeType" : type of Office + are defined: + "C" Central. + "D" Local Office. + "R" performer. + "subname" : name of Office or Performer + "address" : + "postal_code" : + "town" : + "province" : + "country" : + "phone" : list of phone numbers + "fax" : list of fax numbers + "contact_person" : name of contact person + "values": + Methods: + __getstate__(self) + __setstate__(self, tuple) + __init__(self, measure, lines, label) + {get/set}OfficeType + {get/set}Subname + {get/set}Address + {get/set}PostalCode + {get/set}Town + {get/set}Province + {get/set}Country + {get/set}Phone + {get/set}Fax + {get/set}ContactPerson + getValues + """ + __slots__ = ["_Office__officeType", + "_Office__subname", + "_Office__address", + "_Office__postal_code", + "_Office__town", + "_Office__province", + "_Office__country", + "_Office__phone", + "_Office__fax", + "_Office__contact_person", + ] + def __getstate__ (self): + return ( self.__officeType, + self.__subname, + self.__address, + self.__postal_code, + self.__town, + self.__province, + self.__country, + self.__phone, + self.__fax, + self.__contact_person) + def __setstate__(self,tuple): + self.__officeType = tuple[0] + self.__subname = tuple[1] + self.__address = tuple[2] + self.__postal_code = tuple[3] + self.__town = tuple[4] + self.__province = tuple[5] + self.__country = tuple[6] + self.__phone = tuple[7] + self.__fax = tuple[8] + self.__contact_person = tuple[9] + + def __init__(self, type, subname, address, postal_code, town, province, + country, phone, fax, contact_person): + self.officeType = type + self.subname = subname + self.address = address + self.postal_code = postal_code + self.town = town + self.province = province + self.country = country + self.phone = phone + self.fax = fax + self.contact_person = contact_person + def getOfficeType(self): + return self.__officeType + def setOfficeType(self, type): + self.__officeType = type + def getSubname(self): + return self.__subname + def setSubname(self, subname): + self.__subname = subname + def getAddress(self): + return self.__address + def setAddress(self, address): + self.__address = address + def getPostalCode(self): + return self.__postal_code + def setPostalCode(self, postal_code): + self.__postal_code = postal_code + def getTown(self): + return self.__town + def setTown(self, town): + self.__town = town + def getProvince(self): + return self.__province + def setProvince(self, province): + self.__province = province + def getCountry(self): + return self.__country + def setCountry(self, country): + self.__country = country + def getPhone(self): + return self.__phone + def setPhone(self, phone): + self.__phone = phone + def getFax(self): + return self.__fax + def setFax(self, fax): + self.__fax = fax + def getContactPerson(self): + return self.__contact_person + def setContactPerson(self, contact_person): + self.__contact_person = contact_person + def getValues(self): + return {"officeType": self.officeType, + "subname": self.subname, + "address": self.address, + "postal code": self.postal_code, + "town": self.town, + "province": self.province, + "country": self.country, + "phone": self.phone, + "fax": self.fax, + "contact person": self.contact_person, + } + officeType = property(getOfficeType, setOfficeType, None, + """Type of office + """) + subname = property(getSubname, setSubname, None, + """Name of office + """) + address = property(getAddress, setAddress, None, + """Adress + """) + postal_code = property(getPostalCode, setPostalCode, None, + """Postal code + """) + town = property(getTown, setTown, None, + """Town + """) + province = property(getProvince, setProvince, None, + """Province + """) + country = property(getCountry, setCountry, None, + """Country + """) + phone = property(getPhone, setPhone, None, + """Phone numbers + """) + fax = property(getFax, setFax, None, + """Fax numbers + """) + contact_person = property(getContactPerson, setContactPerson, None, + """Contact Person + """) + values = property(getValues, None, None, + """Dictionary with comapany values + """) + +class Company(object): + """base.Company: + + Description: + Company object + __slots__ attribute, __getstate__ and __setstate__ method are defined + to use less ram memory. + Constructor: + base.Company(code, summary, name, offices, cif, web, email) + Ancestry: + +-- object + +-- Company + Atributes: + "code": code to indentifie the company in the buget + "summary": short name + "name": long name + "offices": List of Offices + "cif": CIF + "web": web page + "email": email + "values": + Methods: + __getstate__(self) + __setstate__(self, tuple) + __init__(self, measure, lines, label) + {get/set}Code + {get/set}Summary + {get/set}Name + {get/set}Offices + {get/set}Cif + {get/set}Web + {get/set}Email + getValues + """ + __slots__ = ["_Company__code", + "_Company__summary", + "_Company__name", + "_Company__offices", + "_Company__cif", + "_Company__web", + "_Company__email", + ] + def __getstate__ (self): + return ( self.__code, + self.__summary, + self.__name, + self.__offices, + self.__cif, + self.__web, + self.__email) + def __setstate__(self,tuple): + self.__code = tuple[0] + self.__summary = tuple[1] + self.__name = tuple[2] + self.__offices = tuple[3] + self.__cif = tuple[4] + self.__web = tuple[5] + self.__email = tuple[6] + + def __init__(self, code, summary, name, offices, cif, web, email): + self.code = code + self.summary = summary + self.name = name + self.offices = offices + self.cif = cif + self.web = web + self.email = email + def getCode(self): + return self.__code + def setCode(self, code): + self.__code = code + def getSummary(self): + return self.__summary + def setSummary(self, summary): + self.__summary = summary + def getName(self): + return self.__name + def setName(self, name): + self.__name = name + def getOffices(self): + return self.__offices + def setOffices(self, offices): + self.__offices = offices + def getCif(self): + return self.__cif + def setCif(self, cif): + self.__cif = cif + def getWeb(self): + return self.__web + def setWeb(self, web): + self.__web = web + def getEmail(self): + return self.__email + def setEmail(self, email): + self.__email = email + def getValues(self): + return {"code": self.code, + "summary": self.summary, + "name": self.name, + "cif": self.cif, + "web": self.web, + "email": self.email} + code = property(getCode, setCode, None, + """Company code + """) + summary = property(getSummary, setSummary, None, + """Company summary + """) + name = property(getName, setName, None, + """Company name + """) + offices = property(getOffices, setOffices, None, + """List of Offices + """) + cif = property(getCif, setCif, None, + """CIF + """) + web = property(getWeb, setWeb, None, + """Web page + """) + email = property(getEmail, setEmail, None, + """Email + """) + values = property(getValues, None, None, + """Dictionary with comapany values + """) + +class File(object): + """base.Company: + + Description: + File object + Constructor: + base.File(name, type, description) + Ancestry: + +-- object + +-- File + Atributes: + "name": name + "fileType": type of file + "description": description file + Methods: + __getstate__(self) + __setstate__(self, tuple) + __init__(self, path,type, description) + {get/set}Name + {get/set}FileType + {get/set}Description + getValues + """ + __slots__ = ["_File__name", + "_File__fileType", + "_File__description", + + ] + def __getstate__ (self): + return (self.__name, + self.__description, + self.__fileType, + ) + def __setstate__(self,tuple): + self.__name = tuple[0] + self.__fileType = tuple[1] + self.__description = tuple[2] + def __init__(self, name, type, description): + self.name = name + self.fileType = type + self.description = description + def getName(self): + return self.__name + def setName(self, name): + self.__name = name + def getFileType(self): + return self.__fileType + def setFileType(self, type): + self.__fileType = type + def getDescription(self): + return self.__description + def setDescription(self, description): + self.__description = description + def getValues(self): + return {"name": self.name, + "fileType": self.fileType, + "description": self.description, + } + name = property(getName, setName, None, + """File name + """) + fileType = property(getFileType, setFileType, None, + """FileType + """) + description = property(getDescription, setDescription, None, + """File description + """) + values = property(getValues, None, None, + """Dictionary with file values + """) + +class RecordType(object): + """base.RecordType: + + Description: + Record Type object + "hierarchy": + -1 -> temporarily unfixed + 0 -> root + 1 -> Chapter/Subchapter + 2 -> Other + "type" and "subtype": + 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 + Constructor: + base.File(hierarchy,type,subtype) + Ancestry: + +-- object + +-- RecordType + Atributes: + "hierarchy": hierarchy + "type": type + "subtype": subtype + Methods: + __getstate__(self) + __setstate__(self, tuple) + __init__(self, hierarchy, type, subtype) + {get/set}Hierarchy + {get/set}Type + {get/set}Subtype + """ + __slots__ = ["_RecordType__hierarchy", + "_RecordType__type", + "_RecordType__subtype", + ] + def __getstate__ (self): + return (self.__hierarchy, + self.__type, + self.__subtype, + ) + def __setstate__(self,tuple): + self.__hierarchy = tuple[0] + self.__type = tuple[1] + self.__subtype = tuple[2] + def __init__(self, hierarchy, type, subtype): + self.hierarchy = hierarchy + self.type = type + self.subtype = subtype + def getHierarchy(self): + return self.__hierarchy + def setHierarchy(self, hierarchy): + if not hierarchy in [-1, 0 , 1 ,2]: + raise ValueError, utils.mapping(_("Invalid Hierarchy ($1) "\ + "The hierarchy must be -1, 0, 1 or 2"), (str(hierarchy))) + self.__hierarchy = hierarchy + def getType(self): + return self.__type + def setType(self, type): + if not type in ["", 0, 1, 2, 3] : + raise ValueError, utils.mapping(_("Invalid type ($1),"\ + "the type must be (empty string,0,1,2,3)"),(str(type))) + self.__type = type + def getSubtype(self): + return self.__subtype + def setSubtype(self, subtype): + if not subtype in ["", "OB", "PU", "EA", "EU", "EC", "EF", "PA", "H", + "Q", "%", "MC", "MCr", "MM", "MS", "ME", "MCu", + "Mal","ML","M"]: + raise ValueError, utils.mapping(_("Invalid subtype ($1), The "\ + "subtype must one in (empty string, EA, "\ + "EU, EC, EF, OB, PA, PU, H, Q, %, MC, MCr, "\ + "MM, MS, ME, MCu, MAl, ML, M)"), (str(subtype))) + self.__subtype = subtype + hierarchy = property(getHierarchy, setHierarchy, None, + """Record Hierarchy + -1 -> temporarily unfixed + 0 -> root + 1 -> Chapter/Subchapter + 2 -> Other + """) + type = property(getType, setType, None, + """Record Type + 0 Without classifying + 1 Labourforce + 2 Machinery and auxiliary equipment + 3 Building materials + """) + subtype = property(getSubtype, setSubtype, None, + """Record Subtype + None + EA Auxiliary element + EU Unitary element + EC Complex element + EF Functional element + OB Construction site + PA Cost overrun + PU Unitary budget + H Labourforce + Q Machinery + % Auxiliary equipment + MC Cement + MCr Ceramic + MM Wood + MS Iron and steel + ME Energy + MCu Copper + MAl Aluminium + ML Bonding agents + M Others materials + """)