from __future__ import print_function from odoo import models, fields, api,_ from odoo.exceptions import UserError, ValidationError,Warning from psycopg2 import sql, DatabaseError from pytz import timezone from odoo.addons.http_routing.models.ir_http import slugify from werkzeug import utils import os.path from google.oauth2 import service_account from googleapiclient.discovery import build from datetime import date,datetime, timedelta import json import babel import babel.dates import time import sib_api_v3_sdk from sib_api_v3_sdk.rest import ApiException from pprint import pprint import logging _logger = logging.getLogger(__name__) # IF YOU MODIFY THE SCOPE DELETE THE TOKEN.TXT FILE SCOPES = ['https://www.googleapis.com/auth/calendar'] GOOGLE_ACCOUNT_FILE = '/usr/lib/python3/dist-packages/odoo/kalachakra_module/kalachakra/models/creds.json' # You should make it an environment variable SUBJECT = 'kalachakra-srv@kalachakra-351613.iam.gserviceaccount.com' client_tz = timezone('Europe/Paris') class EventTagCategory(models.Model): _inherit = "event.tag.category" _description = 'Event tag category' calendar_id=fields.Char('google calendar id') hidden=fields.Boolean('hidden tag') class KalachakraEvent(models.Model): _inherit = "event.event" _description = 'Event' address_id=fields.Many2one(domain="[('is_company', '=', True)]") calendar_id=fields.Char('calendar id') calendar_event_id=fields.Char('event id') free_participation=fields.Boolean('Free participation') participation_product_id=fields.Many2one('product.product',string='participation product') participation_standard_price=fields.Monetary('Standard Price',currency_field='currency_id') participation_member_price=fields.Monetary('Member price',currency_field='currency_id') participation_super_member_price=fields.Monetary('Super member price',currency_field='currency_id') subscription_product_id=fields.Many2one('product.product',string='subscription product') subscription_standard_price=fields.Monetary('Standard Price',currency_field='currency_id') subscription_member_price=fields.Monetary('Member price',currency_field='currency_id') subscription_super_member_price=fields.Monetary('Super member price',currency_field='currency_id') recurring_event=fields.Boolean('Recurring event') recurring_event_newsletter_id=fields.Many2one('mailing.list',string='Recurring event Newsletter') online_event=fields.Boolean('Online event') onthespot_event=fields.Boolean('On the spot event', default=True) online_only=fields.Boolean('Online only') online_link=fields.Char('link') online_id=fields.Char('id') online_password=fields.Char('password',default='123456') duration=fields.Integer('duration', compute="_compute_duration") frequency=fields.Selection(string='Frequency',selection=[('daily', 'Daily'), ('weekly', 'Weekly')]) weekly_day=fields.Selection(string='Weekly day',selection=[('monday', 'Monday'),('tuesday', 'Tuesday'),('wednesday', 'Wednesday'),('thursday', 'Thursday'),('friday', 'Friday'),('saturday', 'Saturday'),('sunday', 'Sunday') ]) end_generation_date=fields.Datetime('End generation date') generated_events=fields.Boolean('generated_ events', compute='_compute_generated_events',copy=False) #for event generated from a parent event parent_event_id=fields.Many2one('event.event',string='parent event', readonly=True,copy=False) find_out_more_link=fields.Char('Find out more link') city=fields.Char('city', related="address_id.city") no_online_payment=fields.Boolean('no online payment') no_onthespot_payment=fields.Boolean('no on the spot payment') headphone_option=fields.Boolean('headphone option') payment_required=fields.Boolean('payment required to valid a registering') display_invoice=fields.Boolean('display invoice') teacher_picto=fields.Binary('Teacher picto') pre_registration_required=fields.Boolean('pre registration required') @api.depends('seats_unconfirmed', 'seats_reserved', 'seats_used') def _compute_seats_expected(self): for event in self: i=0 for reg_id in event.registration_ids: reg=self.env['event.registration'].search([('id','=',int(reg_id))]) if reg.state in ('draft','open','done'):i=i+1 event.seats_expected=i def bulk_change_date_events(self,name,date_start_str,hour): date_format_str = '%d/%m/%Y %H:%M:%S' date_start=datetime.strptime(date_start_str, date_format_str) events=self.env['event.event'].search([('name','=', name),('date_begin','>',date_start)]) if events: for ev in events: ev.date_begin=ev.date_begin+timedelta(hours=1) ev.date_end=ev.date_end+timedelta(hours=1) @api.returns('self', lambda value: value.id) def copy(self, default=None): stage_new=self.env['event.stage'].search([('name','=','Nouveau')],limit=1) default = dict(default or {}) default['stage_id'] = stage_new.id return super(KalachakraEvent, self).copy(default) # def write(self,vals): # res = super(KalachakraEvent, self).write(vals) # #mise à jour du calendrier google si # #modification date de début ou date de fin # #modification du nom de l'événement # # if self.calendar_event_id: # # if vals.get('date_begin') or vals.get('date_end') or vals.get('name'): # # self.update_event_google_agenda() # #s'il existe des événements liés à celui-ci, mise à jour de ceux-ci # #uniquement sur les champs suivants : nom, liste de diffusion, lien zoom, type de participation, lien en savoir plus # _logger.error('id='+str(int(self.id))) # if not self.parent_event_id: # child_events=self.env['event.event'].search([('parent_event_id','=', int(self.id))]) # if child_events: # #raise Warning(self.event_mail_ids) # for child_event in child_events: # child_event.event_mail_ids=self.event_mail_ids # _logger.error(str(child_event.date_begin)+'='+str(child_event.event_mail_ids)) # child_event.name=self.name # child_event.event_type_id=self.event_type_id # child_event.recurring_event_newsletter_id=self.recurring_event_newsletter_id # child_event.find_out_more_link=self.find_out_more_link # child_event.auto_confirm=self.auto_confirm # child_event.seats_limited=self.seats_limited # child_event.seats_max=self.seats_max # child_event.user_id=self.user_id # child_event.address_id=self.address_id # child_event.online_only=self.online_only # child_event.no_online_payment=self.no_online_payment # child_event.online_event=self.online_event # child_event.online_link=self.online_link # child_event.online_id=self.online_id # child_event.online_password=self.online_password # child_event.event_logo=self.event_logo # child_event.cover_properties=self.cover_properties # child_event.tag_ids=self.tag_ids # child_event.description=self.description # child_event.participation_product_id=self.participation_product_id # child_event.participation_standard_price=self.participation_standard_price # child_event.participation_member_price=self.participation_member_price # child_event.participation_super_member_price=self.participation_super_member_price def _compute_generated_events(self): for rec in self: rec.generated_events=False evt=rec.env['event.event'].search([('parent_event_id','=', rec.id)]) if evt:rec.generated_events=True def update_generated_events(self): for rec in self: evt=self.env['event.event'].search([('parent_event_id','=', rec.id)]) if evt: for child_event in evt: #suppression des mails child_event.write({'event_mail_ids':[(5)]}) for mail_id in rec.event_mail_ids: vals={} vals["notification_type"]=mail_id.notification_type vals["template_id"]=int(mail_id.template_id) vals["sms_template_id"]=int(mail_id.sms_template_id) vals["interval_nbr"]=mail_id.interval_nbr vals["interval_unit"]=mail_id.interval_unit vals["interval_type"]=mail_id.interval_type vals["event_id"]=int(child_event.id) #_logger.error(str(vals)) child_event.write({'event_mail_ids':[(0,0,vals)]}) child_event.name=rec.name #_logger.error(str(child_event.event_mail_ids)) #child_event.event_type_id=rec.event_type_id child_event.recurring_event_newsletter_id=rec.recurring_event_newsletter_id child_event.find_out_more_link=rec.find_out_more_link child_event.auto_confirm=rec.auto_confirm child_event.seats_limited=rec.seats_limited child_event.seats_max=rec.seats_max child_event.user_id=rec.user_id child_event.address_id=rec.address_id child_event.online_only=rec.online_only child_event.no_online_payment=rec.no_online_payment child_event.online_event=rec.online_event child_event.online_link=rec.online_link child_event.online_id=rec.online_id child_event.online_password=rec.online_password child_event.teacher_picto=rec.teacher_picto child_event.event_logo=rec.event_logo child_event.cover_properties=rec.cover_properties child_event.tag_ids=rec.tag_ids child_event.description=rec.description child_event.participation_product_id=rec.participation_product_id child_event.participation_standard_price=rec.participation_standard_price child_event.participation_member_price=rec.participation_member_price child_event.participation_super_member_price=rec.participation_super_member_price def remove_generated_events(self): evt=self.env['event.event'].search([('parent_event_id','=', self.id)]) if evt: for e in evt: if e.calendar_id and e.calendar_event_id: e.remove_event_from_google_agenda() # reg=self.env['event.registration'].search([('event_id','=', int(e.id))]) # if reg: # return str(e.date_begin) e.unlink() def remove_generated_calendar_events(self): evt=self.env['event.event'].search([('parent_event_id','=', self.id)]) if evt: for e in evt: if e.calendar_id and e.calendar_event_id: e.remove_event_from_google_agenda() def generate_calendar_events(self): self.remove_generated_calendar_events() evt=self.env['event.event'].search([('parent_event_id','=', self.id)]) if evt: for e in evt: e.add_event_to_google_agenda() def generate_events(self): if self.duration>1 :raise Warning('action cancelled: the duration f the event is more than 1 days !') if self.end_generation_date200:raise Warning('action cancelled: the duration f the event is more than 6 months !') if self.frequency=='daily':d=1 if self.frequency=='weekly':d=7 start_date_event=self.date_begin+timedelta(days=d) end_date_event=self.date_end+timedelta(days=d) # end_date_event=end_date_event.astimezone(client_tz) # start_date_event=start_date_event.astimezone(client_tz) end_generation_date=self.end_generation_date i=1 while start_date_event<=end_generation_date: if not start_date_event.weekday() in (5,6): #avant de dupliquer l'événement on regarde si celui-ci n'a pas déjà été crée ! evt=self.env['event.event'].search([('date_begin','=',start_date_event),('parent_event_id','=', int(self.id))]) if evt: raise Warning('action cancelled: event already generated with this date :'+ str(start_date_event)) dup=self._create_event(start_date_event,end_date_event) #dup.add_event_to_google_agenda() old_end_date_event_tz=end_date_event.astimezone(client_tz) old_start_date_event_tz=start_date_event.astimezone(client_tz) end_date_event=end_date_event+timedelta(days=d) start_date_event=start_date_event+timedelta(days=d) # _logger.error('hour1='+str(start_date_event)) end_date_event_tz=end_date_event.astimezone(client_tz) start_date_event_tz=start_date_event.astimezone(client_tz) diff_start=int(start_date_event_tz.hour)-int(old_start_date_event_tz.hour) #start_date_event=start_date_event+timedelta(hours=diff_start) diff_end=int(end_date_event_tz.hour)-int(old_end_date_event_tz.hour) #end_date_event=end_date_event+timedelta(hours=diff_end) if diff_start!=0: new_hour_start=start_date_event.hour-diff_start new_hour_end=end_date_event.hour-diff_start _logger.error('new_hour_start='+str(new_hour_start)) start_date_event=start_date_event.replace(hour=new_hour_start) end_date_event=end_date_event.replace(hour=new_hour_end) #_logger.error('start_date_event='+str(start_date_event)) #_logger.error('diff_start='+str(diff_start)) # dst_offset_diff_end = old_end_date_event.dst() - end_date_event.dst() # end_date_event=end_date_event+dst_offset_diff_end # dst_offset_diff_start = old_start_date_event.dst() - start_date_event.dst() # start_date_event=start_date_event+dst_offset_diff_start i=i+1 def _create_event(self,start_date_event,end_date_event): vals={} vals['name']=self.name vals['date_begin']=start_date_event vals['date_end']=end_date_event vals['description']=self.description vals['tag_ids']=self.tag_ids vals['event_logo']=self.event_logo vals['is_published']=True vals['free_participation']=self.free_participation vals['participation_product_id']=int(self.participation_product_id) vals['participation_standard_price']=self.participation_standard_price vals['participation_member_price']=self.participation_member_price vals['participation_super_member_price']=self.participation_super_member_price vals['subscription_product_id']=int(self.subscription_product_id) vals['recurring_event']=self.recurring_event vals['recurring_event_newsletter_id']=int(self.recurring_event_newsletter_id) vals['online_event']=self.online_event vals['online_link']=self.online_link vals['online_id']=self.online_id vals['no_online_payment']=self.no_online_payment vals['online_password']=self.online_password vals['parent_event_id']=int(self.id) vals['cover_properties']=self.cover_properties ev=self.env['event.event'].create(vals) for mail_id in self.event_mail_ids: vals={} vals["notification_type"]=mail_id.notification_type vals["template_id"]=int(mail_id.template_id) vals["sms_template_id"]=int(mail_id.sms_template_id) vals["interval_nbr"]=mail_id.interval_nbr vals["interval_unit"]=mail_id.interval_unit vals["interval_type"]=mail_id.interval_type vals["event_id"]=int(ev.id) #_logger.error(str(vals)) ev.write({'event_mail_ids':[(0,0,vals)]}) return ev def _compute_duration(self): for rec in self: if rec.date_end and rec.date_begin: b_date = date(rec.date_begin.year, rec.date_begin.month, rec.date_begin.day) e_date = date(rec.date_end.year, rec.date_end.month, rec.date_end.day) duration = e_date-b_date rec.duration = duration.days+1 else: rec.duration=0 booking_event = fields.Boolean(string="Booking event", tracking=True) question_ids=fields.One2many( 'event.question', 'event_id', string='Questions' ) booking_option_ids=fields.One2many('booking.option','event_id','booking options') booking_price=fields.Monetary('Price',currency_field='currency_id') booking_member_price=fields.Monetary('Member price',currency_field='currency_id') booking_super_member_price=fields.Monetary('Super member price',currency_field='currency_id') booking_down_payment=fields.Monetary('Down payment',default=50,currency_field='currency_id') booking_product_id=fields.Many2one('product.product',string='booking product',domain="[('booking_product','=','True')]") individual_day_price=fields.Monetary('Day price',currency_field='currency_id') individual_member_day_price=fields.Monetary('Member day price',currency_field='currency_id') individual_super_member_day_price=fields.Monetary('Super member Day price',currency_field='currency_id') individual_week_price=fields.Monetary('Week price',currency_field='currency_id') individual_member_week_price=fields.Monetary('Member week price',currency_field='currency_id') individual_super_member_week_price=fields.Monetary('Super member Week price',currency_field='currency_id') individual_2weeks_price=fields.Monetary('2 weeks price',currency_field='currency_id') individual_member_2weeks_price=fields.Monetary('Member 2 weeks price',currency_field='currency_id') individual_super_member_2weeks_price=fields.Monetary('Super member 2 Weeks price',currency_field='currency_id') individual_3weeks_price=fields.Monetary('3 weeks price',currency_field='currency_id') individual_member_3weeks_price=fields.Monetary('Member 3 weeks price',currency_field='currency_id') individual_super_member_3weeks_price=fields.Monetary('Super member 3 Weeks price',currency_field='currency_id') individual_month_price=fields.Monetary('1 month price',currency_field='currency_id') individual_member_month_price=fields.Monetary('1 month price',currency_field='currency_id') individual_super_member_month_price=fields.Monetary('1 month price',currency_field='currency_id') individual_booking_html_introduction=fields.Html('Introduction text') individual_booking_html_introduction2=fields.Html('Introduction text2') @api.onchange('booking_event') def onchange_bookin_event(self): if self.booking_event: booking_option=self.env['product.product'].search([('booking_option_product','=',True)]) if booking_option: #on supprime les options existantes: self.booking_option_ids=False #on rappatrie les options for option in booking_option: id_prd=int(option.id) prd=self.env['product.product'].sudo().search([('id','=',id_prd)]) vals={} #calcul du prix en fonction du sejour,nuité, ou jour price=option.list_price if prd.price_per=='day': price=option.list_price*self.duration if prd.price_per=='night': price=option.list_price*(self.duration-1) vals['event_id']=self._origin.id vals['booking_option_id']=option.id vals['booking_option_price']=price vals['booking_option_member_price']=price vals['booking_option_super_member_price']=price vals['currency_id']=option.currency_id self.booking_option_ids = [(0, 0, vals)] @api.model def _default_currency(self): company = self.env['res.company']._company_default_get( 'event.event') return company.currency_id currency_id = fields.Many2one( 'res.currency', string='Currency', required=True, track_visibility='onchange', ondelete='restrict', default=_default_currency ) teaching_day_ids=fields.One2many( 'event.teaching.day', 'event_id', string='teaching days' ) teaching_html_content_records=fields.Html('teaching content records') teaching_html_content_records_published=fields.Boolean('publish html content') display_english_records=fields.Boolean('display english records',default=True) # only_audio_records=fields.Boolean('only audio records',default=False) # only_video_records=fields.Boolean('only video records',default=False) def add_registration_to_mailing_list(self): registrants=self.env['event.registration'].search([('event_id','=',int(self.id))]) if registrants: for reg in registrants: if reg.event_id.recurring_event_newsletter_id: mailing_contact=self.env['mailing.contact'].sudo().search([('email','=',reg.partner_id.email)],limit=1) #si le contact n'existe pas comme mailing contact, création if not mailing_contact: vals={} vals['email']=reg.partner_id.email vals['name']=reg.partner_id.name vals['title_id']=int(reg.partner_id.title) vals['country_id']=int(reg.partner_id.country_id) vals['list_ids']=[(4,int(reg.event_id.recurring_event_newsletter_id))] mailing_contact=self.env['mailing.contact'].sudo().create(vals) else: #si le contact existe, si la liste de diffusion n'est pas lié au contact, on l'ajoute if not reg.event_id.recurring_event_newsletter_id in mailing_contact.list_ids: mailing_contact.sudo().write({'list_ids':[(4,int(reg.event_id.recurring_event_newsletter_id))]}) def add_event_to_google_agenda(self): credentials = service_account.Credentials.from_service_account_file(GOOGLE_ACCOUNT_FILE, scopes=SCOPES) delegated_credentials = credentials.with_subject(SUBJECT) service = build('calendar', 'v3', credentials=delegated_credentials) start=self.date_begin.astimezone(timezone('Europe/Paris')) end=self.date_end.astimezone(timezone('Europe/Paris')) start=start.isoformat() end=end.isoformat() diff_date=self.date_end-self.date_begin if diff_date.days>0: date_param=self.date_begin.strftime('%Y-%m-%d')+'-'+self.date_end.strftime('%Y-%m-%d')+'-'+str(self.id) else: date_param=self.date_begin.strftime('%Y-%m-%d')+'-'+str(self.id) name_event_slug=slugify(self.name,max_length=1024, path=True) name_param=name_event_slug.replace(' ','-') name_param=name_param.replace('\'','-') description='cliquer ici pour s\'inscrire' #str(self.date_begin.year())+'-'+str(self.date_begin.month())+'-'+self.date_begin.day() body={"summary": self.name, "description": description, "start": {"dateTime": start, "timeZone": 'Europe/Paris'}, "end": {"dateTime": end, "timeZone": 'Europe/Paris'}, } #recherche de l'id calendar lié à l'étiquette de l'événément calendar_find=False for tag in self.tag_ids: if tag.category_id.calendar_id: calendar_find=True event = service.events().insert(calendarId=tag.category_id.calendar_id, body=body).execute() self.calendar_event_id=event['id'] self.calendar_id=tag.category_id.calendar_id if not calendar_find: raise Warning('operation cancelled: no calendar id find, please selected a label wih category with a calendar_id') def remove_event_from_google_agenda(self): credentials = service_account.Credentials.from_service_account_file(GOOGLE_ACCOUNT_FILE, scopes=SCOPES) delegated_credentials = credentials.with_subject(SUBJECT) service = build('calendar', 'v3', credentials=delegated_credentials) event = service.events().get(calendarId=self.calendar_id, eventId=self.calendar_event_id).execute() #raise Warning(json.dumps(event, indent = 4) ) if event['status'] != "cancelled" : service.events().delete(calendarId=self.calendar_id, eventId=self.calendar_event_id).execute() self.calendar_event_id=False self.calendar_id=False def update_event_google_agenda(self): credentials = service_account.Credentials.from_service_account_file(GOOGLE_ACCOUNT_FILE, scopes=SCOPES) delegated_credentials = credentials.with_subject(SUBJECT) service = build('calendar', 'v3', credentials=delegated_credentials) start=self.date_begin.astimezone(timezone('Europe/Paris')) end=self.date_end.astimezone(timezone('Europe/Paris')) start=start.isoformat() end=end.isoformat() diff_date=self.date_end-self.date_begin if diff_date.days>0: date_param=self.date_begin.strftime('%Y-%m-%d')+'-'+self.date_end.strftime('%Y-%m-%d')+'-'+str(self.id) else: date_param=self.date_begin.strftime('%Y-%m-%d')+'-'+str(self.id) name_param=self.name.replace(' ','-') name_param=name_param.replace('\'','-') description='cliquer ici pour s\'inscrire' #str(self.date_begin.year())+'-'+str(self.date_begin.month())+'-'+self.date_begin.day() body={"summary": self.name, "description": description, "start": {"dateTime": start, "timeZone": 'Europe/Paris'}, "end": {"dateTime": end, "timeZone": 'Europe/Paris'}, } #event = service.events().get(calendarId=self.calendar_id, eventId=self.calendar_event_id).execute() updated_event = service.events().update(calendarId=self.calendar_id, eventId=self.calendar_event_id, body=body).execute() def update_date_event(self,event_name,date_begin,hour_begin,hour_end): hour_begin=hour_begin-2 hour_end=hour_end-2 evt=self.env['event.event'].search([('name','=',event_name),('date_begin','>',date_begin)]) for e in evt: vals={} vals['date_begin']=e.date_begin.replace(hour=hour_begin) vals['date_end']=e.date_end.replace(hour=hour_end) e.write(vals) class EventTag(models.Model): _inherit = "event.tag" _description = 'Event tag' hidden=fields.Boolean('hidden tag')