Compare commits
2 Commits
35fee0f473
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| c4b68400b3 | |||
| d7df194a12 |
101
assets/ics2csv.py
Normal file
101
assets/ics2csv.py
Normal file
@@ -0,0 +1,101 @@
|
||||
import icalendar
|
||||
|
||||
def DumpComponentHeader(file, component):
|
||||
fields = []
|
||||
inclusiveUnitCounter = 0
|
||||
fields += map(lambda x: x + ' - required', component.required)
|
||||
fields += map(lambda x: x + ' - singletons', component.singletons)
|
||||
for units in component.inclusive:
|
||||
fields += map(lambda x: x + ' - inclusive{}'.format(inclusiveUnitCounter), units)
|
||||
inclusiveUnitCounter += 1
|
||||
fields += ('exclusive - name', 'exclusive - data')
|
||||
fields += ('multiple', )
|
||||
file.write(','.join(fields))
|
||||
file.write('\n')
|
||||
|
||||
def DumpComponentData(file, component):
|
||||
data = []
|
||||
gotten_instance = None
|
||||
|
||||
for item in component.required:
|
||||
gotten_instance = component.get(item)
|
||||
if gotten_instance is not None:
|
||||
data.append(AdvancedFormater(gotten_instance))
|
||||
else:
|
||||
data.append('')
|
||||
for item in component.singletons:
|
||||
gotten_instance = component.get(item)
|
||||
if gotten_instance is not None:
|
||||
data.append(AdvancedFormater(gotten_instance))
|
||||
else:
|
||||
data.append('')
|
||||
for units in component.inclusive:
|
||||
for item in units:
|
||||
gotten_instance = component.get(item)
|
||||
if gotten_instance is not None:
|
||||
data.append(AdvancedFormater(gotten_instance))
|
||||
else:
|
||||
data.append('')
|
||||
|
||||
gotten_name = ""
|
||||
gotten_data = ""
|
||||
for item in component.exclusive:
|
||||
gotten_instance = component.get(item)
|
||||
if gotten_instance is not None:
|
||||
gotten_name = item
|
||||
gotten_data = AdvancedFormater(gotten_instance)
|
||||
break
|
||||
data.append(gotten_name)
|
||||
data.append(gotten_data)
|
||||
|
||||
for item in component.multiple:
|
||||
gotten_instance = component.get(item)
|
||||
if gotten_instance is not None:
|
||||
data.append('- {} -'.format(item))
|
||||
data.append(AdvancedFormater(gotten_instance))
|
||||
else:
|
||||
data.append('')
|
||||
|
||||
file.write(','.join(data))
|
||||
file.write('\n')
|
||||
|
||||
def AdvancedFormater(data):
|
||||
if isinstance(data, icalendar.prop.vDDDTypes):
|
||||
return str(data.dt)
|
||||
else:
|
||||
return str(data)
|
||||
|
||||
# read file
|
||||
icsFile = open('test.ics', 'rb')
|
||||
cal = icalendar.Calendar.from_ical(icsFile.read())
|
||||
icsFile.close()
|
||||
|
||||
# analyse file
|
||||
csvEvent = open('event.csv', 'w')
|
||||
csvEventHeader = False
|
||||
csvAlarm = open('alarm.csv', 'w')
|
||||
csvAlarmHeader = False
|
||||
|
||||
eventCount = 0
|
||||
alarmCount = 0
|
||||
miscCount = 0
|
||||
for component in cal.walk():
|
||||
if component.name == 'VEVENT':
|
||||
eventCount += 1
|
||||
if not csvEventHeader:
|
||||
DumpComponentHeader(csvEvent, component)
|
||||
csvEventHeader = True
|
||||
DumpComponentData(csvEvent, component)
|
||||
elif component.name == 'VALARM':
|
||||
alarmCount += 1
|
||||
if not csvAlarmHeader:
|
||||
DumpComponentHeader(csvAlarm, component)
|
||||
csvAlarmHeader = True
|
||||
DumpComponentData(csvAlarm, component)
|
||||
else:
|
||||
miscCount += 1
|
||||
|
||||
|
||||
csvEvent.close()
|
||||
csvAlarm.close()
|
||||
print('Event count: {}\nAlarm count: {}\nMisc count: {}'.format(eventCount, alarmCount, miscCount))
|
||||
156
assets/ics_converter.py
Normal file
156
assets/ics_converter.py
Normal file
@@ -0,0 +1,156 @@
|
||||
import icalendar
|
||||
import sys
|
||||
import os
|
||||
import database
|
||||
import json
|
||||
import datetime
|
||||
import dt as localdt
|
||||
|
||||
def AdvancedDatetTimeGet(dt, isStartDateTime):
|
||||
if isinstance(dt, datetime.datetime):
|
||||
gottenDatetime = int(dt.timestamp() / 60)
|
||||
elif isinstance(dt, datetime.date):
|
||||
gottenDatetime = int(datetime.datetime(
|
||||
dt.year,
|
||||
dt.month,
|
||||
dt.day,
|
||||
0 if isStartDateTime else 23,
|
||||
0 if isStartDateTime else 59,
|
||||
0 if isStartDateTime else 59,
|
||||
0, tzinfo=LOCAL_TZ
|
||||
).timestamp() / 60)
|
||||
else:
|
||||
raise Exception('Unexpected data')
|
||||
|
||||
timezoneOffset = LOCAL_UTC_OFFSET
|
||||
return (gottenDatetime, timezoneOffset)
|
||||
|
||||
def AdvancedDateTimeAnalyser(component):
|
||||
startDatetimeRef = component.get('DTSTART').dt
|
||||
(startDatetime, timezoneOffset) = AdvancedDatetTimeGet(startDatetimeRef, True)
|
||||
|
||||
if component.get('DTEND') is not None:
|
||||
(endDatetime, _) = AdvancedDatetTimeGet(startDatetimeRef, False)
|
||||
elif component.get('DURATION') is not None:
|
||||
endDurationRef = component.get('DURATION').dt
|
||||
if isinstance(endDurationRef, datetime.timedelta):
|
||||
endDatetime = startDatetime + int(endDurationRef.total_seconds() / 60)
|
||||
else:
|
||||
raise Exception('Unexpected data')
|
||||
else:
|
||||
raise Exception('Unexpected data')
|
||||
|
||||
return (startDatetime, endDatetime, timezoneOffset)
|
||||
|
||||
def LoopRulesConverter(component):
|
||||
jsonData = component.get('RRULE')
|
||||
if jsonData is None:
|
||||
return ""
|
||||
|
||||
loopRules = ""
|
||||
loopStopRules = ""
|
||||
freq = jsonData.get('FREQ')[0]
|
||||
if freq == 'MONTHLY':
|
||||
loopRules = 'MSA{}'.format(str(jsonData.get('INTERVAL')[0]))
|
||||
elif freq == 'WEEKLY':
|
||||
occupiedWeek = [False, ] * 7
|
||||
for item in jsonData.get('BYDAY'):
|
||||
occupiedWeek[WEEK_DICT[item]] = True
|
||||
loopRules = 'W{}{}'.format(
|
||||
''.join(map(lambda x: 'T' if x else 'F', occupiedWeek)),
|
||||
str(jsonData.get('INTERVAL')[0])
|
||||
)
|
||||
elif freq == 'YEARLY':
|
||||
loopRules = 'YS{}'.format(str(jsonData.get('INTERVAL')[0]))
|
||||
else:
|
||||
raise Exception('Unexpected data')
|
||||
|
||||
if jsonData.get('COUNT') is not None:
|
||||
loopStopRules = 'T{}'.format(str(jsonData.get('COUNT')[0]))
|
||||
else:
|
||||
loopStopRules = 'F'
|
||||
|
||||
return loopRules + '-' + loopStopRules
|
||||
|
||||
# ============================ read args
|
||||
icsFilePath = sys.argv[1]
|
||||
if not os.path.isfile(icsFilePath):
|
||||
print('Fail to load ics file')
|
||||
sys.exit(1)
|
||||
|
||||
# read file
|
||||
icsFile = open(icsFilePath, 'rb')
|
||||
cal = icalendar.Calendar.from_ical(icsFile.read())
|
||||
icsFile.close()
|
||||
|
||||
# ============================ init const
|
||||
utfOffset = float(input('Input this ics file\'s utc offset (time unit: hour)>'))
|
||||
LOCAL_UTC_OFFSET = int(utfOffset * 60)
|
||||
LOCAL_TZ = localdt.UTCTimezone(LOCAL_UTC_OFFSET)
|
||||
WEEK_DICT = {
|
||||
"SU": 6, "MO": 0, "TU": 1, "WE": 2, "TH": 3, "FR": 4, "SA": 5,
|
||||
}
|
||||
|
||||
# ============================ pick database
|
||||
|
||||
db = database.CalendarDatabase()
|
||||
db.open()
|
||||
username = input('Input username >')
|
||||
password = input('Input password >')
|
||||
(status, error, token) = db.common_webLogin(username, password, 'Python backend', '127.0.0.1')
|
||||
if not status:
|
||||
print('Fail to login.')
|
||||
sys.exit(1)
|
||||
(status, error, collectionList) = db.collection_getFullOwn(token)
|
||||
if not status:
|
||||
print('Database return an error')
|
||||
sys.exit(1)
|
||||
print('Pick a collection to insert imported events')
|
||||
counter = 0
|
||||
for i in collectionList:
|
||||
print('{}\t{}'.format(counter, i[1]))
|
||||
counter += 1
|
||||
pickedIndex = int(input())
|
||||
collectionUuid = collectionList[pickedIndex][0]
|
||||
|
||||
# ============================ analyse file
|
||||
eventCount = 0
|
||||
allCount = 0
|
||||
for component in cal.walk():
|
||||
allCount += 1
|
||||
# only import event chunk
|
||||
if component.name == 'VEVENT':
|
||||
eventCount += 1
|
||||
title = str(component.get('SUMMARY'))
|
||||
descriptionPrototype = {
|
||||
'color': '#1e90ff',
|
||||
'description': None
|
||||
}
|
||||
descriptionList = []
|
||||
if component.get('DESCRIPTION') is not None and str(component.get('DESCRIPTION')) != '':
|
||||
descriptionList.append(component.get('DESCRIPTION'))
|
||||
if component.get('LOCATION') is not None and str(component.get('LOCATION')) != '':
|
||||
descriptionList.append(component.get('LOCATION'))
|
||||
descriptionPrototype['description'] = '\n'.join(descriptionList)
|
||||
description = json.dumps(descriptionPrototype)
|
||||
|
||||
(eventDateTimeStart, eventDateTimeEnd, timezoneOffset) = AdvancedDateTimeAnalyser(component)
|
||||
loopRules = LoopRulesConverter(component)
|
||||
|
||||
(status, _, _) = db.calendar_add(
|
||||
token,
|
||||
collectionUuid,
|
||||
title,
|
||||
description,
|
||||
eventDateTimeStart,
|
||||
eventDateTimeEnd,
|
||||
loopRules,
|
||||
timezoneOffset
|
||||
)
|
||||
if not status:
|
||||
print('Database return an error')
|
||||
sys.exit(1)
|
||||
|
||||
db.common_logout(token)
|
||||
db.close()
|
||||
print('All chunk: {}\nEvent count: {}'.format(allCount, eventCount))
|
||||
BIN
frontend-legacy/static/image/icon.png
Normal file
BIN
frontend-legacy/static/image/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
Reference in New Issue
Block a user