def _(id, value): log.info(f'Update activity "{id}" status to "{status}"') activity = VesselActivity.objects.get(id=id) activity.status = status or None activity.save() return activity.to_dict(timezone="Europe/Paris", include_vessel=True)
def meme_liste(self,liste1,liste2): """teste si deux listes sont identiques""" log.info("lancement de meme liste.teste si deux listes sont identiques") verite = False if self.listes_meme_longueur(liste1,liste2): return self.listes_meme_contenus(liste1,liste2) else: return verite
def _(id, value): activity = VesselActivity.objects.get(id=id) vessel = activity.vessel log.info(f'Update vessel "{vessel.id}" helico to "{obs}"') vessel.helico_observation = obs or None vessel.save() return activity.to_dict(timezone="Europe/Paris", include_vessel=True)
def assertSameFileContent(self, fich1, fich2): #item 306 log.info("lancement de assertSameFileContent avec {} et {}".format(fich1, fich2)) assertion = False for ligne in readFichierTemporaire(fich1): l = readLineFichierTemporaire(fich2) if ligne == l: assertion = True log.info("les lignes sont similaires") else: log.error("les fichiers diffèrent: ",ligne, " vs ", l) return assertion
def setSeuils(self, *args): """1ere resp""" """remplir la liste cstte de limites legales""" """ la liste doit etre exclusivement composee d entiers""" log.info( "lancement de Seuils.setSeuils avec pour param {}".format(args)) print("creation d un seuil depuis", args) for i in args: if type(i) is not type(1): print("type(", str(i), ") vaut: ", type(i)) print("type(1) vaut:", type(1)) raise TypeError """ la liste doit etre triee""" self.seuils = sorted(args)
def sup25_deja_paye(heures_cp): log.info("appel de sup25 deja paye avec heures_cp {}".format(heures_cp)) try: if heures_cp > 39: raise ValueError( 'heures cp doit etre <= 39 j ai eu {} heures'.format( heures_cp)) valeur = 4 - (heures_cp * 4 / 39) return valeur except (ValueError): print("je passe heures cp au max soit 39. corrigez err ds cp") heures_cp = 39 valeur = 4 - (heures_cp * 4 / 39) return valeur
async def update_activity_status(id, status): """ Update ship status for the given activity """ def _(id, value): log.info(f'Update activity "{id}" status to "{status}"') activity = VesselActivity.objects.get(id=id) activity.status = status or None activity.save() return activity.to_dict(timezone="Europe/Paris", include_vessel=True) activity = await loop.run_in_executor(None, _, id, status) activity['timestamp'] = pendulum.utcnow().timestamp() log.info(f'Update status info for activity {activity!r}') self.publish('smit.activity.update', activity) return activity
def isSqlite3(self, filename): """ fich sqlite3?""" TAILLE_HEADER_SQLITE3 = 100 from os.path import isfile, getsize if not isfile(filename): log.error(filename + " est pas un fich") return False if getsize(filename) < TAILLE_HEADER_SQLITE3: log.error("trop petit pr e fic sqlitr3") return False with open(filename, 'rb') as fd: header = fd.read(TAILLE_HEADER_SQLITE3) verite = (header[:16] == b'SQLite format 3\x00') if not verite: log.error("{}: mauvais header sqlite3".format(filename)) else: log.info( "fichier {} est bien de type sqlite3".format(filename)) return verite
def listes_meme_longueur(self,liste1=None,liste2=None): """teste si deux listes ont la mm longueur""" log.info("lancement de liste_meme_longueur avec {} et {}. teste si les deux param st des listes et ont la mm lg".format(liste1, liste2)) self.planter_si_liste_vide(liste1,"liste1") self.planter_si_liste_vide(liste2,"liste2") verite = False if type(liste1) == type([]): if type(liste2) == type([]): verite = (len(liste1) == len(liste2)) return verite else: print("type( ", str(liste1), "):", type(liste1)) print("type([]): ", type([])) raise TypeError else: print("type( ", str(liste2), "):", type(liste2)) print("type([]): ", type([])) raise TypeError
def listes_meme_contenus(self,liste1,liste2): """si les deux elements sont des listes de mm lg, cherche si elles ont le meme contenu""" log.info("lancement de listes_meme_contenus. si les deux elements sont des listes de mm lg, cherche si elles ontle meme contenu") verite = True """ parametre: deux listes non vides""" """non vides""" if liste1 is None: log.critical("liste1 is none! should be list") raise TypeError if liste2 is None: log.critical("liste2 is none! should be list") raise TypeError for pos, elem in enumerate(liste1): if liste1[pos] != liste2[pos]: verite = False break return verite
async def update_vessel_helico_obs(id, obs): """ Update helicopter obs for the vessel of this activity """ def _(id, value): activity = VesselActivity.objects.get(id=id) vessel = activity.vessel log.info(f'Update vessel "{vessel.id}" helico to "{obs}"') vessel.helico_observation = obs or None vessel.save() return activity.to_dict(timezone="Europe/Paris", include_vessel=True) activity = await loop.run_in_executor(None, _, id, obs) activity['timestamp'] = pendulum.utcnow().timestamp() log.info(f'Update helico obs for activity {activity!r}') self.publish('smit.activity.update', activity) return activity
def gen_heures_sup_annee2(a): TI = [''.join(['ANNEE ', str(a)])] E = ['m', 'tot', '>35', '>43', '>48'] L = list(iter_heures_sup_mois2(a)) A = functools.reduce(somme_terme_a_terme, iter_heures_sup_mois(a), [0, 0, 0, 0]) log.debug("A {}vaut {}".format(a, A)) log.info(TI) log.info(E) for ligne in L: log.info(ligne) log.info(["CUMUL MOIS"] + A) return A
def gen_heures_sup_mois2(m, a): TI = ["mois de {} {}".format(m, a)] E = ['s', 'tot', 'h>39', 'h>43', 'h>48'] S = list(iter_heures_sup_semaines_mois2(a, m)) M = functools.reduce(somme_terme_a_terme, iter_heures_sup_semaines_mois(a, m), [0, 0, 0, 0]) #M = ["CUMUL SEM"] + M log.info(TI) log.info(E) for ligne in S: log.info(ligne) log.info(["CUMUL SEM"] + M) log.debug("M {} de l annee {} vaut {} ".format(m, a, M)) return [str(m), M]
def eqv_trv_de_sup_du(mois, annee): ## def eqv_trv_de_sup_sem_du(semaine, annee): ## e_s = eqv_trv_de_sup_sem_du_25(semaine,annee) ## + eqv_trv_de_sup_sem_du_50(semaine,annee) ## + eqv_trv_de_sup_sem_du_100(semaine,annee) ## return e_s ## def eqv_trv_de_sup_sem_du_25(semaine,annee): ## heures_effectuees_semaine = getCumulHeuresTravailleesSemaine(semaine,annee) ## return bonification25( ## heures_sup_25_effectuees(heures_effectuees_semaine) ## ) ## def eqv_trv_de_sup_sem_du_50(semaine,annee): ## heures_effectuees_semaine = getCumulHeuresTravailleesSemaine(semaine,annee) ## return bonification50( ## heures_sup_50_effectuees(heures_effectuees_semaine) ## ) ## ## def eqv_trv_de_sup_sem_du_100(semaine,annee): ## heures_effectuees_semaine = getCumulHeuresTravailleesSemaine(semaine,annee) ## return bonification100( ## heures_illegales_effectuees(heures_effectuees_semaine) ## ) eqv = 0 import utilitaireDates semainesmois = list(utilitaireDates.iterSemaine(annee, mois)) for semaine in semainesmois: eqv = eqv + eqv_trv_de_sup_sem_du(semaine, annee) phrase_debug = "eqv vaut {} lors semaine {} (sem25: {} sem50: {} sem100: {}" phrase_debug = phrase_debug.format( eqv, semaine, eqv_trv_de_sup_sem_du_25(semaine, annee), eqv_trv_de_sup_sem_du_50(semaine, annee), eqv_trv_de_sup_sem_du_100(semaine, annee)) log.info(phrase_debug) return eqv
def get_vessel(call_sign, name, draft, length, duplicates): """ Try to get a matching vessel """ # get the vessel by the unique name if call_sign: try: vessel = Vessel.objects.get(call_sign=call_sign) log.info(f'Known vessel: {vessel}') return vessel except Vessel.DoesNotExist: vessel = Vessel.objects.create( draft=draft, length=length, name=name, call_sign=call_sign ) log.info(f'New vessel: {vessel}') return vessel # failling that, try to get the boat by the name. if name not in duplicates: # from the boat table try: return Vessel.objects.get(name=name) except Vessel.DoesNotExist: log.info(f'New vessel: {vessel}') return Vessel.objects.create( draft=draft, length=length, name=name, all_sign=call_sign ) # or in case there are several known vessels with that # name, from the most recent unique activity with this # boat except Vessel.MultipleObjectsReturned: activity = VesselActivity.get_recent_activity_for( name=name ) if activity: log.info(f'Known vessel: {activity.vessel}') return activity.vessel return None
async def process_xml(xml): log.debug(f'Processing xml') distances = {} try: nh_settings = Settings.objects.get(active=True) except (Settings.DoesNotExist, Settings.MultipleObjectsReturned): log.error('Unable to load NH settings') return {} try: root = ET.fromstring(xml) for tag in root.findall('.//ns:ObjectData', XML_NS): try: id_tag = list(tag.findall('.//ns:Identifier', XML_NS))[0] except IndexError: continue try: call_sign = id_tag.get('Callsign').strip() except AttributeError: xmlstr = ET.tostring(id_tag, encoding='utf8', method='xml') log.error(f'No call sign for a vessel in this xml: {xmlstr!r}') continue try: pos_tag = list(tag.findall('.//ns:Pos', XML_NS))[0] except IndexError: continue try: lat = float(pos_tag.get('Lat')) long = float(pos_tag.get('Long')) except (TypeError, ValueError): continue distances[call_sign] = round(haversine( (nh_settings.red_dot_lat, nh_settings.red_dot_long), (lat, long) ), 2) try: log.info('Checking xml sanity. Last call sign:') log.info(call_sign) except NameError: log.error(f'No call signs found in xml: {xml!r}') log.info(f'New distances to red dot: {distances!r}') return distances except Exception as e: log.exception(f"Unable to read xml stream: {xml!r}")
def run(self): sys.settrace(lambda *a, **k: None) try: while True: logger.info('{}: Start'.format(self)) try: self.running = True self.callback() except Exception as e: self.running = False raise else: self.running = False logger.info('{}: Clean exit'.format(self)) except SigFinish: logger.info('{}: SigFinish'.format(self))
def planter_si_liste_vide(self,liste,nomliste): log.info("lancement de planter si liste vide avec {} et {}".format(liste, nomliste)) if liste is None: log.critical(nomliste + " is None. should be non empty list") raise TypeError
def test_plantage_listes_memes_lg_si_param1_liste_vide(self): log.info("lancement de test_plantage_listes_memes_lg_si_param1_liste_vide") self.assertRaises(TypeError,self.listes_meme_longueur,None,[1])
async def crawl_xml( xml_callback=print, tick=10 * 60, # 10 minutes max_timeout=10 ): if not callable(xml_callback): raise ValueError('xml_callback must be callable') if not inspect.iscoroutinefunction(xml_callback): xml_callback = asyncio.coroutine(xml_callback) timeout = 1 while True: try: try: nh_settings = Settings.objects.get(active=True) except (Settings.DoesNotExist, Settings.MultipleObjectsReturned): log.error('Unable to load NH settings') await asyncio.sleep(1) continue host = nh_settings.nh_ip_address port = nh_settings.nh_port login = nh_settings.nh_username pwd = nh_settings.nh_password tick = nh_settings.nh_refresh_rate log.info(f"Connection to {host}:{port}...") reader, writer = await asyncio.open_connection(host, port) with closing(writer): login_payload = NH_LOGIN_TEMPLATE.format( login=login, password=pwd ) writer.write(login_payload.encode('utf8')) timeout = 1 connected = True while connected: buffer = "" empty_xml = 0 while True: log.info('Download a piece of xml.') data = await reader.read(100000) buffer += data.decode('utf8', errors="replace") if len(buffer) > 10000000: log.error('Buffer is too big. Flushing') buffer = "" continue start = buffer.find(XML_OPENING_TAG) end = buffer.find(XML_CLOSING_TAG) if (start != -1 and end != -1): log.info('Passing XML to callback.') xml = buffer[start:end + len(XML_CLOSING_TAG)] if not xml: # NH has closed the socket for some reason if reader.at_eof(): # ask for immediat reconnection connected = False timeout = None break log.error(f'XML is empty, skipping. (buffer: {buffer!r})') continue await xml_callback(xml) break else: nh_settings.refresh_from_db() await asyncio.sleep(nh_settings.nh_refresh_rate) log.info(f'Next download in {tick}s.') except Exception as e: raise e if timeout is None: # requested instant reconnection before timeout = 1 else: await asyncio.sleep(timeout) timeout *= 2 if timeout > max_timeout: timeout = max_timeout
def clear_old_activities(): six_month_ago = pendulum.utcnow().add(months=-6) res = VesselActivity.objects.filter(modified__lt=six_month_ago).delete() log.info(f'Deleted old activities: {res}')
def test_cas_heures_sup_au_premier_seuil_seuil_unique(self): log.info("lancement de test_cas_heures_sup_au_premier_seuil_seuil_unique") self.t_seuils([15,3],Seuil(15).decouper(18))
def t_seuils(self,liste1,liste2): log.info("lancement de t_seuils avec: {} {}".format(liste1,liste2)) self.assertEqual(True, self.meme_liste(liste1,liste2), '{} differe de {}'.format(liste1, liste2))
def total2(): TI = ['TOTAL ANNEES'] E = ['a', 'tot', '>35', '>43', '>48'] L = list(iter_heures_sup_annees_a()) T = functools.reduce(somme_terme_a_terme, iter_heures_sup_annees2(), [0, 0, 0, 0]) T = ['TOTAL'] + T log.info(TI) log.info(E) for ligne in L: log.info(ligne) log.info(T) log.info(T) log.info( "si l on fait un décompte qui vous est favorable (le total sur 3 ans -plus la période est longue, plus les totaux se lissent-" ) log.info( "le total d heures supplémentaires à 25 % s'élève à {} heures".format( T[2])) log.info( "ce qui, comparé à 564 heures à 25% payées sur ces 3 années (47 x 4 x 3années)" ) log.info( "représente encore {} hors bonification heures à 25% non payées. ". format(T[2] - 3 * 47 * 4)) log.info("ce qui représente après bonification {} equivalent heures dûes". format((T[2] - 3 * 47 * 4) * 1.25)) log.info( "d autre part, les heures à 50% (de 43 à 48 heures ne sont jamais rémunérées" ) log.info( "cela représente {} hors bonification sur 3 ans jamais rémunérées". format(T[3])) log.info("soit {} heures bonifiées".format(T[3] * 1.5)) log.info( "ramené en équivalent heures - bonification de 25% et 50%- cela représente {} heures dûes" .format(((T[2] - 3 * 47 * 4) * 1.25) + (T[3] * 1.50))) log.info( "enfin, sur ces 3 ans, {} heures ont été effectuées au-delà du maximum légal de 48 heures." .format(T[4])) log.info( "comment comptez vous les rémunérer? à 100% cela ajoute encore {} heures dûes" .format(T[4] * 2))
async def crawl_csv( host, port, login, pwd, path, csv_callback=print, tick=10 * 60, # 10 minutes max_timeout=10 ): if not callable(csv_callback): raise ValueError('csv_callback must be callable') if not inspect.iscoroutinefunction(csv_callback): csv_callback = asyncio.coroutine(csv_callback) timeout = 1 while True: try: try: ftp_settings = Settings.objects.get(active=True) except (Settings.DoesNotExist, Settings.MultipleObjectsReturned): log.error('Unable to load ftp settings') await asyncio.sleep(1) continue host = ftp_settings.sirene_ftp_ip_address port = ftp_settings.sirene_ftp_port login = ftp_settings.sirene_ftp_username pwd = ftp_settings.sirene_ftp_password tick = ftp_settings.sirene_ftp_refresh_rate path = ftp_settings.sirene_csv_file_path log.info(f"Connection to {host}:{port}...") async with aioftp.ClientSession(host, port, login, pwd) as client: timeout = 1 while True: if not await client.exists(path): log.error( f"'{path}' can't be " f"found on FTP {host}:{port}" ) else: log.info('Downloading csv.') async with client.download_stream(path) as stream: log.info('Passing csv to callback.') await csv_callback(stream) log.info('Callback ok.') log.info(f'Next download in {tick}s.') ftp_settings.refresh_from_db() await asyncio.sleep(ftp_settings.sirene_ftp_refresh_rate) except AttributeError as e: if "'Client' object has no attribute 'stream'" in str(e): log.warning( f"Unable to connect to FTP {host}:{port}\n" f"Retrying in {timeout}s" ) else: raise except aioftp.errors.StatusCodeError as e: if '530' in e.received_codes: log.error( f"The login or password has been " f'rejected by {host}:{port}' ) else: log.warning( f"Connection to {host}:{port} failed with: '{e}'\n" f"Retrying in {timeout}s" ) except ConnectionResetError: log.warning( f"Connection to {host}:{port} lost\n" f"Retrying in {timeout}s" ) await asyncio.sleep(timeout) timeout *= 2 if timeout > max_timeout: timeout = max_timeout
def test_production_intervalles_multiples_3(self): log.info("lancement de test_production_intervalles_multiples_3") self.t_seuils([15,3,1],Seuil(15,18,19).getIntervalles())
def total3(): TI = ['TOTAL ANNEES'] E = ['a', 'tot', '>35', '>43', '>48'] L = list(iter_heures_sup_annees_a()) T = functools.reduce(somme_terme_a_terme, iter_heures_sup_annees2(), [0, 0, 0, 0]) T = ['TOTAL'] + T log.info(" ") log.info( "si l on arrete le décompte chaque mois, comme cela devrait etre le cas," ) log.info( "et qu on oppose ce qui devrait etre paye à ce qui a effectivement été payé," ) log.info(" ne décomptant que ce qui mnaque mois par mois,") log.info("alors on arrive au décompte suivant:") log.info("modifier gen_semaines pour cumul au mois soit 4h par sem trav") """
def prejudice(): """hsrd = hss_effectuee - hsspayee. les heures négatives ? c est le point le plus important: trois positions possibles: 1) les heures sont perdues pour l employeur et le décompte se fait à la semaine, paiement au mois 2) les heures ne sont pas perdues pour l employeur et le cumul se fait algébriquement sur le mois 3) les heures ne sont pas perdues pour l employeur et le cumul se fait algébriquement sur l'année. 4) il n'y a jamais de perte pour l'employeur et le compteur n est jamais perdu pour l employeur. je penche pour le type 3 pour l instant: il s'agit d une CORRECTION DU PAIEMENT REEL PAR RAPPORT A UN PAIEMENT DU pour qualifier un DOL PAR CONTRE QUEL QUE SOIT LE RESULTAT, LE COMPTEUR DE DIFF25 EST REMIS A ZERO au 1er janvier des exceptions sont à prendre en compte: pendant certaines semaines de congés payées, on travaille. on est à la fois en congés et au travail. cela se résoud en considérant cela comme des heures supplémentaires: en effet, les cp sont censées se substituer à des jours de travail mais ils sont payés comme des jours de travail (les heures à poser pour un jour de cp correspond à la moyenne des heures d une semaine de travail normal) """ """ALGO: pour chaque semaine du mois: probleme | semaine à exclure | semaine à inclure si semaine_travaillée et semaine_cp => probleme si non semaine travaillee et semeaince cp => semaine a exclure sinon semaine à inclure pour chaque semaine à inclure du mois: pour chaque type_d heures sup: heure_sup_dues vs heures_sup_payees. si heures sup_dues: ajouter (typeheures_sup : + heures_sup_dues) si heures_en_trop: ajouter(typeheures_sup: cagnotte globale) fin_du_mois : pour chaque type_heures_sup: pour chaque_semaine: ajouter_type_heures_sup ajouter_cagnotte vider_cagnotte si_heures_sup: ajouter_heures_sup renvoyer tableau_heures_sup_mensuellesje vex une methode restant_dues qui permette de calculer le nombre d heures restant dues à 25% . mon employeur me paie effectivement 169 heures par mois dont 4 bonifiées à 25 %: il applique la l3121-31: Article L3121-31 En savoir plus sur cet article... Modifié par LOI n°2016-1088 du 8 août 2016 - art. 8 (V) Dans les entreprises dont la durée collective hebdomadaire de travail est supérieure à la durée légale hebdomadaire, la rémunération mensuelle due au salarié peut être calculée en multipliant la rémunération horaire par les cinquante-deux douzièmes de cette durée hebdomadaire de travail, en tenant compte des majorations de salaire correspondant aux heures supplémentaires accomplies. cependant si dans mon contrat l'annualisaiont n'est pas valable, alors les 39 non plus (pas un self service ou on prend une partie et pas le reste) le raisonnement de l'avocat est de partir des 35 heures sur la base des art 3121-27 et 3121-28 et 29: 1) compter la qté d heures de chaque semaine travaillée. si plus de 35 heures 25% dues. 2) cumuler ca à l'année. Article L3121-27 En savoir plus sur cet article... Modifié par LOI n°2016-1088 du 8 août 2016 - art. 8 (V) La durée légale de travail effectif des salariés à temps complet est fixée à trente-cinq heures par semaine. Article L3121-28 En savoir plus sur cet article... Modifié par LOI n°2016-1088 du 8 août 2016 - art. 8 (V) Toute heure accomplie au delà de la durée légale hebdomadaire ou de la durée considérée comme équivalente est une heure supplémentaire qui ouvre droit à une majoration salariale ou, le cas échéant, à un repos compensateur équivalent. Article L3121-29 En savoir plus sur cet article... Modifié par LOI n°2016-1088 du 8 août 2016 - art. 8 (V) Les heures supplémentaires se décomptent par semaine. Article L3121-30 En savoir plus sur cet article... Modifié par LOI n°2016-1088 du 8 août 2016 - art. 8 (V) Des heures supplémentaires peuvent être accomplies dans la limite d'un contingent annuel. Les heures effectuées au delà de ce contingent annuel ouvrent droit à une contrepartie obligatoire sous forme de repos. Les heures prises en compte pour le calcul du contingent annuel d'heures supplémentaires sont celles accomplies au delà de la durée légale. Les heures supplémentaires ouvrant droit au repos compensateur équivalent mentionné à l'article L. 3121-28 et celles accomplies dans les cas de travaux urgents énumérés à l'article L. 3132-4 ne s'imputent pas sur le contingent annuel d'heures supplémentaires. AUTRE PT DE VUE: les heures sup dues sur un mois somment les heures sup dues des semaines terminees elles representent apres bonification un eqv heures trav comparable à l eqv heures trav de la bonif payée effectivement. elles permettent donc de déterminer un préjudice. """ anneesdispo = list(db.bdd().iterAnneesDispo()) moisannee = list(range(1, 13)) def eqv_trv_de_sup_paye_obsolete(mois, annee): cste_h25_payee_mois = 17.33 return bonification25(cste_h25_payee_mois) def eqv_trv_de_sup_sem_paye(semaine, annee): heures_cp = getCumulHeuresCpSemaine(annee, semaine) paye_semaine = sup25_deja_paye(heures_cp) if heures_cp: phrase_cp_non_nuls = "en semaine {}, {} heures de cp ramenent les hsup payéees de 4 à {}" phrase_cp_non_nuls = phrase_cp_non_nuls.format( semaine, annee, paye_semaine) log.critical(phrase_cp_non_nuls) else: phrase_cp_nuls = "{} heures ont été payées à 25% au titre de la mens des heures de 35 à 39h" phrase_cp_nuls = phrase_cp_nuls.format(paye_semaine) log.debug(phrase_cp_nuls) return bonification25(paye_semaine) def eqv_trv_de_sup_paye(mois, annee): eqv = 0 import utilitaireDates semainesmois = list(utilitaireDates.iterSemaine(annee, mois)) for semaine in semainesmois: eqv = eqv + eqv_trv_de_sup_sem_paye(semaine, annee) phrase_debug = "eqv trv de paye vaut {} lors semaine {}" phrase_debug = phrase_debug.format( eqv_trv_de_sup_sem_paye(semaine, annee), semaine) log.debug(phrase_debug) return eqv def bonification25(heures): return heures * 1.25 def bonification50(heures): return heures * 1.50 def bonification100(heures): return heures * 2 def eqv_trv_de_sup_du(mois, annee): ## def eqv_trv_de_sup_sem_du(semaine, annee): ## e_s = eqv_trv_de_sup_sem_du_25(semaine,annee) ## + eqv_trv_de_sup_sem_du_50(semaine,annee) ## + eqv_trv_de_sup_sem_du_100(semaine,annee) ## return e_s ## def eqv_trv_de_sup_sem_du_25(semaine,annee): ## heures_effectuees_semaine = getCumulHeuresTravailleesSemaine(semaine,annee) ## return bonification25( ## heures_sup_25_effectuees(heures_effectuees_semaine) ## ) ## def eqv_trv_de_sup_sem_du_50(semaine,annee): ## heures_effectuees_semaine = getCumulHeuresTravailleesSemaine(semaine,annee) ## return bonification50( ## heures_sup_50_effectuees(heures_effectuees_semaine) ## ) ## ## def eqv_trv_de_sup_sem_du_100(semaine,annee): ## heures_effectuees_semaine = getCumulHeuresTravailleesSemaine(semaine,annee) ## return bonification100( ## heures_illegales_effectuees(heures_effectuees_semaine) ## ) eqv = 0 import utilitaireDates semainesmois = list(utilitaireDates.iterSemaine(annee, mois)) for semaine in semainesmois: eqv = eqv + eqv_trv_de_sup_sem_du(semaine, annee) phrase_debug = "eqv vaut {} lors semaine {} (sem25: {} sem50: {} sem100: {}" phrase_debug = phrase_debug.format( eqv, semaine, eqv_trv_de_sup_sem_du_25(semaine, annee), eqv_trv_de_sup_sem_du_50(semaine, annee), eqv_trv_de_sup_sem_du_100(semaine, annee)) log.info(phrase_debug) return eqv cumul_total_posit = 0 cumul_total_neg = 0 cumul_total = 0 for annee in anneesdispo: cumul_annuel_posit = 0 cumul_annuel_neg = 0 bilan_annuel = 0 for mois in moisannee: cumul_mensuel_posit = 0 cumul_mensuel_neg = 0 p = eqv_trv_de_sup_paye(mois, annee) d = eqv_trv_de_sup_du(mois, annee) phrase_datation = "pour mois {} annee {}".format(mois, annee) phrase_ok_ko = phrase_datation + "\n paye {} alors que du {}".format( p, d) phrase_cumul_mensuel = "cumul mensuel de {}".format(d - p) if p >= d: log.info(phrase_ok_ko) cumul_mensuel_neg = d - p log.info(phrase_cumul_mensuel) else: log.critical(phrase_ok_ko) cumul_mensuel_posit = d - p log.critical(phrase_cumul_mensuel) log.critical( "prejudice de {} pour le mois".format(cumul_mensuel_posit)) cumul_annuel_posit = cumul_annuel_posit + cumul_mensuel_posit cumul_annuel_neg = cumul_annuel_neg + cumul_mensuel_neg # fin de pour chaque mois de l'année # debut de pour chaque année log.critical( "cumul_annuel_posit (le seul à prendre en ocmpte puisque les heures sup sont comptees à la semaine et payees au mois. on s arrete donc au décompte des mois positifs): {}" .format(cumul_annuel_posit)) log.info("cumul_annuel_neg : {}".format(cumul_annuel_neg)) log.critical("prejudice annuel (prejudice si posit) {}".format( cumul_annuel_posit + cumul_annuel_neg)) cumul_total_posit = cumul_annuel_posit + cumul_total_posit cumul_total_neg = cumul_annuel_neg + cumul_total_neg # fin de pour chaque annee #debut de decompte final log.critical( "pour action: cumul total posit : {}".format(cumul_total_posit)) log.info("pour info : cumul total neg: {}".format(cumul_total_neg)) cumul_total = cumul_total_posit + cumul_total_neg log.critical("prejudice si chiffre positif: {}".format(cumul_total))
async def save_csv( stream, encoding='utf8', new_activity_delay=5 * 3600, timezone='Europe/Paris' ): now = pendulum.utcnow().timestamp() vessel_activities = [] loop = asyncio.get_event_loop() try: text = await stream.read(10_000_000) text = text.decode(encoding=encoding, errors='replace') # Check if we have several vessels with the same name duplicate_names = {} for row in DictReaderStrip(io.StringIO(text)): name = get_text_field(row, 'NOMNAVIRE') duplicate_names[name] = duplicate_names.get(name, 0) + 1 duplicate_names = { name: count for name, count in duplicate_names.items() if count > 1 } all_call_signs = set() for i, row in enumerate(DictReaderStrip(io.StringIO(text))): vessel_activity_dict = {'timestamp': now} # Example of row: # OrderedDict([ # ('MOUVEMENT', 'E'), # ('NUMERODEMANDE', '198678'), # ('NOMNAVIRE', 'BUTES'), # ('LONGUEUR', '87.84'), # ('TIRANTEAU', '3.3'), # ('POSTEAQUAI', ''), # ('PROVENANCE', 'Warrenpoint'), # ('DESTINATION', 'Sevilla'), # ('DATEDEBUTMVTPREVUE', '27/05/2016 00:01'), # ('NUMEROSOFI', '194323'), # ('NAVIREDANGEREUX', '0'), # ('CALLSIGN', 'A8UR6'), # ('TYPEVTS', '4'), # ('NUMEROLLOYD', '9409637'), # ('BONPOURMOUVEMENT', '0'), # ('OBSERVATIONS', '') # ]) call_sign = get_text_field(row, 'CALLSIGN') # duplicate call sign. There is nothing we can do but # ignore it if call_sign and call_sign in all_call_signs: log.error(f'Duplicate call sign {call_sign}') continue all_call_signs.add(call_sign) draft = get_num_field(row, 'TIRANTEAU') length = get_num_field(row, 'LONGUEUR') name = get_text_field(row, 'NOMNAVIRE') # no name and no call sign: nothing we can do if not call_sign and not name: continue def get_vessel(call_sign, name, draft, length, duplicates): """ Try to get a matching vessel """ # get the vessel by the unique name if call_sign: try: vessel = Vessel.objects.get(call_sign=call_sign) log.info(f'Known vessel: {vessel}') return vessel except Vessel.DoesNotExist: vessel = Vessel.objects.create( draft=draft, length=length, name=name, call_sign=call_sign ) log.info(f'New vessel: {vessel}') return vessel # failling that, try to get the boat by the name. if name not in duplicates: # from the boat table try: return Vessel.objects.get(name=name) except Vessel.DoesNotExist: log.info(f'New vessel: {vessel}') return Vessel.objects.create( draft=draft, length=length, name=name, all_sign=call_sign ) # or in case there are several known vessels with that # name, from the most recent unique activity with this # boat except Vessel.MultipleObjectsReturned: activity = VesselActivity.get_recent_activity_for( name=name ) if activity: log.info(f'Known vessel: {activity.vessel}') return activity.vessel return None vessel = await loop.run_in_executor( None, get_vessel, call_sign, name, draft, length, duplicate_names ) if not vessel: log.error(f'Anonymous vessel record: {row}') # If we can find a vessel, we update its values else: before = repr(vessel) if draft and vessel.draft != draft: vessel.draft = draft if length and vessel.length != length: vessel.length = length if name and vessel.name != name: vessel.name = name after = repr(vessel) if before != after: await loop.run_in_executor(None, vessel.save) log.info(f'Vessel data has changed to: {vessel!r}') # the serialized vessel activity is either from db # or, lacking from a known vessel, constructed from the # date from the csv vessel_activity = None if vessel: vessel_activity_dict['anonymous'] = False vessel_activity_dict.update(vessel.to_dict()) vessel_activity = await loop.run_in_executor( None, vessel.get_latest_activity, new_activity_delay ) else: vessel_activity_dict['anonymous'] = True vessel_activity_dict['name'] = name vessel_activity_dict['length'] = length vessel_activity_dict['draft'] = draft vessel_activity_dict['call_sign'] = call_sign if not vessel_activity: vessel_activity = VesselActivity(vessel=vessel) activity_type = get_text_field(row, 'MOUVEMENT') vessel_activity.type = ({ 'S': 'departing', 'E': 'incomming', 'D': 'shifting' }).get(activity_type) vessel_activity.good_to_go = bool(get_num_field( row, 'NOMBREDETUGS', cast=int, default=0) ) incoming_from = get_text_field(row, 'PROVENANCE') vessel_activity.incoming_from = incoming_from leaving_to = get_text_field(row, 'DESTINATION') vessel_activity.leaving_to = leaving_to berth = get_text_field(row, 'POSTEAQUAI') vessel_activity.berth = berth services = get_text_field(row, 'DEBUTDESOPERATIONSCOMMERCIALES') vessel_activity.services = services or None vessel_activity.tugs = get_num_field(row, 'NOMBREDETUGS', int) try: date = get_text_field(row, 'DATEDEBUTMVTPREVUE') or '' sirene_time_estimate = pendulum.parse( date, tz=timezone ) except (ValueError, AttributeError): sirene_time_estimate = None vessel_activity.sirene_time_estimate = sirene_time_estimate obs = get_text_field(row, 'OBSERVATIONS') vessel_activity.sirene_observation = obs vessel_activity.dangerous_materials = get_num_field( row, 'NAVIREDANGEREUX', int ) # we only create a vessel activity if we have a named vessel # since we could not get it back later anyway with it if vessel: vessel_activity.save() # serialized vessel activity data is completed with previous # data from db and the one found in the csv plus a few # adjustment for the front end vessel_activity_dict.update(vessel_activity.to_dict(timezone)) if vessel_activity_dict['anonymous']: # generate a fake unique id for this name duplicate_index = duplicate_names.setdefault(name, 0) - 1 unique_name = f"{name}/{duplicate_index}".encode('utf8') fake_id = hashlib.sha256(unique_name).hexdigest() duplicate_names[name] -= 1 vessel_activity_dict['id'] = fake_id vessel_activities.append(vessel_activity_dict) except Exception as e: log.exception("Unable to read csv file.") # clear all activities older than 6 months def clear_old_activities(): six_month_ago = pendulum.utcnow().add(months=-6) res = VesselActivity.objects.filter(modified__lt=six_month_ago).delete() log.info(f'Deleted old activities: {res}') await loop.run_in_executor(None, clear_old_activities) return vessel_activities