albertyw/csv-ical

View on GitHub
csv_ical/convert.py

Summary

Maintainability
A
35 mins
Test Coverage
A
100%
"""
This file reads the CSV file and saves an ical file.
There are a bunch of configurable variables
"""

import csv
import datetime
from pathlib import Path
from platform import uname
from typing import Any, List, Optional, TypedDict, Union
from uuid import uuid4

from icalendar import Calendar, Event


class Config(TypedDict):
    HEADER_ROWS_TO_SKIP: int
    CSV_NAME: int
    CSV_START_DATE: int
    CSV_END_DATE: int
    CSV_DESCRIPTION: int
    CSV_LOCATION: int
    CSV_DELIMITER: str
class ConfigOverrides(Config, total=False):
    pass
DEFAULT_CONFIG: Config = {
    'HEADER_ROWS_TO_SKIP':  0,

    # The variables below refer to the column indexes in the CSV
    'CSV_NAME': 0,
    'CSV_START_DATE': 1,
    'CSV_END_DATE': 1,
    'CSV_DESCRIPTION': 2,
    'CSV_LOCATION': 3,

    # Delimiter used in CSV file
    'CSV_DELIMITER': ',',
}



class Convert():
    def __init__(self) -> None:
        self.csv_data: List[List[Any]] = []
        self.cal: Calendar = None

    def _generate_configs_from_default(
        self,
        overrides: Optional[ConfigOverrides] = None,
    ) -> Config:
        """ Generate configs by inheriting from defaults """
        config = DEFAULT_CONFIG.copy()
        non_optional_overrides: ConfigOverrides = {}  # type: ignore
        if overrides:
            non_optional_overrides = overrides
        for k, v in non_optional_overrides.items():
            config[k] = v  # type: ignore
        return config

    def read_ical(self, ical_file_location: Union[str, Path]) -> Calendar:
        """ Read the ical file """
        with open(ical_file_location, 'r', encoding='utf-8') as ical_file:
            data = ical_file.read()
        self.cal = Calendar.from_ical(data)
        return self.cal

    def read_csv(
        self,
        csv_location: Union[str, Path],
        csv_configs: Optional[Config] = None
    ) -> List[List[Any]]:
        """ Read the csv file """
        csv_configs = self._generate_configs_from_default(csv_configs)
        with open(csv_location, 'r', encoding='utf-8') as csv_file:
            csv_reader = csv.reader(csv_file, delimiter=csv_configs['CSV_DELIMITER'])
            self.csv_data = list(csv_reader)
        self.csv_data = self.csv_data[csv_configs['HEADER_ROWS_TO_SKIP']:]
        return self.csv_data

    def make_ical(
        self,
        csv_configs: Optional[Config] = None,
    ) -> Calendar:
        """ Make iCal entries """
        csv_configs = self._generate_configs_from_default(csv_configs)
        self.cal = Calendar()
        for row in self.csv_data:
            event = Event()
            event.add('summary', row[csv_configs['CSV_NAME']])
            event.add('dtstart', row[csv_configs['CSV_START_DATE']])
            event.add('dtend', row[csv_configs['CSV_END_DATE']])
            event.add('description', row[csv_configs['CSV_DESCRIPTION']])
            event.add('location', row[csv_configs['CSV_LOCATION']])
            event.add('uid', uuid4().hex + '@' + uname().node)
            event.add('dtstamp', datetime.datetime.now())
            self.cal.add_component(event)
        return self.cal

    def make_csv(self) -> None:
        """ Make CSV """
        for event in self.cal.subcomponents:
            if event.name != 'VEVENT':
                continue
            dtstart = ''
            if event.get('DTSTART'):
                dtstart = event.get('DTSTART').dt
            dtend = ''
            if event.get('DTEND'):
                dtend = event.get('DTEND').dt
            row = [
                event.get('SUMMARY'),
                dtstart,
                dtend,
                event.get('DESCRIPTION'),
                event.get('LOCATION'),
            ]
            row = [str(x) for x in row]
            self.csv_data.append(row)

    def save_ical(self, ical_location: str) -> None:
        """ Save the calendar instance to a file """
        data = self.cal.to_ical()
        with open(ical_location, 'wb') as ical_file:
            ical_file.write(data)

    def save_csv(
        self,
        csv_location: str,
        csv_delimiter: str = ','
    ) -> None:
        """ Save the csv to a file """
        with open(csv_location, 'w', encoding='utf-8') as csv_handle:
            writer = csv.writer(csv_handle, delimiter=csv_delimiter)
            for row in self.csv_data:
                writer.writerow([r.strip() for r in row])